Idempotency & the Exactly-Once Illusion
Over a lossy wire, “did it happen?” is unanswerable — so retries are the default reality. Exactly-once delivery is impossible; exactly-once effects are not. The property that buys them back is idempotency, drawn and computed.
Concept · Systems. The source ↗
A free, interactive, animated visual explainer of Idempotency & the Exactly-Once Illusion — built to be understood, not skimmed.
Questions
- Is exactly-once delivery possible?
- Not over an unreliable network. The two-generals result proves it: because an acknowledgement can be lost as easily as the original message, no finite exchange lets the sender know for certain the message arrived — so it must either retry (risking a duplicate) or not (risking a loss). What is achievable is exactly-once effects: deliver at-least-once, but make applying a message twice have the same result as applying it once. That property is idempotency.
- What is an idempotency key?
- A unique value the client generates and attaches to a request so the server can recognize retries of the same operation. Stripe puts it plainly: "A client generates an idempotency key, which is a unique key that the server uses to recognize subsequent retries of the same request." The server saves the first response for that key and returns the same saved result on every retry, so a timed-out-and-retried payment charges the card once, not twice.
- What is the difference between at-least-once and at-most-once delivery?
- They are the two honest failure modes when the wire cannot promise exactly-once. At-most-once acknowledges before doing the work, so a crash in between loses the message but never duplicates it. At-least-once does the work (or commits) before acknowledging, so a crash re-delivers the message but never loses it. At-least-once plus an idempotent consumer is the combination worth wanting: never lost, and duplicates are harmless.
- What is a fencing token?
- A monotonically increasing number handed out with a lock or lease so a storage system can reject a stale writer. If a process pauses (a long GC, a network partition) and its lease expires, another process takes over with a higher token. When the paused "zombie" wakes and tries to write with its old, lower token, the storage layer sees a number it has already surpassed and refuses the write — turning a would-be corruption into a safe rejection.
- How long should a dedup or idempotency window be?
- It is a memory-versus-safety trade-off. The server must remember every seen key for the window so it can catch a duplicate; a longer window costs more memory but catches later retries. A retry that arrives after the window has evicted its key is treated as new and applied twice. Stripe stores keys for a bounded window — "You can remove keys from the system automatically after they’re at least 24 hours old" — which comfortably covers client retries while bounding storage. Size the window to your longest realistic retry delay, not longer.
Related explainers
- Design a Stream Processor with Exactly-Once
- Design a Distributed Cache
- Back-of-the-Envelope: the Numbers That Design Systems
- Threads, Locks & the Anatomy of a Race
- Consistency, Quorums & CAP
- How to Design a System in 60 Minutes
- Rate Limiting: Four Algorithms, Honestly Compared
- Design Ad-Click Aggregation (Lambda vs Kappa)