Saltar al contenido principal
Idempotency · reliability foundation

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

Network failures retry
Cliente request crashed mid-flight · cliente retries · sin idempotency procesa 2 veces · doble charge / doble message / doble appointment
Webhook provider retries
Stripe/Meta/Cal.com retry webhooks por hours si HTTP 5xx · sin idempotency procesa N veces · N side-effects undesired
Queue replay (DLQ retry)
Job falls a DLQ · founder retries · sin idempotency reprocesa side-effects ya aplicados · double-write disasters
Client app bug duplicate submit
Cliente UI double-click button · sin debounce + sin idempotency = duplicate transaction · clásico race condition
Resilience scenarios disaster recovery
Restore desde backup pre-incident · application replays events post-restore · sin idempotency duplicates ya procesados pre-incident
Distributed systems exactly-once illusion
Exactly-once delivery NO existe distributed systems · solo at-least-once + idempotency = effectively-once · Mathematical certainty

6 implementation patterns

Postgres UNIQUE constraint
Tabla `processed_events` con columna `idempotency_key` UNIQUE · INSERT con ON CONFLICT DO NOTHING · second attempt rejected silently · cleanup vía TTL
Redis SETNX with TTL
SETNX `idem:<key>` value 1 EX 86400 · returns 0 si ya existe · perfect para hot-path cache · DB fallback secondary
Idempotency key in URL path
POST /api/messages/idem-<uuid> · routing layer cache · 24h TTL · semantic 'this URL = this operation'
Hash request body deduplication
Para webhooks sin native idempotency key (older providers) · SHA-256 body + timestamp window · stored 1h · prevents replay
Workflow state machine
QStash Workflows · stateful processing · each step idempotent automatically · restart resumes from last committed state · framework-level guarantee
Outbox pattern (event sourcing)
Para writes con side-effects external · evento durable DB FIRST · async dispatch enqueue · retry-safe via idempotency key · transactional consistency

Key lifecycle · 8 steps

  1. GENERATION: cliente genera UUID v4 OR provider assigns (Stripe event.id) OR derive from semantic (message_id Meta) · NEVER server-assigned
  2. TRANSMISSION: idempotency key transmitted Header `Idempotency-Key: <uuid>` OR query param OR body field · documented per-endpoint
  3. VALIDATION: server checks key store (Postgres unique OR Redis SETNX) · si exists return CACHED response (status + body) · si new proceed
  4. PROCESSING: si new key · process request · ATOMICALLY persist key + response · DB transaction guarantees consistency
  5. RESPONSE: return result (NEW process success · OR CACHED response replay si idempotency hit) · client recibe identical response either way
  6. TTL EXPIRY: keys expire after 24h-7d retention (configurable) · garbage collection background job · prevents unbounded growth
  7. AUDIT: idempotency hits logged (info level) · pattern analysis · if hits spike investigate (potential bug client side · attack)
  8. 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

ModeAction
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 retriesTreated as new request · acceptable since client gave up >24h ago · documented per-endpoint TTL policy
Cross-region key visibility delayRead-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 opDetected via body hash mismatch (if stored) · reject 422 · same as collision · investigate
CLAUDE.md non-negotiable · idempotency mandatory

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.