Claim an Outbox Batch Safely

Parallel relays need exclusive claims without turning an empty or slow row into global head-of-line blocking.

Distributed systems · SQL

Recoverable Outbox Claim

Parallel delivery needs exclusive claims, stable event identity, and recovery after abandoned work.

WITH claimed AS (
  SELECT id FROM outbox
  WHERE published_at IS NULL
    AND available_at <= now()
    AND (claimed_until IS NULL OR claimed_until < now())
  ORDER BY id
  FOR UPDATE SKIP LOCKED
  LIMIT 100
)
UPDATE outbox o
SET claimed_by = $1, claimed_until = now() + interval '30 seconds'
FROM claimed
WHERE o.id = claimed.id
RETURNING o.*;

Invariant: No healthy worker reclaims a row before its lease expires; event identity remains stable after recovery.

Use when: Several relay workers publish events without processing the same row concurrently.

Why this boundary matters

Row locks coordinate selection only inside a transaction. A recoverable lease and stable event ID are still required when a publisher stalls after claiming.

Failure policy

BoundaryAction
Row claimedOnly the claimant may publish during the lease
Broker acknowledges publishMark published durably
Publish outcome is ambiguousRelease or expire the claim and retry with the same event ID
Worker diesRecover the row after a bounded lease
Poison eventQuarantine after a retry threshold without blocking unrelated rows

Trade-offs

SKIP LOCKED improves parallelism but does not guarantee delivery, ordering, or consumer idempotency. Leases add recovery latency; aggressive expiry creates concurrent publishers. Monitor oldest-row age, claim expiry, duplicates, and poison rows.

Decision rule: Use parallel claiming when throughput matters and downstream consumers can safely absorb duplicate delivery.

Further reference

Browse all engineering snippets · Read the complete outbox design

>