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

Spring outbox claims: move pending work once per database state

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

An outbox claim changes a pending record to claimed only if it is still pending, so a second claimant can observe that it lost the state transition.

Download Spring source kit

Use the affected-row count

The H2 fixture changes E-41 from pending to claimed with a guarded UPDATE. The first call changes one row; repeating it changes zero. Treat zero as a lost claim, not as a successful delivery. The claim belongs after the atomic business write and before any external send.

One UPDATE is a useful local predicate, but a production worker needs a lease or another recovery rule. If a process dies after claiming and before sending, a permanent claimed flag strands the event. If the send succeeds and acknowledgement fails, retry can duplicate delivery. Consumers must tolerate repeated event IDs.

Make ownership recoverable

A durable design stores a claim token, deadline, attempt count and last failure under a target database transaction. Release or renew only when the token still matches. Do not let an expired worker complete a row owned by a newer worker. The source kit checks the simple state transition only; multi-worker and crash behavior remain untested.

Checked source

Java
int claimed = jdbc.update(
    "update outbox set state = 'claimed' where id = ? and state = 'pending'", "E-41");
if (claimed != 1) throw new IllegalStateException("claim lost");

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

A primary-key guarded update is usually indexed, but lock waits and database isolation can dominate elapsed time. This lesson verifies one H2 transition, not concurrent worker exclusion, lease expiry or exactly-once delivery.

Common Mistakes

  • Do not infer exactly-once delivery from one successful UPDATE.
  • Do not leave claimed rows without a crash-recovery path.
  • Do not let an old worker acknowledge a new owner's claim.

Read next

Spring transactional outbox: commit a receipt and event row together, Spring transaction propagation: joined rollback and independent commit, Spring transaction events: run a listener after commit without claiming durability.

Continue with checked worker recovery

Continue with Spring outbox claims: allow recovery after a worker lease expires, Outbox acknowledgements: require the current lease token.

spring
spring-boot
outbox-claim-state
Storage details