Idempotency & exactly-once
Networks retry. The same "charge card" message can arrive twice. Now you've double-charged.
Open the interactive version → diagrams, practice & moreThe problem
Networks retry. The same "charge card" message can arrive twice. Now you've double-charged.
The idea
Make operations idempotent: applying them twice has the same effect as once.
How it works
Make operations idempotent so applying them twice equals once. The client attaches an idempotency key; the server atomically records "key → result" and on a duplicate returns the stored result instead of redoing the work. *Exactly-once delivery is impossible over an unreliable network, so the real recipe is at-least-once delivery + idempotent processing = exactly-once effect*. Enforce it where it's atomic — a unique constraint or INSERT … ON CONFLICT — not a check-then-act two concurrent retries can both pass.
The tradeoff
You must store and check keys with a retention window long enough to outlive retries but bounded so it doesn't grow forever. The transactional-outbox pattern closes the sibling gap (DB write commits but the event publish fails) by writing the event in the same transaction and relaying it later. The cost is bookkeeping; the payoff is the only safe way to handle the retries that will happen.
In the wild
Stripe's API uses an Idempotency-Key header for exactly this reason.
Interview deep dive
Flow
- Client generates an idempotency key per logical operation.
- Server atomically records key → result (unique constraint).
- A duplicate key returns the stored result, skipping the work.
- Expire keys after a window that safely outlives retries.
Watch for
- Check-then-act lets two concurrent retries both slip through — make it atomic.
- Key retention too short → a late retry double-applies.
- DB write + separate publish can diverge — use a transactional outbox.
Interviewer trap
Say exactly-once effect = at-least-once delivery + idempotent apply, enforced atomically.