Problem
After a timeout, a sender cannot tell whether a message was lost or only its acknowledgment was. It must either retry and risk duplicates, or not retry and risk loss.
Also known as: exactly-once-delivery-myth, at-least-once-plus-idempotence
Achieve exactly-once effects by combining at-least-once delivery with idempotent processing, because exactly-once delivery itself is not possible over unreliable networks.
After a timeout, a sender cannot tell whether a message was lost or only its acknowledgment was. It must either retry and risk duplicates, or not retry and risk loss.
Exactly-once delivery over an unreliable network is impossible: it is a variant of the Two Generals problem. What systems offer is exactly-once effect, built from at-least-once delivery plus idempotent handling. The illusion is that each message was delivered once; the reality is that duplicates arrived and were harmlessly ignored.
Think of it as two separate guarantees: the transport promises nothing is lost, and the receiver promises repeats change nothing. Together they produce the observable behavior of exactly one effect per message.
The sender retries until it sees an acknowledgment. The receiver tracks which messages it has already applied.
producer --msg(id=42)--> consumer
(ack lost)
producer --msg(id=42)--> consumer retry
consumer:
BEGIN
if exists(processed, 42) then skip
else apply(effect); insert(processed, 42)
COMMIT
send ack
The effect and the processed marker must commit atomically. For HTTP APIs, clients send an idempotency key and the server returns the stored result on repeats. Kafka's idempotent producer and transactions apply the same principle inside the Kafka ecosystem, but external side effects still need their own protection.
Low: storage for dedupe keys
Medium: expire and monitor dedupe state
Medium: idempotent design discipline
Dedupe record and side effect not committed atomically
Idempotency keys expire before a late retry arrives
Non-idempotent external call hidden inside the handler