Background jobs / Crash-point lab
Transactional Outbox & Inbox Crash Lab
Which state survives a crash between the business commit, message delivery, effect, and acknowledgement?
Interactive calculations run in your browser; the initial example is pre-rendered. There are no accounts, uploads, or live queue connections. Inputs stay in page memory; the site does not persist them, put them in URLs, or send them to analytics. A worksheet download includes only what you explicitly export.
Durable state through recovery
Publications
2
Accepted copies of one message
Consumer deliveries
4
Includes redelivery after the crash
Business effects
1
Not the delivery count
Lost accepted intent
No
Within the selected recovery model
Follow the durable effect count, not just acknowledgements. Switching the effect from the database to an external provider leaves the inbox unable to undo it. A stable provider key is a separate downstream contract; an outbox alone does not supply it.
| Step / actor | Event | Order | Outbox pending | Inbox committed | Effects |
|---|---|---|---|---|---|
| 1. Producer | Begin local transaction for order-demo. | Absent | No | No | 0 |
| 2. Database | Order and outbox intent commit atomically. | Committed | Yes | No | 0 |
| 3. Relay | Broker accepts another copy of order-demo's message. | Committed | Yes | No | 0 |
| 4. Producer | Crash after successful publish, before recording progress. | Committed | Yes | No | 0 |
| 5. Relay | Recovery cannot infer publication from the still-pending row; it republishes. | Committed | Yes | No | 0 |
| 6. Relay | Broker accepts another copy of order-demo's message. | Committed | Yes | No | 0 |
| 7. Relay | Mark the outbox row dispatched after publish. | Committed | No | No | 0 |
| 8. Consumer | Receive delivery 1 of the same message identity. | Committed | No | No | 0 |
| 9. Database | Stage the increment and inbox identity together, not yet committed. | Committed | No | No | 0 |
| 10. Consumer | Crash rolls back BOTH the staged database effect and inbox claim; broker redelivers. | Committed | No | No | 0 |
| 11. Consumer | Receive delivery 2 of the same message identity. | Committed | No | No | 0 |
| 12. Database | Stage the increment and inbox identity together, not yet committed. | Committed | No | No | 0 |
| 13. Database | Commit the inbox identity and any local transactional effect. | Committed | No | Yes | 1 |
| 14. Broker | Delivery acknowledged. | Committed | No | Yes | 1 |
| 15. Consumer | Receive delivery 3 of the same message identity. | Committed | No | Yes | 1 |
| 16. Inbox | Committed message identity already present; skip the effect and acknowledge. | Committed | No | Yes | 1 |
| 17. Consumer | Receive delivery 4 of the same message identity. | Committed | No | Yes | 1 |
| 18. Inbox | Committed message identity already present; skip the effect and acknowledge. | Committed | No | Yes | 1 |
An outbox closes a local gap, not every effect boundary
The outbox makes a business change and its delivery intent atomic in one database. The relay can still publish twice. An inbox can make consumer deduplication atomic with a local database effect, but an external API is not in that transaction. Move the effect across that boundary and rerun the same crash before relying on a recovery design.
Assumptions and limits
- One fictional order is committed once. The dual-write producer commits the order before publishing; it has no recovery scan or durable intent ledger.
- The outbox producer commits the order and message intent in one local transaction. Its relay eventually recovers; a publish-before-mark crash causes a duplicate publication.
- Broker delivery is at least once. Extra deliveries and one consumer crash are explicit inputs, not measured probabilities. There are no poison payloads or permanent outages.
- The inbox and database effect share one consumer transaction. An external API effect is outside that transaction and cannot be rolled back by an inbox.
- The optional provider key is stable, atomically bound to the external effect, and retained for this entire example. These are assumptions, not properties supplied by an outbox.
- Before-effect and after-effect crashes happen before the consumer local commit. After-local-commit crashes happen before broker acknowledgement. A crashed delivery is retried once.
- Steps establish ordering, not elapsed time. This lab does not implement a broker, database, payment API, or production recovery procedure.
