The Inbox & Outbox Pattern: Reliable Events with Kafka ๐ฌ
How to stop losing (and duplicating) events when your service writes to a database and Kafka at the same time.
Practical patterns for safer ingestion, retries, and medallion-style processing on AWS.
Retries are a fact of life in data pipelines โ a worker crashes mid-batch, a scheduler fires twice, an upstream export gets replayed. The question isn't whether your ingestion worker runs more than once, it's whether running it twice quietly corrupts your data. Idempotent workers make "run again" a safe, boring operation. ๐
A worker is idempotent when processing the same input twice leaves the system in the same state as processing it once. In AWS Glue and AppFlow pipelines, that means making three things explicit:
Get those right and a duplicate run is a no-op, not an incident.
The most reliable idempotency technique is to let the destination reject duplicates, rather than trusting the worker to remember. A few patterns, roughly in order of strength:
| Technique | How it works | Best for |
|---|---|---|
| Deterministic partition overwrite | Re-run rewrites the same dt=/id= partition | Batch loads to S3 / lakehouse |
| Idempotency key + conditional write | PUT if not exists on a natural key | Row-level upserts |
| Dedup table | Record processed IDs, skip on repeat | Event-by-event consumers |
Here's the shape of a single run:
flowchart TD
A[Worker picks up batch] --> B{Seen this<br/>source ID?}
B -->|Yes| C[Skip ยท no-op]
B -->|No| D[Transform]
D --> E[Write to deterministic<br/>partition / key]
E --> F[Record source ID<br/>as processed]Bronze, Silver, and Gold layers give idempotency a natural home. Raw input lands in Bronze exactly as received โ replays here are cheap because Bronze is append-and-dedup, not business logic. Silver applies validated transforms against Bronze, so re-running a transform is deterministic by construction. Gold serves the curated shape analytics consume, insulated from operational side effects upstream. Because each layer only reads the one below it, re-processing any layer is safe as long as its write is deterministic.
Reliability includes knowing when a retry misbehaved. Lake Formation, IAM roles, and SSO keep access decisions close to the pipeline, while CloudWatch, CloudTrail, and Macie surface the signals that catch a bad replay: sudden row-count jumps, duplicate natural keys, or a partition that grew when it should have been overwritten. Idempotency you can't observe is idempotency you're only hoping for.
How to stop losing (and duplicating) events when your service writes to a database and Kafka at the same time.
Practical patterns for retries, idempotency, and observability in Lambda-backed product APIs.
What makes Kafka a distributed log rather than a queue, and when to reach for RabbitMQ or Redis instead.