End-to-End Delivery Guarantees
The idea in one sentence
Section titled “The idea in one sentence”An end-to-end guarantee is only as strong as its weakest link — the producer side (acks and idempotence) and the consumer side (commit placement) each contribute, and you have to reason about them together.
Two halves of one guarantee
Section titled “Two halves of one guarantee”Delivery semantics are not a single switch. Data flows producer → log → consumer, and each hop has its own failure mode:
- Producer → log. With
acks=allandenable.idempotence=true, a record is written durably and retries do not create duplicates in the partition. Withacks=0oracks=1, a broker failure at the wrong moment can lose the write. - Log → consumer. Commit after processing gives at-least-once; commit before gives at-most-once. The log itself never loses committed records within retention.
Combine them and you get the real end-to-end behavior:
| Producer | Consumer commit | End-to-end |
|---|---|---|
acks=all + idempotent | after processing | at-least-once (no loss, possible dupes) |
acks=1 | after processing | at-least-once, but a rare broker failure can still lose a write |
acks=all + idempotent | before processing | at-most-once (no dupes, possible loss) |
| transactional producer | offsets in the transaction | exactly-once inside Kafka |
flowchart LR prod["Producer: acks=all + idempotence"] -->|durable, no dupes| log["Partition log"] log -->|committed records retained| cons["Consumer"] cons -->|commit after processing| out["at-least-once end-to-end"] out -->|idempotent sink or transactions| eos["effectively exactly-once"]
The default worth designing for
Section titled “The default worth designing for”Most systems run at-least-once end-to-end: acks=all, idempotent producer, commit after processing — then make the downstream effect idempotent so duplicates are harmless. It is simple, robust, and fast. Reach for full exactly-once (transactions) only when the pipeline stays inside Kafka and duplicates are genuinely unacceptable, because it adds coordination cost. The rest of this module builds out that exactly-once path.