You send a message. The network is flaky. Did it arrive? If you retry, does the consumer see it twice? If you don't retry, did it get lost?
Three answers exist: at-most-once (may drop), at-least-once (may duplicate), exactly-once (the elusive unicorn). Every message system's correctness story starts with which one it gives you. Every consumer's correctness story starts with "I assume at-least-once, so I'm idempotent."
02
The three guarantees
Guarantee
Can lose?
Can duplicate?
When acceptable
At-most-once
Yes
No
Metrics, telemetry, ephemeral UI updates — losing one sample is invisible
When duplicates are catastrophic AND loss is catastrophic. Rare; expensive.
03
At-most vs at-least — the ACK timing
The difference boils down to when the consumer acknowledges:
ACK before processing → at-most-once. Consumer receives message, acks immediately, then processes. If consumer crashes mid-process, message is already acked (gone from queue) — lost.
ACK after processing → at-least-once. Consumer receives, processes, then acks. If consumer crashes before acking, message stays in queue → redelivered on retry → duplicated.
Everyone picks at-least-once. The solution to duplicates is not a stronger guarantee — it's idempotent consumers.
04
Deep dive — why exactly-once is (almost) a lie
Exactly-once across independent systems (producer → queue → consumer → downstream) is provably impossible without transactional coordination. The proof: generals problem. Any message to the downstream can be lost; any response can be lost. You always have some retry strategy, which means either "don't retry" (at-most-once) or "retry until confirmed" (at-least-once).
But Kafka claims exactly-once! True — within Kafka, with the transactional API. It works because:
Producer and broker share a transaction ID; the broker deduplicates messages with the same producer + sequence number.
Consumer reads messages AND commits offset in the same Kafka transaction. Read + offset-commit succeed together or both get rolled back.
"Exactly once from Kafka topic → Kafka topic" is the only rigorous claim. Writing to an external DB at the end (side effect) breaks the guarantee unless the external DB is also part of the transaction.
Idempotent consumers are the real answer. Assign each message a unique ID. The consumer maintains a "seen" set (usually a DB unique constraint). Duplicate message → unique constraint violation → ignore silently. Logically exactly-once with at-least-once delivery underneath.
Interview answer
"We use at-least-once delivery with idempotent consumers. Each message has a unique event ID, and we enforce uniqueness in the DB. This gives us exactly-once semantics end-to-end without needing distributed transactions."
05
How to make consumers idempotent
Deterministic side effects. If processing the same message twice produces the same result, you don't need anything special. "Set user X status to active" is idempotent. "Increment user X counter" is not.
Unique-key upserts. INSERT ... ON CONFLICT DO NOTHING. The second delivery of the same message becomes a no-op.
Processed-ID table. Maintain processed_events(event_id). Start of handler: INSERT; on duplicate-key error → already processed, return. On success, commit business change + INSERT together in one DB transaction.
Idempotency keys in APIs. Client sends Idempotency-Key header; server stores first response by key; retries return the cached response. Stripe's approach.
06
Real-world
Stripe payments
At-least-once + idempotency keys
Clients include Idempotency-Key on charge requests. Retries of the same request return the same charge without double-billing. The industry gold standard.
Kafka transactional
Exactly-once within Kafka
Producer-to-consumer exactly-once via transaction IDs + offset commits in the same transaction. Breaks when writing to external systems.
Email dispatch
At-least-once (and accept duplicates)
Mail services typically guarantee at-least-once. The occasional double email is preferable to losing password resets.
Analytics pipelines
At-most-once, usually
Dropping 0.01% of events barely affects metrics. Operational simplicity wins over correctness.
07
Used in problems
Payment gateway requires idempotency keys for every charge. WhatsApp message delivery uses at-least-once with client-side deduplication. Notification system uses at-least-once with idempotent hooks. Distributed queue exposes at-least-once semantics by default.