The 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.
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.
Common trap
Say exactly-once effect = at-least-once delivery + idempotent apply, enforced atomically.
Common questions
What problem does Idempotency & exactly-once solve?
Networks retry. The same "charge card" message can arrive twice. Now you've double-charged.
How does Idempotency & exactly-once work?
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…
What are the tradeoffs of Idempotency & exactly-once?
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…
Where is Idempotency & exactly-once used in production?
Stripe's API uses an Idempotency-Key header for exactly this reason.