Idempotency
Mental model
An idempotent operation can be safely repeated without creating additional business effects.
This is especially important for payments and order creation.
The Problem
Client
│
│ POST /payments
▼
Payment Service
│
├── charge card ✓
│
└── response lost
│
▼
Client retries
│
▼
Payment Service
│
└── charge card AGAIN ❌The client cannot tell whether the first request succeeded.
Idempotency Key
POST /payments
Idempotency-Key: 8c5e...The server stores the result associated with the key.
idempotency_key
│
├── request fingerprint
├── status
├── response
└── created_atJava Concept
String key = request.getHeader("Idempotency-Key");
var existing = repository.findByKey(key);
if (existing.isPresent()) {
return existing.get().response();
}The database must enforce uniqueness:
CREATE UNIQUE INDEX
idx_idempotency_key
ON idempotency_keys(key);The unique constraint is important because two requests can arrive simultaneously.
Race Condition
Request A ──┐
├── find key → not found
Request B ──┘
└── find key → not foundBoth could proceed unless the database provides a concurrency-safe uniqueness guarantee.
Interview answer
Never rely only on “check then insert.” Use a unique constraint and design the operation around the possible race.
.NET Concept
var existing =
await db.IdempotencyKeys
.SingleOrDefaultAsync(x => x.Key == key);Then persist the key/result as part of the operation, backed by a unique database constraint.
Idempotency and Kafka
Kafka consumers can also receive duplicates.
A consumer might store the event ID:
processed_events
----------------
event_id UNIQUEThen:
if (processedEventRepository.exists(event.id())) {
return;
}In production, the business update and processed-event record should be designed carefully so a crash cannot leave them inconsistent.
What Makes an API Idempotent?
PUT is defined to be idempotent by HTTP semantics.
POST is not inherently idempotent, but a payment API can be made operationally idempotent using an idempotency key.
Interview Questions
Why UUID?
A UUID gives us a globally unique identifier with extremely low collision probability. But a UUID alone does not provide idempotency. We still need server-side deduplication.
What if the same idempotency key is reused with different request data?
Reject it. The key should identify one logical operation, and the server can compare a request fingerprint/hash with the original request.
Where should the idempotency record live?
For a service backed by PostgreSQL, a database table is often appropriate because uniqueness and transactional behavior are important.
Remember
UUID identifies. Unique constraint enforces. Idempotency key deduplicates.