Transactional outbox delivery

Crash the publisher after delivery but before acknowledgement

The outbox's duplicate window is predictable. Stop the worker after broker acceptance and verify that replay does not repeat the consumer's business effect.

In this article

Create one identifiable event

Commit a synthetic order change and its outbox row in the normal transaction. Record the event identifier and expected consumer effect, such as creating one fulfilment task.

Use a test broker or controlled integration environment where acceptance and delivery can be observed. Keep external notifications and real fulfilment disabled.

Establish the baseline by publishing and consuming the event once, then reset the fixture through a controlled process for the failure case.

Put the interruption after acceptance

Pause the publisher immediately after the broker accepts the message but before the outbox row is marked published. Terminate the worker and restart it.

The row should still be eligible for recovery under the publisher's claim policy. On retry, verify the same event identifier is sent. Creating a new identifier would make the duplicate harder for the consumer to recognise.

Do not count a crash before the send as equivalent evidence. That case tests recoverable intention, while this one tests duplicate delivery after an uncertain result.

Inspect the consumer's transaction

Deliver both copies and observe how the consumer records processing. If it writes a local business record, its duplicate marker and effect should be committed together under the chosen transaction rule.

Test a consumer crash after the effect but before message acknowledgement. The broker may deliver again even if the publisher sent only once. Duplicate protection needs to cover that path too.

For an external side effect, inspect the downstream operation identifier and idempotency contract. A local processed-event flag written before an external call can lose the effect, while writing it afterwards can leave a duplicate window. The design needs an explicit resolution.

Add an ordering case

Publish two changes for the same order and delay the earlier one. Confirm the consumer follows the required version or ordering rule. Multiple publishers can overtake one another even when each fetches rows in timestamp order.

Finally, inspect the target system. There should be one intended fulfilment effect and a trace linking both delivery attempts to the same event. Keep the interruption hooks and assertions as regression tests for publisher, broker configuration and consumer changes.

Primary sources

AWS: transactional outbox patternAWS: SQS at-least-once delivery

References checked 11 September 2026.