← Concepts

阅读中文版本

Transactional Inbox

A transactional inbox stores received messages in the same database transaction as their processing, so consumers handle each message exactly once even under redelivery.

architecture

Definition

The transactional inbox (consumer-side dual of the outbox) persists each consumed message id in the same transaction as its business effect. Redelivered messages find the recorded id and are skipped as already processed. It converts at-least-once delivery into effectively-once processing without distributed transactions, at the cost of a durable deduplication table per consumer.

Context and scope

Applied in Kafka consumers, SQS handlers, and webhook processors — anywhere redelivery overlaps with non-idempotent side effects. Pairs with the transactional outbox on the producer side.

Common misconceptions

  • Idempotent consumers make an inbox unnecessary — designed idempotency covers specific operations; the inbox covers all of them uniformly, including side effects you forgot to make idempotent
  • The inbox must be a separate service — it is a table in the consumer database, written inside the same transaction as the effect
  • Once implemented, ordering is solved too — the inbox deduplicates delivery; ordering requires key-based partitioning or sequencing on top

Anti-patterns

  • Marking messages processed before the business write commits and losing them on rollback
  • Growing the inbox forever without archival and slowing every transaction
  • Deduplicating on broker offset instead of message id and breaking under rebalances and reprocessing

Related concepts

Authoritative source

https://microservices.io/patterns/data/transactional-outbox.html

What it is

Brokers redeliver: crashes, timeouts, rebalances. Every consumer that performs non-idempotent work will eventually execute a message twice. The inbox records what was already done, atomically with doing it.

How it works

On receive, the handler transaction inserts the message id into the inbox table and performs the business write. A redelivery hits a unique violation on the id and skips. Cleanup policies archive or partition the inbox by age so the hot table stays small.

When to apply

Consumers writing to databases with non-idempotent effects: double charges, duplicate records, double notifications. Combined with the outbox, it yields end-to-end effectively-once flows without distributed transactions.

Pitfalls

Dedup key choice matters: broker offsets change on replay and rebalance, while business or producer message ids survive them. The inbox write must share the business transaction — a separate commit window loses messages or double-processes. Non-database side effects — API calls, emails — still need idempotency keys at the target, because the inbox only protects the local transaction.