Idempotency & Exactly-Once Illusion
Read a little, play a little. No scary maths, and no rush.
The customer who was charged twice
A user taps "Pay". The payment provider takes the money, then the response is lost — a dropped connection, not a bug. The app cannot tell the difference between "never arrived" and "arrived and got lost", so it retries. Now the user has been charged twice, and support gets the email.
Every network call sits on that knife edge: you cannot know whether it ran. Idempotency is how you make retrying safe instead of dangerous.
Idempotent means "safe to repeat"
A call is idempotent when repeating it has the same effect as calling it once. DELETE /cart/42 is naturally idempotent — the cart is either deleted or it isn't. POST /payments is not: the second call creates a second payment. So the verbs have different rules:
GET— safe, changes nothing.PUT— idempotent by definition; it replaces, not adds.DELETE— idempotent in effect.POST— not idempotent. This is the dangerous one.
The fix is a table and a key
You cannot change the semantics of POST, so the server makes the retry safe. The client generates a unique key per user intent — an Idempotency-Key header, or a client-generated order_id — and sends it with every attempt.
POST /payments Idempotency-Key: 8f2c-...
1. Key seen before? → return the stored response, do nothing else
2. Mark key as "in progress" ← this claim is what closes the race
3. Do the real work (charge once)
4. Store the response against the key
The subtle part is step 2. Two identical requests can arrive at the same instant — the client's timeout fires while the first is still running, so both are in flight. Without an atomic claim, both see "key not seen", both charge. With it, the second gets a conflict and waits or gets told to retry later.
Why this is "exactly once" in quotes
You cannot actually make two systems — your app and the payment provider — agree on one truth across two databases. What idempotency gives you is the same observable result from the caller's side: one charge, one response, even if the network tried five times. Every real system settles for that illusion, and it holds as long as the key is stored durably and never reused.
Remember this
- You cannot know if a call ran, so assume the retry is coming and make repeats harmless.
POSTis the non-idempotent verb; design it with a client-supplied key.- The atomic "claim" on that key is what stops two simultaneous duplicates.
- Store the response, not just a flag — the replay should be byte-identical.
Check your understanding
2 questions · correct answers earn XP once each
My notes
Saved in this browser. Highlight a line above and save it, or write it in your own words.
Nothing saved yet. Your highlights will live here.
References
Finished reading?
Ticking it here also ticks the chapter in the sidebar, the section count and your streak — it is all one number.
Related chapters
Spotted a mistake or want a topic covered? Report an issue