Idempotency
Make repeated requests safe so retries and at-least-once delivery stop being frightening.
Open this lesson in the learning hubKey points
- Idempotent means doing it twice has the same effect as doing it once. In a distributed system you will do it twice.
- GET, PUT and DELETE are naturally idempotent. POST is not, and that is where all the work is.
- Take an
Idempotency-Keyheader. Store the key with its result; on a repeat, return that result instead of redoing the work. - Back it with a database unique constraint. A duplicate then fails loudly instead of silently creating a second order.
- For message consumers, dedupe on the event id. A processed-ids table written in the same transaction as the work is cheap and reliable.
Example
@PostMapping("/api/payments")
ResponseEntity<Payment> pay(@RequestHeader("Idempotency-Key") String key,
@RequestBody PaymentRequest request) {
// Seen this key before? Replay the original answer, charge nobody twice.
Optional<Payment> existing = payments.findByIdempotencyKey(key);
if (existing.isPresent()) {
return ResponseEntity.ok(existing.get());
}
try {
// Table has: UNIQUE (idempotency_key)
Payment saved = payments.save(Payment.of(key, request));
return ResponseEntity.status(HttpStatus.CREATED).body(saved);
} catch (DataIntegrityViolationException raced) {
// Two identical requests landed at the same moment.
// The loser returns the winner's result.
return ResponseEntity.ok(payments.findByIdempotencyKey(key).orElseThrow());
}
}
Assume every request arrives twice, then make the second one harmless.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Microservices course, and every lesson in it is listed on the Microservices contents page.