Skip to content

Transactional Outbox/Inbox Relay

Reliable-relay method — instantiates Message-Mediated State Coordination

Closes the gap between saving state and sending a message by writing the outgoing message into the same database transaction as the state change, then relaying it — with the receiver deduping on an inbox.

Transactional Outbox/Inbox Relay solves the dual-write problem: a participant needs to update its own database and publish a message about it, but these are two separate systems with no shared transaction, so a crash in between either loses a message that should have been sent or emits one for a change that was never committed. The mechanism's defining move is to make the message part of the same local transaction as the state change — the outgoing message is written into an outbox table alongside the business data, so both commit or neither does. A separate relay then reads the outbox and publishes; the receiver's inbox discards anything it has already seen. It lives precisely on the seam between a participant's private state and the shared channel, which no sibling here occupies.

Example

A hospital admissions system records a new admission in its database and must notify the pharmacy and billing services. The naïve version writes the admission row, commits, then publishes PatientAdmitted — and if the process dies in that gap, the patient exists in admissions but is invisible to pharmacy and billing. Reversing the order is no better: publish first, then fail to commit, and downstream now believes in an admission that never happened.

With an outbox, the admission row and the PatientAdmitted message are written in one transaction, so they are atomically both-or-neither. A relay polls the outbox (or tails the database's change log) and publishes the message after the fact; if the broker is momentarily down, the message simply waits in the committed outbox and is relayed when it recovers — it cannot be lost. Pharmacy's inbox recognizes a redelivered event it already processed and ignores it. The state change and the notification can no longer disagree.

How it works

The whole trick is the atomic local commit; everything else is cleanup around it:

  • Write the message into the transaction. The outgoing message is inserted into an outbox table in the same transaction as the state change, so publication intent is as durable as the data itself.
  • Relay after commit. A background relay reads committed outbox rows and publishes them — by polling the table, or by change-data-capture tailing the transaction log for lower latency.
  • Dedup on the inbox. Because the relay publishes at-least-once (it may crash after publishing but before marking a row sent), the receiver keeps an inbox of processed message IDs and drops repeats.

Tuning parameters

  • Relay mechanism — polling the outbox versus change-data-capture on the transaction log. Polling is simple and adds latency plus query load; CDC is lower-latency and near-real-time but heavier to operate.
  • Ordering guarantee — preserve per-entity order via the outbox sequence, or relay in parallel for throughput. Ordered delivery simplifies consumers; parallel relay scales but can reorder.
  • Outbox retention — how long relayed rows are kept before cleanup. Longer retention aids audit and replay; shorter keeps the table from bloating.
  • Delivery target — accept at-least-once (and require an idempotent inbox) or chase stricter guarantees. At-least-once plus a deduping inbox is the pragmatic default.
  • Inbox scope — dedup per consumer versus a shared inbox. Per-consumer is correct when consumers process independently; shared saves storage at the cost of coupling.

When it helps, and when it misleads

Its strength is eliminating the lost-or-ghost message at the state-to-channel seam without a distributed transaction: no two-phase commit, no locking a broker and a database together, just one ordinary local commit and a relay. It is the standard cure for "my database and my message broker must never disagree."

Its costs and traps are real. The relay adds latency (the message is published after commit, not during) and an operational surface — a table, a relay process, and lag to monitor. Crucially, the pattern is at-least-once, not exactly-once: it guarantees the message is emitted, not that it arrives once, so consumers must be idempotent or duplicates will double-apply. And it does not order across entities unless you make it. The classic misuse is assuming the outbox delivers exactly-once end-to-end and skipping the inbox dedup, which reintroduces the double-processing it was supposed to prevent.[n1] The discipline is to keep consumers idempotent, watch relay lag, and reach for change-data-capture when latency matters.

How it implements the components

  • participant_state_boundary — it operates exactly at the boundary between a participant's private database state and the shared channel, making the crossing from committed state to outbound message atomic rather than a risky pair of independent writes.
  • channel_delivery_semantics — it establishes the end-to-end delivery guarantee: at-least-once publication from the durably committed outbox, made effectively exactly-once by the receiver's inbox dedup.

It does NOT mint the marker its inbox dedups on (Retry with Idempotency Key), park unrelayable messages (Dead-Letter Queue), or define the message's schema (Message Schema Registry) — it guarantees state-and-message atomicity and hands those concerns on.

Editorial Notes

Form Classification

Form family: Control, Automation & Runtime

Rationale: Transactional Outbox/Inbox Relay operates as a live operational control that automatically routes, enforces, adapts, or responds during execution because it closes the gap between saving state and sending a message by writing the outgoing message into the same database transaction as the state change, then relaying it — with the receiver deduping on an inbox.

Independent corroboration: The frozen evidence defines Transactional Outbox/Inbox Relay as 'Closes the gap between saving state and sending a message by writing the outgoing message into the same database transaction as the state change, then relaying it — with the receiver deduping on an inbox', so its operative form is Control, Automation & Runtime.

Nearest alternative: Protocol, Workflow & Routine — Transactional Outbox/Inbox Relay includes features of a repeatable ordered procedure or handoff sequence that coordinates action, but its defining operation is a live operational control that automatically routes, enforces, adapts, or responds during execution.

Review outcome: Independent reviewer agreement; medium confidence.

Origin Attribution

Primary origin: Computer Science & Software Engineering

Origin pattern: Single lineage

Present-day reach: Specialized

Rationale: Both independent reviews identify computer science as the historical home of the operation—Closes the gap between saving state and sending a message by writing the outgoing message into the same database transaction as the state change, then relaying it — with the receiver deduping on an inbox.. The retained alternates document formative adjacent traditions; the reach field, not the origin field, carries later applicability.

Related originating lineages:

  • Engineering & Design — Engineering design, reliability, and systems-safety practice supplies a parallel or contributing lineage for the mechanism's defining operation: closes the gap between saving state and sending a message by writing the outgoing message into the same database transaction as the state change, then relaying it — with the….

Review resolution: Both blind reviewers independently place the defining operation—Closes the gap between saving state and sending a message by writing the outgoing message into the same database transaction as the state change, then relaying it — with the receiver deduping on an inbox.—in computer science. Their queued differences are secondary: origin_mode_disagreement, domain_reach_disagreement, encyclopedia_synthesis_disagreement. Reviewer A contributes no unique alternate; reviewer B contributes no unique alternate. I preserve the full evidence-supported union of 1 alternate domain(s), without a numeric cap. origin_mode=single_lineage reflects the reviewers' evidence about historical construction, while domain_reach=specialized separately reflects present-day portability. The affirmative encyclopedia-synthesis finding is preserved, and confidence=high uses the more conservative reviewer level.

Encyclopedia synthesis: The exact catalogued form synthesizes established practice rather than reproducing a single standard historical label.

Review outcome: Reconciled after independent review; high confidence.

Notes

The outbox fixes only the emit gap — the moment where a committed state change might fail to produce its message. It says nothing about what happens after publication: durable transport is still Durable Queue with Acknowledgement's job, and because delivery is at-least-once, every downstream consumer must be idempotent. The outbox is one end of a reliable chain, not the whole chain.

[n1] The dual-write problem names the impossibility of atomically updating two independent systems (here a database and a message broker) without a shared transaction. The transactional outbox is the standard pattern that sidesteps it by folding the message write into the single local transaction, trading a distributed commit for eventual, at-least-once relay.