Idempotency

An operation is idempotent when it has the same effect if it runs twice or once. A check against a store of seen IDs takes about 0.3 ms. That small cost turns each repeated delivery into no extra effect.

Play a level

See it on the board

The same traffic runs on two boards. The simulator measured both when this page was built.

Board to show
SQLUsersMessage bro…1 vCPU · 1 GBWorker2 vCPU · 2 GBPostgres2 vCPU · 2 GB
AMQPHTTP/2SQLUsersMessage br…WorkerPostgres
Each repeated batch adds to the counts: misses a goal
gRPCSQLUsersMessage bro…1 vCPU · 1 GBWorker2 vCPU · 2 GBPostgres2 vCPU · 2 GBKey-value s…1 vCPU · 2 GB
AMQPHTTP/2gRPCSQLUsersMessage br…WorkerPostgresKey-value…
Store of seen click IDs before the counts: meets the goals
See it on the board
Measure Goal Each repeated batch adds to the counts Store of seen click IDs before the counts
Duplicates none 60 Fail 0 Pass
Lost writes none 0 Pass 0 Pass
Completion time under 150 s 103.32 s Pass 103.32 s Pass
Monthly cost 20 USD/mo 28 USD/mo
p99 latency 8.5 ms 8.5 ms
Error rate 0% 0%

Key numbers

Default part values from the catalog and the price table. The simulator uses the same values.
NumberValue
Look up one ID in a key-value store, median0.3 ms
Lookups per second, one key-value store30,000 ops/s
Time before a queue sends a job again30 s
Price of one key-value store4 USD/mo
Delivered twice, charged onceTap to turn the ID check on or off

13 deliveries · 10 payments applied · 0 charged twice

How it works

Repeats are normal in distributed systems. A client retries after a timeout, a queue delivers a job again after a lost ack, and a user clicks a button twice. If each repeat charges a card or adds to a count, the result is wrong.

Some operations are idempotent by nature. To set a field to a value gives the same result each time. To add to a total or to send money does not. To make such an operation safe, give each request a stable ID, such as an idempotency key or an event ID. Before the work, the service looks for the ID in a store of seen IDs. If the ID is new, it does the work and records the ID. If the ID is known, it returns the saved result and does nothing more.

The check and the effect must be atomic. If the service records the ID but stops before the effect, the work is lost. If it does the effect but stops before it records the ID, the work runs twice. Keep both in one transaction, or use a conditional write.

The store of seen IDs grows with each request. Keep each ID only as long as a repeat can arrive, for example a few days.

In the levels you add a store of seen IDs so that each repeated delivery counts once.