Idempotency Patterns Cheat Sheet

Idempotency patterns for distributed systems — exactly-once semantics, idempotency keys, deduplication strategies, HTTP idempotency, and database techniques to pr.

Last Updated: May 1, 2025

Core Concepts

ItemDescription
Idempotencyf(f(x)) = f(x). Applying an operation multiple times produces the same result as applying it once. No additional side effects on retry.
Idempotency KeyUnique identifier (UUID) sent with each request. Server stores key + response. On retry, server returns stored response instead of re-processing.
Exactly-Once SemanticsThe holy grail: each message processed exactly once. Achieved by combining at-least-once delivery with idempotent processing on the receiver.
DeduplicationDetecting and ignoring duplicate requests. Can be done at the application layer (idempotency keys) or infrastructure layer (message broker dedup).
At-Least-Once DeliveryMessage brokers guarantee delivery ≥ 1 time. Consumer MUST be idempotent. RabbitMQ, Kafka, SQS all default to at-least-once.
Exactly-Once Delivery (Kafka)Kafka transactions + idempotent producer: enable.idempotence=true. Guarantees no duplicates within the producer session.

HTTP Idempotency

MethodIdempotent?Safe?Semantics
GETYesYesRetrieve resource. Multiple identical GETs = same response, no side effects.
HEADYesYesSame as GET but no body. Useful for checking existence.
PUTYesNoFull replacement. Same body N times = same end state. Required by RFC 7231.
DELETEYesNoDelete resource. First call deletes, subsequent calls return 404. End state is the same.
OPTIONSYesYesDescribe communication options. No side effects.
POSTNoNoCreate resource. Each POST creates a NEW resource. NOT idempotent. Use Idempotency-Key header.
PATCHNoNoPartial update. May or may not be idempotent depending on patch content (use with care).

Implementation Strategies

ItemDescription
Stripe-Style Idempotency KeyClient generates UUID. Sends in `Idempotency-Key` header. Server: (1) check key in DB, (2) if found, return stored response, (3) if new, process + store response + key. Keys expire after 24h.
Database Unique ConstraintsNatural idempotency: INSERT ON CONFLICT DO NOTHING. Use order_id as primary key — second INSERT with same order_id is a no-op.
Version-Based UpdatesUPDATE items SET qty=qty-1, version=version+1 WHERE id=5 AND version=7. If version already incremented, second call affects 0 rows.
Transactional OutboxWrite event + mark operation as processed in one DB transaction. On retry, check if already processed before producing duplicate events.
Idempotent Consumer (Kafka)Store consumed offsets. On rebalance, reprocess from last committed offset. Use message key as dedup key in a processed-messages table.
Request FingerprintHash(request body + endpoint + timestamp window). If hash seen before → duplicate. Less storage than full key-value store but potential collisions.

Common Pitfalls

ItemDescription
POST without idempotency keyThe #1 cause of duplicate orders and double charges. Always include Idempotency-Key on POST that creates resources.
Idempotency key reuseClient must generate a NEW key for each distinct request. Reusing the same key for different payloads = first payload wins forever.
Key expiration too shortIf keys expire before the retry window closes, a legitimate retry creates a duplicate. Match expiration to your retry policy max window.
Non-atomic check-then-actRace condition: two concurrent requests both see key as new and both process. Use DB-level uniqueness constraints: INSERT fails on key collision.
Partial failure side effectsRequest succeeds partially (payment captured, email not sent). Idempotency key stores success response — retry returns 'success' without sending the email.
Pro Tip: The golden rule of distributed systems: assume every request will be delivered at least once, and design your handlers to be safe when called multiple times. Idempotency keys are the cheapest distributed safety net you can buy.