Skip to main content
José David Baena
Tools and working references

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.

Choose the transaction boundaries

Choose the transaction boundaries

What survives between the business commit and publication?

One crash, followed by the selected recovery contract.

A local inbox can share a transaction with local data only.

The first effect-capable delivery crashes once and is redelivered.

Changing this boundary can invalidate the same inbox design.

Only applies to the external API, not to a database increment.

Additional copies of an actually published message; zero if nothing was published.

Durable state through recovery

One effect under the selected transaction and provider contracts

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.

Crash trace: state shown after each step
Step / actorEventOrderOutbox pendingInbox committedEffects
1. ProducerBegin local transaction for order-demo.AbsentNoNo0
2. DatabaseOrder and outbox intent commit atomically.CommittedYesNo0
3. RelayBroker accepts another copy of order-demo's message.CommittedYesNo0
4. ProducerCrash after successful publish, before recording progress.CommittedYesNo0
5. RelayRecovery cannot infer publication from the still-pending row; it republishes.CommittedYesNo0
6. RelayBroker accepts another copy of order-demo's message.CommittedYesNo0
7. RelayMark the outbox row dispatched after publish.CommittedNoNo0
8. ConsumerReceive delivery 1 of the same message identity.CommittedNoNo0
9. DatabaseStage the increment and inbox identity together, not yet committed.CommittedNoNo0
10. ConsumerCrash rolls back BOTH the staged database effect and inbox claim; broker redelivers.CommittedNoNo0
11. ConsumerReceive delivery 2 of the same message identity.CommittedNoNo0
12. DatabaseStage the increment and inbox identity together, not yet committed.CommittedNoNo0
13. DatabaseCommit the inbox identity and any local transactional effect.CommittedNoYes1
14. BrokerDelivery acknowledged.CommittedNoYes1
15. ConsumerReceive delivery 3 of the same message identity.CommittedNoYes1
16. InboxCommitted message identity already present; skip the effect and acknowledge.CommittedNoYes1
17. ConsumerReceive delivery 4 of the same message identity.CommittedNoYes1
18. InboxCommitted message identity already present; skip the effect and acknowledge.CommittedNoYes1

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.