Idempotency
Definition

HTTP Methods and Idempotency

Implementing Idempotency
// Method 1: Idempotency Key
@RestController
public class PaymentController {
@PostMapping("/payments")
public ResponseEntity<Payment> createPayment(
@RequestBody PaymentRequest request,
@RequestHeader("Idempotency-Key") String idempotencyKey) {
// Check if already processed
Optional<Payment> existing = paymentRepo
.findByIdempotencyKey(idempotencyKey);
if (existing.isPresent()) {
// Return same result - don't process again
return ResponseEntity.ok(existing.get());
}
// Process payment
Payment payment = paymentService.process(request);
payment.setIdempotencyKey(idempotencyKey);
paymentRepo.save(payment);
return ResponseEntity.status(201).body(payment);
}
}
// Method 2: Conditional Updates (Optimistic Locking)
public class AccountService {
public void updateBalance(String accountId, BigDecimal newBalance,
long expectedVersion) {
int updated = jdbcTemplate.update(
"UPDATE accounts SET balance = ?, version = version + 1 " +
"WHERE id = ? AND version = ?",
newBalance, accountId, expectedVersion
);
if (updated == 0) {
throw new ConcurrentModificationException();
}
}
}
// Method 3: Natural Idempotency Key
public class OrderService {
// Using order ID makes create idempotent
public Order createOrder(String orderId, OrderRequest request) {
// If order exists, return existing
return orderRepo.findById(orderId)
.orElseGet(() -> {
Order order = new Order(orderId, request);
return orderRepo.save(order);
});
}
}
Idempotency in Distributed Systems

Database Idempotency
// Pattern 1: UPSERT (INSERT ... ON CONFLICT)
public class UserRepository {
public void saveOrUpdate(User user) {
// PostgreSQL
jdbcTemplate.update("""
INSERT INTO users (id, email, name)
VALUES (?, ?, ?)
ON CONFLICT (id) DO UPDATE
SET email = EXCLUDED.email, name = EXCLUDED.name
""", user.getId(), user.getEmail(), user.getName());
}
}
// Pattern 2: Conditional INSERT
public class EventRepository {
public boolean saveIfNotExists(Event event) {
try {
jdbcTemplate.update(
"INSERT INTO events (id, type, data) VALUES (?, ?, ?)",
event.getId(), event.getType(), event.getData()
);
return true; // New event saved
} catch (DuplicateKeyException e) {
return false; // Already exists
}
}
}
// Pattern 3: Compare-and-Swap
public class InventoryService {
public boolean decrementStock(String productId, int quantity,
int expectedStock) {
int updated = jdbcTemplate.update("""
UPDATE inventory
SET stock = stock - ?
WHERE product_id = ? AND stock = ?
""", quantity, productId, expectedStock);
return updated > 0; // True if successful
}
}
Non-Idempotent to Idempotent

Idempotency Key Best Practices

Tips & Tricks
