Notes: Idempotency in Distributed System

Idempotency, in practical terms, ensures that repeating the same operation, whether due to retries, user error, or network issues, has no unintended side effects. It guarantees that an operation can be safely retried without causing duplicate changes.
A common real-world example is a payment system. Suppose a user tries to pay for the same order twice, either due to a network failure or by retrying manually, the second attempt should not create a duplicate payment. Instead, the system should ensure that only one successful payment is processed per order, typically by mapping the order ID to a unique payment ID. Any subsequent request for the same order is either ignored or the system returns the original payment result, maintaining consistency and preventing double charges.
This mechanism of preventing duplicates and ensuring consistency is what we call idempotency.
In this blog, we’ll cover some common methods to handle Idempotency in Distributed Systems.
Idempotency Keys
An idempotency key is a unique identifier that clients generate (can be requested from server as well for some use cases, potentially to avoid concurrency issues) and send with an API request to ensure that the request is processed only once, even if it's retried. This is particularly important for non-idempotent HTTP methods like POST, where multiple requests with the same parameters could lead to unintended side effects.
Step by step process:
Generation: The client generates a unique identifier, often a UUID or a randomly generated string, and includes it in the request as an "Idempotency-Key" header.
Server Handling: When the server receives the request, it checks for the presence of the idempotency key in a cache or a database. If the key exists, the server verifies if it's already been used for a similar request.
Duplicate Detection: If the server detects that the key has been used previously, it either returns the result of the previous request or skips the operation entirely. This allows clients to retry requests without fear of causing duplicate actions or unexpected outcomes.
Example: Imagine a client trying to make a payment. They send a request with an idempotency key. If the payment fails, they can retry with the same key, and the server will either return the payment result if it was successful or skip the payment if it's already been processed.
PUT over POST
PUT ensures that the resource is created if it doesn’t exist. If it exists, update it or ignore it, don’t recreate.
Example: PUT /user/123 with same payload doesn’t change state if called multiple times.
Database Constraints
We can use unique constraints to avoid duplicate inserts in tables e.g. user registration (unique email), product addition (unique sku) etc.
INSERT ... ON CONFLICTquery to avoid duplicate inserts in tables.
Distributed Locks
To carry out an idempotent operation in concurrent environments, distributed locks can also be used. To know more about distributed locks, read this blog: Distributed Locks.
Example: Consider the payment scenario
Acquire a distributed lock for
order:{orderID}.Check if a payment already exists for that order.
If not, create a payment and release the lock.
If yes, return the existing result.



