A production relay must connect durable claim state to an actual transport, consumer, observability, and recovery policy.
Spring project: turn the local outbox model into a measured relay
Build in testable slices
Start from the checked local state machine. Put the producer request and outbox insert in one database transaction. Add a poller that selects due rows in bounded batches, conditional claims with tokens and lease renewal, and a transport adapter that publishes a stable event ID. Acknowledge only after the transport contract says the message is accepted. On uncertain results, retry with the same identity.
At the consumer, commit a unique applied-event key with the business mutation. Set a retry budget, a parked-event review path, and metrics for pending age, repeated delivery, dead rows and oldest unacknowledged event. Define tenant scope, payload version and schema migration before creating a second producer.
Prove the missing boundary
Run two workers against the target database and observe one claim winner. Kill a worker after the consumer commits but before acknowledgement; verify a later retry changes stock once. Inject transport timeout, slow consumer, expired lease, malformed payload and storage outage. Record the final row state after each cut point. Measure polling query plans and backlog drain rate with realistic row counts.
The current kit does none of those deployed checks. Its H2 tests provide a starting contract, not a deployable service. The trace lists checked behavior; the dead-state lesson describes the operational decision still needed.
Checked source
mvn -q test
# Replace the local direct consumer call with a real transport adapter and integration tests.Verification boundary
OutboxRelayFlowTest has seven local tests in the downloadable Spring source kit. The excerpt is shortened; the kit contains the complete test.
Costs and limits
No broker, scheduler, deployment manifest, external database, replay console, or production alert is shipped with this exercise. Those are required before describing the relay as operational.
Common Mistakes
- Do not publish directly from the request path and call it an outbox relay.
- Do not claim exactly-once transport delivery from a consumer ledger.
- Do not launch without a way to inspect and replay parked work.
Read next
Spring outbox relay: claim, deliver, and acknowledge one event, Spring relay failure trace: inspect persisted state at each cut point, Spring outbox retry budget: park a poison event for inspection, Spring Boot tenant command API project: assemble the local write path.
Continue with scheduled relay checks
Continue with Spring relay integration tests: prove the boundaries H2 cannot.
