← System Design
Core Concepts

What is Idempotency?

An operation is idempotent if doing it once or doing it many times has the same effect. "Set the balance to 100" is idempotent; "add 100 to the balance" is not. In distributed systems, retries and duplicate deliveries are normal, so any operation that might be repeated must be made idempotent, or users get charged twice.

In simple words

Pressing a lift button five times doesn't call five lifts; it's still one request. An idempotent operation is like that: doing it again by accident changes nothing. That's exactly what you want for "pay ₹500" when the network makes the app retry.

Retry with an idempotency keyClient(retries on timeout)Payments APIIdempotency keysCharge cardkey K1seen K1?only once
A retry with the same key K1 gets the saved result back instead of charging the card again.

Why It Matters

client --POST /pay--> server (charges card, commits)
client <---- response lost (timeout) ----
client: "did it work? retry!" --POST /pay--> charged AGAIN

The client cannot tell "the request failed" from "the request succeeded but the response was lost". Queues deliver at-least-once, so consumers see duplicates too. The safe answer is to retry, and to make the retry harmless.

How It Works

POST /v1/payments
Idempotency-Key: 5f1c-...-9a2e      (client-generated, reused on retry)

server:
  BEGIN
    INSERT INTO idempotency(key, status) VALUES ($key, 'processing')
      -- unique key: a duplicate insert fails -> return the stored response
    charge card ...; store response under key
  COMMIT

Key Pointers

Follow-up Questions

Two retries with the same key arrive at the same time. What happens?
The unique constraint (or an atomic set-if-absent) lets only one proceed; the other either waits and returns the stored result, or gets a "request in progress" (409) and retries later.
Can a message consumer be idempotent without keys from the client?
Yes: use the message id (or a business id such as order id) and record processed ids, or make the operation naturally idempotent (upsert by id, set state rather than increment).

Related