C.W.K.
Stream
Lesson 08 of 10 · published

Idempotency in Database Operations

~12 min · transactions, idempotency

Level 0Schema Seedling
0 XP0/86 lessons0/10 achievements
0/120 XP to next level120 XP to go0% complete

Network failure is the default

Any time your application calls the database over a network, the call can fail in three ways: before the database saw it, after it processed but before the response arrived, or successfully. From the application's perspective, "I got no response" is identical to "the request never arrived" or "the request succeeded but I never heard back." Your retries must be safe regardless.

Idempotent operations

An operation is idempotent if running it twice (or twenty times) has the same effect as running it once. SELECT is idempotent. SET status = 'paid' is idempotent. SET balance = balance - 100 is not. Many natural-looking writes are not idempotent.

Patterns that make writes idempotent

  • Use ON CONFLICT DO NOTHING or DO UPDATE for inserts.
  • Pass an idempotency key from the client; insert it into a table; reject duplicates.
  • Use absolute updates (SET balance = ?) when possible instead of deltas (SET balance = balance - ?).
  • Track applied operations in a side table so you can detect re-execution.

Code

Client-supplied idempotency key·sql
CREATE TABLE payments (
    id              UUID PRIMARY KEY,
    idempotency_key TEXT UNIQUE NOT NULL,
    amount          NUMERIC(10,2) NOT NULL,
    status          TEXT NOT NULL DEFAULT 'pending',
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- Same key twice → second insert silently skipped, no double-charge
INSERT INTO payments (id, idempotency_key, amount)
VALUES (gen_random_uuid(), :key, :amount)
ON CONFLICT (idempotency_key) DO NOTHING
RETURNING *;
Idempotent UPDATE via WHERE·sql
-- Only marks paid if not already paid; safe to retry
UPDATE invoices
SET status = 'paid', paid_at = now()
WHERE id = ? AND status <> 'paid';

External links

Exercise

Audit your project for non-idempotent writes (delta-style updates, plain INSERTs that could duplicate). For at least one, redesign it to be idempotent — either via ON CONFLICT, an idempotency key, or a status-guarded UPDATE.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.