Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Spring transactional outbox: commit a receipt and event row together

Last updated: 30 Sept 20264 min read
tutorial
IntermediateBy AITrove Editorial

A transactional outbox stores the business change and a pending delivery record in one database transaction.

Download Spring source kit

One commit boundary

The checked H2 fixture inserts receipt R-41 and outbox event E-41 through one Spring TransactionTemplate. A failure before commit leaves neither row. A successful retry leaves one of each. The event row is a promise to attempt delivery later, not evidence that a broker has received anything.

A direct broker call inside the transaction cannot be rolled back with the database. Sending only after commit avoids that ordering problem but can still lose a message if the process exits before sending. The stored event closes that particular gap when a worker later scans it. After-commit listeners alone do not make delivery durable.

The database boundary still needs a schema

Give the event its own stable ID, a state, and enough payload or a reference to reconstruct the delivery. Insert both rows with the same DataSource and transaction manager. A second database, external queue or independent transaction breaks the atomic claim. Claiming pending rows is the next boundary, not part of this commit.

Checked source

Java
transaction.executeWithoutResult(status -> {
    jdbc.update("insert into receipt values (?, ?)", "R-41", "accepted");
    jdbc.update("insert into outbox values (?, ?, ?)", "E-41", "R-41", "pending");
});

Verification boundary

OutboxWriteContractTest.receiptAndOutboxRowCommitOrRollBackTogether runs in the downloadable Spring source kit. The excerpt leaves out surrounding setup and imports; the kit contains the complete test.

Costs and limits

Two indexed inserts are O(log n) in the usual B-tree model, plus commit and log I/O; that is not a latency promise. The local H2 test proves rollback and commit together for one transaction manager. It does not test broker delivery, crash recovery, production isolation or schema migration.

Common Mistakes

  • Do not call an after-commit callback a durable queue.
  • Do not write the business row and event row through unrelated transactions.
  • Do not mark an event delivered when only the database commit succeeded.

Read next

Spring TransactionTemplate: roll back a failed multi-row change, Spring transaction events: run a listener after commit without claiming durability, Spring transaction propagation: joined rollback and independent commit.

Continue with checked worker recovery

Continue with Spring outbox claims: allow recovery after a worker lease expires, Spring consumer deduplication: commit the event ID with the mutation.

Continue with checked tenant commands

Continue with Spring receipt outbox command: record intent without claiming delivery.

Continue with checked relay behavior

Continue with Spring outbox transaction boundaries: where atomicity ends.

spring
spring-boot
outbox-atomic-write
Storage details