CMD Guide
HomeSystem DesignScalable Systems (Advanced Topics)

What Do At‑most‑once, At‑least‑once, And Exactly‑once Delivery Semantics Mean

Exactly-once delivery is impossible — start there

Over an unreliable network, exactly-once delivery cannot be achieved. This is not an engineering gap that a better broker will one day close; it is the Two Generals problem. A sender transmits a message and waits for an acknowledgement. If no ack arrives, the sender is stuck in a fundamental ambiguity: did the message get lost, or did the message arrive and the ack get lost? It cannot tell the two apart. So it has exactly two choices, and each buys one guarantee at the cost of the other:

There is no third delivery guarantee. What you can build — and what every serious system builds — is effectively-once:

at-least-once delivery + idempotent processing = effectively-once. Stop trying to deliver exactly once. Retry until it lands, and make duplicates harmless by deduplicating on the consumer.

The distinction that trips people up: exactly-once delivery (the wire guarantee) is a myth; exactly-once effect (the observable outcome) is achievable. The legacy framing that opens with "each message arrives exactly one time, no loss and no duplicates" describes the myth as if it were a product you can buy. It isn't. Everything below is about producing the effect honestly.

The three semantics, by mechanism

All three differ in one decision: when do you acknowledge relative to processing, and do you retry? That single choice determines the failure mode.

At-most-once — ack (or don't wait) before processing

Fire-and-forget. The sender transmits and moves on without waiting for confirmation, or the consumer commits the offset / acks before doing the work. If anything crashes mid-flight, the message is gone and nobody retries. Zero duplicate risk, zero retry overhead, lowest latency — and silent loss. Correct for high-volume telemetry, metrics, and best-effort notifications where one dropped sample out of millions is invisible.

At-least-once — ack after processing, retry on timeout

The sender keeps the message until it receives an ack, and retransmits if the ack doesn't arrive within a timeout. The consumer commits its offset / acks only after the work is durably done. Nothing is ever lost. But the lost-ack case is now a guaranteed source of duplicates: the work completed, the ack died in transit, the sender retries, the consumer sees the same message again. This is the default posture of RabbitMQ acks and SQS visibility timeouts, and what a Kafka consumer gets once it commits offsets after processing — note that Kafka's out-of-the-box enable.auto.commit=true commits on a 5-second timer decoupled from your processing, which can also lose messages on a crash; turn it off (manual commit after the work) for true at-least-once. Either way the trade holds because losing data is usually worse than seeing it twice.

Effectively-once — at-least-once plus a dedup key or idempotent write

Keep at-least-once delivery for durability, then neutralize the duplicates at the point of effect. Two techniques:

Sequence diagram between producer and consumer: send message with key abc, consumer processes and stores the result, the ack is lost so the producer times out and retries the same key, the dedup table recognizes the key and returns the stored result, then the ack is delivered — two deliveries, one effect
Sequence diagram between producer and consumer: send message with key abc, consumer processes and stores the result, the ack is lost so the producer times out and retries the same key, the dedup table recognizes the key and returns the stored result, then the ack is delivered — two deliveries, one effect

Traced example: a lost ack, step by step

A producer submits "charge $50, key=abc". The consumer charges the card and stores the result under abc. The ack back to the producer is lost in the network. The producer's timeout fires; it cannot distinguish "never processed" from "processed, ack lost", so — being at-least-once — it retries the identical message. Without dedup, the card is charged twice. With a dedup table keyed on abc, the second arrival is recognized and the stored result is returned. One effect, despite two deliveries.

tProducerNetworkConsumerEffect so far
1send charge $50, key=abcmsg deliveredreceives msg
2waiting for ackcharge card; store abc → "OK $50"charged once
3waiting for ackack LOSTack sentcharged once
4timeout → ambiguity: lost or ack-lost?idlecharged once
5RETRY same msg, same key=abcmsg deliveredreceives msg againcharged once
6ano dedup: charge againcharged TWICE
6bdedup: key abc seen → return stored resultcharged once
7receives ackack delivereddone

The dedup check must be atomic — a naive "check if key exists, then act" is itself a race between two concurrent retries. Use an atomic putIfAbsent or a database unique constraint so the second writer loses cleanly.

Kafka's "exactly-once semantics" (EOS), precisely

Kafka advertises exactly-once. It is real, it is well-engineered, and it is bounded. EOS is two independent mechanisms, and knowing exactly what each does — and where the boundary ends — is the staff-level distinction.

1. The idempotent producer (dedups producer retries)

When a producer's send ack is lost, it retries — the classic duplicate source, but now on the write path into the log. Kafka assigns each producer a PID (producer id) and stamps every record with a monotonic sequence number per partition. The broker tracks the last sequence it accepted for that (PID, partition). A retry carries the same sequence number, so the broker recognizes it as a duplicate and drops it without appending a second copy. This deduplicates producer retries within a single partition — it does not span partitions and does not touch the consumer side. Enabled with enable.idempotence=true (default in modern Kafka).

2. Transactions (atomic read-process-write)

The idempotent producer stops duplicate writes. Transactions solve the stream-processing pattern "consume from topic A, produce to topic B, commit the consumed offset" — making the produced records and the offset commit a single atomic unit. Either all of it commits or none of it does. A transactional.id gives the producer a stable identity so a fenced-off zombie after a crash cannot commit. The final piece is on the consumer: isolation.level=read_committed, so downstream readers only ever see records from committed transactions and never observe the aborted ones. Together this is genuine exactly-once processing for a Kafka-to-Kafka pipeline.

The boundary: Kafka EOS holds within Kafka's world — records in topics, offsets in the __consumer_offsets topic, state in a Kafka-backed store. The instant your consumer performs an external side effect — charge a card, send an email, call a third-party API, write to a non-transactional database — that effect is outside the transaction and Kafka guarantees nothing about it. An email can't be un-sent. The transaction can abort and roll back the offset, but the email is already gone. External effects still need their own idempotency key.
Kafka EOS boundary diagram: a dashed box covers Topic A, the stream processor and Topic B, where produce plus offset commit form one atomic transaction and read_committed readers never see aborted records — exactly-once within Kafka; external effects such as charging a card, sending an email or calling a third-party API sit outside the box, are not covered by EOS (an email can't be un-sent) and need their own idempotency key
Kafka EOS boundary diagram: a dashed box covers Topic A, the stream processor and Topic B, where produce plus offset commit form one atomic transaction and read_committed readers never see aborted records — exactly-once within Kafka; external effects such as charging a card, sending an email or calling a third-party API sit outside the box, are not covered by EOS (an email can't be un-sent) and need their own idempotency key

Pitfalls

Selection & trade-offs

Three real options, ranked by cost. Pick the cheapest one that meets the failure tolerance of the data.

ApproachLoss?Duplicates?Latency / throughputComplexityUse when
At-most-oncepossibleneverbest (no acks, no retries)lowestmetrics, telemetry, logs, best-effort notifications — a dropped sample is invisible
At-least-once + idempotent processing (effectively-once)neveron the wire, but neutralized in effectmoderate (acks + retries + a dedup read/write)moderate — you own the dedup key and TTLthe default for anything that matters: payments, orders, queue consumers, webhooks. Works across arbitrary external systems.
Kafka EOS (idempotent producer + transactions)nevernone within Kafkaworst of the durable options — transactions add a commit round per batch (transaction markers + coordinator write), cutting throughput and adding end-to-end latency vs plain at-least-once; tune the transaction commit cadence (commit.interval.ms in Kafka Streams) to trade latency for batching — transaction.timeout.ms is only the abort deadline for hung transactions, not a batching dialhighest config surface, but the dedup logic is the broker's, not yoursKafka-to-Kafka stream processing (read-process-write) where the whole effect lives inside Kafka. Does not extend to external side effects.

The decision rule: if losing data is acceptable, use at-most-once and stop paying for reliability you don't need. If the effect is entirely inside Kafka, EOS gives you exactly-once processing with the dedup handled for you. For everything else — and especially anything touching an external system — at-least-once + idempotency is the honest, portable answer. Note that even a Kafka EOS pipeline that reaches out to charge a card degrades to "at-least-once + your own idempotency key" at that reach-out point; EOS does not rescue you there.

Takeaways


Sources & further reading. Kleppmann, Designing Data-Intensive Applications (ch. 8–9, "The Trouble with Distributed Systems" and consistency/consensus, on why exactly-once delivery is unattainable and how idempotence recovers the effect). Kafka exactly-once design: KIP-98 — Exactly Once Delivery and Transactional Messaging, and the Confluent "Exactly-once Semantics" design notes (idempotent producer, transactions, read_committed). The Two Generals problem (impossibility of coordinated agreement over a lossy channel). Nygard, Release It! (retries, timeouts, and idempotency as production stability patterns). See also this guide: Idempotency & "Exactly-Once Is a Myth", The 8 Fallacies of Distributed Computing (#1), Designing for Failure, Kafka.
🤖 Don't fully get this? Learn it with Claude

Stuck on What Do At‑most‑once, At‑least‑once, And Exactly‑once Delivery Semantics Mean? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🎨 Explain it visually

Build the mental picture, not memorization.

I just read a lesson on **What Do At‑most‑once, At‑least‑once, And Exactly‑once Delivery Semantics Mean** (System Design) and want to truly understand it. Explain What Do At‑most‑once, At‑least‑once, And Exactly‑once Delivery Semantics Mean from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🤔 Walk me through it (interactive)

Socratic — adapts to where you're stuck.

Teach me **What Do At‑most‑once, At‑least‑once, And Exactly‑once Delivery Semantics Mean** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧪 Quiz me & fix my gaps

Active recall exposes what you missed.

Quiz me on **What Do At‑most‑once, At‑least‑once, And Exactly‑once Delivery Semantics Mean** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧠 Make it stick

Intuition + hook + flashcards for long-term memory.

Help me remember **What Do At‑most‑once, At‑least‑once, And Exactly‑once Delivery Semantics Mean** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes