Delivery Semantics
The idea in one sentence
Section titled “The idea in one sentence”There are three delivery semantics — at-most-once, at-least-once, and exactly-once — and from the consumer’s seat you choose between the first two mainly by when you commit, then reach the third with idempotent processing or transactions.
The three guarantees
Section titled “The three guarantees”- At-most-once — every record is delivered zero or one times. Records may be lost, never duplicated. You get this by committing the offset before processing: if you crash after committing but before finishing, the record is skipped on restart.
- At-least-once — every record is delivered one or more times. Records may be duplicated, never lost. You get this by committing after processing: a crash before the commit re-reads the batch. This is the practical default.
- Exactly-once — every record affects the outcome once, with no loss and no duplicates. This needs more than commit placement.
flowchart TB
poll["poll batch"] --> choice{"commit before or after processing?"}
choice -->|before| amo["at-most-once: crash then skip (loss)"]
choice -->|after| alo["at-least-once: crash then reprocess (duplicates)"]
alo --> idem["make processing idempotent to reach effectively exactly-once"] Getting to exactly-once
Section titled “Getting to exactly-once”At-least-once plus idempotent processing is the most common route: design the side effect so that reprocessing the same record twice has the same result as once. Practical tactics:
- Upsert by a natural key instead of blind insert, so a duplicate overwrites rather than duplicates.
- Dedup on a record id the producer stamps, discarding ids you have already applied.
- Make downstream operations commutative/idempotent (e.g. “set status = shipped”, not “increment count”).
The stronger, framework-level route is Kafka transactions — atomically writing output records and committing input offsets together — which the next module covers in full. That gives exactly-once for consume-transform-produce pipelines that stay inside Kafka.