Idempotency keys explained
6 razones why critical · 6 implementation patterns (Postgres UNIQUE · Redis SETNX · workflow state · outbox · hash body · path) · 8-step key lifecycle · 6 failure modes documented. Reliability foundation que evita double-charges · double-messages · double-appointments.
6 razones · por qué críticos
6 implementation patterns
Key lifecycle · 8 steps
- GENERATION: cliente genera UUID v4 OR provider assigns (Stripe event.id) OR derive from semantic (message_id Meta) · NEVER server-assigned
- TRANSMISSION: idempotency key transmitted Header `Idempotency-Key: <uuid>` OR query param OR body field · documented per-endpoint
- VALIDATION: server checks key store (Postgres unique OR Redis SETNX) · si exists return CACHED response (status + body) · si new proceed
- PROCESSING: si new key · process request · ATOMICALLY persist key + response · DB transaction guarantees consistency
- RESPONSE: return result (NEW process success · OR CACHED response replay si idempotency hit) · client recibe identical response either way
- TTL EXPIRY: keys expire after 24h-7d retention (configurable) · garbage collection background job · prevents unbounded growth
- AUDIT: idempotency hits logged (info level) · pattern analysis · if hits spike investigate (potential bug client side · attack)
- RECOVERY: si idempotency store data loss (Redis flush · DB restore) · accept brief duplicate risk OR coordinate via DB transactions · documented runbook
Failure modes · 6 escenarios
| Mode | Action |
|---|---|
| Key collision (different requests same key) | Reject second request HTTP 422 · log warning · cliente bug suspected · investigate · NEVER process two distinct ops same key |
| Idempotency store unavailable (Redis down) | Fallback Postgres unique constraint · slower pero correct · degraded performance acceptable vs incorrect duplicate processing |
| Process crashes mid-write (key persisted, response not) | Next retry sees key exists · returns stale 'in-progress' status · client retries · eventually consistent · documented protocol |
| TTL expired between client retries | Treated as new request · acceptable since client gave up >24h ago · documented per-endpoint TTL policy |
| Cross-region key visibility delay | Read-after-write consistency Postgres primary · Redis cross-region replication ≤1s · acceptable for non-financial · stricter primary-only for billing |
| Malicious client reuse key for different op | Detected via body hash mismatch (if stored) · reject 422 · same as collision · investigate |
Per `CLAUDE.md` línea 11 · "Every external action must be idempotent" es non-negotiable arquitectónico. Apartments: NO excepciones para WhatsApp send · Stripe charge · Cal.com booking · email send · push notification. Si código no idempotente · NO se mergea.
Esta arquitectónica decision desde Sprint Hardening Día 5 (ADR-038) · validated por 0 production incidents 79 PRs merged · idempotency saves multiple retry storms automatically. Architecture decision paid off.
¿Tu engineering team necesita idempotency code samples?
Para Enterprise procurement · code samples 4 idiomas (Node · Python · Go · PHP) · ORM-specific patterns Postgres + Redis · test suite reference disponibles bajo NDA.