Idempotency Key
A unique token a client attaches to a request so the server can recognize a retry and return the original result instead of doing the work twice, the standard defense against double charges and duplicate orders.
An idempotency key is a unique value that a client attaches to a request so that sending the same request again has no further effect. The server records the key along with the result of the first attempt. If a request with the same key arrives later, the server returns the stored result instead of repeating the work.
The problem it solves is old and unavoidable. A client sends a payment request and the connection drops before the reply arrives. The client can't tell whether the server never received the request, received it and failed, or charged the card and lost the response. No protocol can remove this uncertainty; see Timeout. Retrying risks a double charge, and not retrying risks a lost order.
HTTP defines some methods, such as PUT and DELETE, as idempotent, safe to repeat, but POST, the usual way to create something, isn't; see Idempotence. An idempotency key makes a non-repeatable operation repeatable. Payment companies popularized the pattern, most visibly Stripe, whose API accepts an Idempotency-Key header and keeps keys for at least a day. An IETF working group has drafted a standard version of the header.
A typical implementation:
- The client generates a random key, usually a UUID, when the user starts an action, not when the request is sent, so every retry carries the same key.
- The server tries to record the key before doing the work, relying on a unique index, a Database Constraint, so that two concurrent copies can't both proceed.
- If the key is new, the server does the work and stores the response under the key.
- If the key exists and the work is finished, the server replays the stored response. If it's still in progress, the server tells the client to wait.
The details matter. Recording the key and doing the work should happen in one ACID transaction, or a crash between them leaves the two out of step. A retry that reuses a key with a different request body is a client bug and should be rejected rather than silently answered. Keys need an expiry, or the table grows forever.
The same idea underpins reliable messaging. A Message Queue usually promises that a message arrives at least once, which means consumers must cope with duplicates; deduplicating on a message ID gives the effect of exactly-once processing. Workflow engines that resume after crashes rely on it for each step. An Upsert keyed on a natural key is idempotent by construction.
Clients still need to retry sensibly, spacing attempts out with Exponential Backoff so a recovering server isn't flooded.
See also2
Linked from9
- Database ConstraintData & Content
Rules declared in the schema that the database itself enforces on every write, such as uniqueness, required values, and allowed ranges, so bad data is refused at the door rather than cleaned up later.
- Exponential BackoffNetworks & Distribution
Waiting longer after each failed attempt, with a dose of randomness, so retries give a struggling system room to recover instead of hammering it.
- IdempotenceSystems & Tooling
The property that performing an operation twice has the same effect as performing it once.
- Message QueueData & Content
A buffer that decouples a producer from a consumer in time, so neither has to be up, fast, or reachable at the same moment.
- Optimistic UIWeb Platform
Updating the UI as if a server request has already succeeded, before the server has actually responded.
- TimeoutNetworks & Distribution
The limit on how long to wait for a remote call, the most basic defense against a hung dependency, and the harder problems of choosing the number and passing the remaining time down a chain of services.
- Universally Unique IdentifierData & Content
A 128-bit identifier that any machine can generate without coordination and still be practically certain no one else will produce the same one.
- UpsertData & Content
Inserting a row if it doesn't exist and updating it if it does, in one atomic statement, which is harder than it sounds because the obvious check-then-insert version races with itself.
- WebhookNetworks & Distribution
A webhook is an HTTP callback a service pushes to you when an event happens, inverting who initiates the request.