An expiring outbox lease lets another worker reclaim a pending event after the current worker stops making progress.
Spring outbox claims: allow recovery after a worker lease expires
Claim with one conditional write
The H2 fixture writes an event E-41, then lets worker-A claim it until logical time 200. Worker-B cannot claim at time 150. At 200 it can replace the expired token. The SQL update checks state and lease time in the same statement, so the returned row count tells the caller whether it owns the row in this fixture. The earlier claim-state lesson checks a simple pending-to-claimed transition without expiry.
Use a database clock or an agreed clock policy in a real service. The fixture passes deterministic long values so the boundary is testable. It does not prove behavior under clock skew, a long network pause, database lock contention or a two-node race. Keep the claim transaction short; do not hold a row lock while calling a broker.
An expiry is not a cancellation
Worker-A may still be sending after its lease expires. Its send can race worker-B's retry. A lease permits recovery; it does not enforce exactly-once external delivery. A token-checked acknowledgement stops an old worker from marking the new owner's row done, and a consumer ledger handles repeated event IDs at the destination.
Checked source
update delivery_outbox
set claim_token = ?, lease_until = ?
where event_id = ? and delivered = false
and (claim_token is null or lease_until <= ?);Verification boundary
OutboxLeaseContractTest.expiredLeaseCanBeReclaimedButOldWorkerCannotAcknowledge runs in the downloadable Spring source kit. The excerpt is shortened; the kit contains the complete tests.
Costs and limits
The check uses one row and sequential statements on H2. Each scan or update costs database I/O; an index and target-database query plan are needed for a large pending set. The test does not establish concurrent fairness, crash recovery across processes or broker delivery.
Common Mistakes
- Do not interpret lease expiry as proof the former worker has stopped.
- Do not hold a database transaction open during network delivery.
- Do not assume test-supplied timestamps cover production clock behavior.
Read next
Spring outbox claims: move pending work once per database state, Outbox acknowledgements: require the current lease token, Spring consumer deduplication: commit the event ID with the mutation.
Continue with checked relay behavior
Continue with Spring outbox claims: lease expiry and token-fenced acknowledgement.
