A field manual for the day one click became two charges
The Ack.
Every message that crosses a wire carries a promise about delivery — and there are only three shapes that promise can take. The wordiest one, exactly-once, is a marketing term; the real thing is assembled from two humbler parts. You will lose a message, lose a receipt, and watch one $120 click become two charges — then build the receipt book that makes it impossible. This is the machinery underneath The Lock’s idempotency keys and The Cache’s outbox consumers.
the third packet never came home. the sender is about to make a decision.
BEGINThree shapes of a promise.
Even inside one process, a function can change state and then throw or crash; local work needs a transaction if it must be atomic. A wire adds another uncertainty: the packet or its reply can vanish. And here is the cruel part: silence looks identical to loss. A timeout alone cannot tell a dead wire from a slow one — you held that knife once already, in the CAP essay’s asynchronous network. So every sender must choose what to do about not knowing. There are three common delivery policies:
At-most-once — send and never look back. A lost message is gone forever, but nothing ever happens twice. At-least-once — repeat until a receipt comes home. Nothing is ever lost, but a lost receipt means the truth gets delivered twice. Exactly-once — the promised land. This essay’s argument is that it does not exist as a transport property at all; it exists as an effect, assembled at the receiver from the first two. Every duplicate charge you have ever suffered was assembled the same way.
You have met all three already without the names. The Cache introduced an outbox relay that published at-least-once and its consumers “deduped by event id.” The Lock’s flaky-network toggle retried with “an idempotency key the service knows and will not act on twice.” This page is those two sentences, unfolded.
Break it yourself.
Below: one order service, one payment service, one unfaithful wire. Choose which promise the sender makes, then arm a break and place an order. Two kinds of packet fly: the order (ink, top lane) and the receipt — the ack — flying home (green, bottom lane). Losing the message and losing the receipt are completely different crimes. One of them costs you $240.
Run the crime scene twice. Break the message: in at-least-once, the sender waits, panics politely, retries — the order arrives once, one charge. Fine. Now break the receipt: the charge is real, the money has moved, but the sender never learns it. Its timeout fires, it retries the same order — and a receiver with no receipt book executes the same charge a second time. The network never delivered anything twice. The sender’s doubt did.
Now switch the promise to exactly-once and arm the receipt-break again. The duplicate arrives — and dies at the door. The receiver’s receipt book recognizes the order, charges nothing, and — this is the detail everyone forgets — sends the receipt anyway. A receiver that stays silent about a duplicate has just armed the sender’s timer for another round. The receipt book doesn’t just prevent the double charge; it is what lets the conversation ever end.
The sender has one knob, and both ends hurt.
How long should a sender wait before deciding a receipt is lost? Wait too long and every real failure stretches your confirmation time — customers stare at spinners. Wait too short and you retry on receipts that are merely still in flight — duplicating work on a healthy wire. The timeout is not a performance setting. It is a doubt dial, and doubt is priced in duplicates.
Drag the timeout below ~2 seconds — the wire’s true round trip — and watch duplicates explode with zero packet loss. That is the dial’s secret: a timeout shorter than reality manufactures duplicates out of impatience alone. Drag it up and duplicates fall to exactly the lost-receipt tax while confirmation time climbs. There is no correct setting. There is only the one you tuned — and a receiver with a receipt book, which makes the dial safe at any position. That is why Stripe’s API tells you to retry with the same idempotency key: the key is what turns a retry into a reminder instead of a second transaction.
Why the perfect promise can’t exist.
Before you file exactly-once under “engineering gap,” look underneath: it is a theoretical floor, not a missing feature. Two armies camp on opposite hills and must attack together or both die. Their only couriers can be captured. Every protocol you invent ends with someone attacking on faith — because after any finite exchange of messages, the sender of the last one can never know it arrived. Certainty about delivery requires an infinite regress of receipts: “did you get my ack?” “did you get my ack of your ack?” No finite conversation escapes. This is not pessimism; it is the same species of proof as Gilbert–Lynch for CAP and 2PC’s in-doubt freeze — a theorem with a body count.
THE TWO GENERALS — THE REGRESS IN FOUR SENTENCES
Suppose a protocol existed that let both generals know the attack is agreed. Take its final message — the last one sent. The sender of that message cannot distinguish “delivered” from “captured,” so it is still uncertain: the protocol failed to deliver certainty.
Every candidate protocol has a last message, by definition — a finite exchange must end. So the argument applies to all of them, and no finite protocol can do it.
The escape is the one you have already practiced: replace certainty about delivery with an effect that survives redelivery. Don’t prove the message arrived once — make arriving twice indistinguishable from arriving once.
The same move powers 2PC’s durable decision log, Raft’s terms, and the receipt book in FIG. 01. The floor is real; the engineering is what you build on top of it.
One honesty note, because the marketing will find you anyway: exactly-once does exist in scoped places. Kafka’s transactions give exactly-once within Kafka — consume, process, produce, all inside one broker-side transaction — because there the “wire” and the “receipt book” live in the same system with one log. The moment the boundary crosses to your payment provider, the regress returns, and it is receipt books all the way down.
The field guide.
- At-most-once
- One delivery attempt at this boundary, with no transport retry. Loss is possible; this boundary does not create duplicate deliveries. Useful when the workload explicitly tolerates missing samples more than repeats.
- At-least-once
- Retry an unacknowledged message until it is confirmed or the retry policy ends. With durable storage, recovery, and eventual delivery, this avoids permanent loss; duplicate delivery is possible, not inevitable.
- Ack / receipt
- The receiver’s confirmation. Not a polite gesture — a durable fact, written before it is sent, because the sender will plan its whole life around it.
- Idempotency key
- A name for the intent, not the attempt. Reused across every retry of the same logical operation. The single word that separates a retry from a duplicate.
- Receipt book
- The receiver’s seen-set of keys, committed atomically with the effect. Duplicates arrive, are recognized, are ignored — and are re-acked regardless.
- Exactly-once effect
- Repeated attempts have one durable effect when the effect and deduplication record commit together, the same intent keeps its key, and that record outlives all possible retries.
- Poison message
- A message that crashes the receiver every time it is delivered. With at-least-once, it becomes an infinite retry loop — the herd’s cousin from The Cache, wearing a different coat.
- DLQ
- Dead-letter queue: where the poison and the permanently-failed go after N tries, so one bad message cannot hold the pipeline hostage. Every retry policy needs an exit.
Three scenarios.
From these figures, you can trace a retry through an uncertain acknowledgement and place the receipt in the same transaction as the effect. Try changing one assumption and check whether your explanation still holds.
The sealed sheetThree questions are sealed inside this sheet. Nobody is asked to open it — the receipt book already forgives.Break the seal
A customer clicks Pay once. The card is charged twice. In FIG. 01's terms, where was the actual fault?