Idempotency & Retry-Safe Systems · Idempotency Keys in Practice

Storing Keys: Database vs Redis

Where to keep keys and responses, and how to make storage atomic with the work.

Create a free account to save this page, take notes and track your progress. Sign in

The strongest design stores the key in the same database transaction as the business change: either the request commits with its key, or neither does. Redis is fast and has TTLs but isn't transactional with your database — use it for short locks or as a cache in front of the database record. Store: key, scope, request fingerprint, status (processing/completed), response status/body/headers, created_at, and locked_until for recovery.

Key storage options

Criterion Same database (transactional) Redis Hybrid
Atomic with business write Yes No Yes (DB is source of truth)
Latency DB round trip Sub-millisecond Fast path via Redis
Expiry Cleanup job Native TTL Both
Failure modes DB contention on hot keys Lost keys on failover; split brain More moving parts
Best for Payments, orders Low-stakes dedupe, locks High-traffic critical APIs
CREATE TABLE idempotency_keys (
  scope            text        NOT NULL,          -- account / api key
  key              text        NOT NULL,
  fingerprint      text        NOT NULL,          -- hash of method + path + body
  status           text        NOT NULL,          -- processing | completed
  response_status  int,
  response_body    jsonb,
  locked_until     timestamptz,
  created_at       timestamptz NOT NULL DEFAULT now(),
  PRIMARY KEY (scope, key)
);
CREATE INDEX ON idempotency_keys (created_at);       -- for expiry cleanup

Common pitfalls

  • Key stored in Redis, order in Postgres: crash between them breaks guarantees.
  • Storing full responses forever (privacy + storage).
  • Hot-row contention when one key is hammered by retries.

Key points

  • Prefer the same transaction as the work
  • Redis for locks/caching, not the source of truth
  • Store fingerprint, status and response

Further reading: Brandur: Implementing Stripe-like idempotency keys in Postgres

Practice questions

What goes wrong if the idempotency key is stored in Redis but the order in Postgres?

Answer

A crash or failover between the two writes can leave an order without a key (retry creates a duplicate) or a key without an order (retry returns a response for something that never happened).

Why store the response body with the key?

Answer

So retries can receive exactly the original result (same IDs) without re-executing the operation.

Open in the app to mark lessons complete, take notes and track your progress.

Was this page helpful?