テック技報最新号へ →
コード

冪等キーは「処理結果」と一緒に、同じトランザクションで保存する

冪等キー(Idempotency-Key)を使うなら、キーと返したレスポンスを、副作用(注文の作成や残高の更新)と同じトランザクションで書く。別々に書くと、その間でプロセスが落ちたときにリトライが二重実行になる。

@amitsinha__07 · 投稿 2026-11-04
#idempotency#postgres#transaction#api-design#infra

何が起きるか

よくある失敗は「処理する → 成功したらキーを保存」の二段階にしているケース。課金が成功した直後、キーを保存する前にプロセスが落ちると、クライアントのリトライは「未処理のキー」として扱われ、もう一度課金される。逆に「キーを先に保存 → 処理」にすると、処理の途中で落ちたときにキーだけが残り、リトライしても何も起きない(処理されないまま成功扱い)。

やり方

sql
CREATE TABLE idempotency_keys (
  key           text PRIMARY KEY,
  request_hash  text NOT NULL,      -- 同じキーで別の内容が来たら拒否するため
  status_code   int,
  response_body jsonb,
  created_at    timestamptz NOT NULL DEFAULT now()
);

1 リクエストの流れ(すべて 1 つのトランザクション):

sql
BEGIN;
-- 1. キーを確保。既にあれば 0 行になる
INSERT INTO idempotency_keys (key, request_hash)
VALUES ($1, $2)
ON CONFLICT (key) DO NOTHING;

-- 0 行だったら: 既存の行を読んで、保存済みのレスポンスをそのまま返す
--   (request_hash が違えば 422 など、response_body がまだ空なら処理中として 409)

-- 2. 副作用(同じ DB の中の書き込み)
INSERT INTO orders (...) VALUES (...);

-- 3. 返すレスポンスをキーの行に書く
UPDATE idempotency_keys
SET status_code = 201, response_body = $3
WHERE key = $1;
COMMIT;

途中で落ちればトランザクションごと消えるので、キーも注文も残らない。リトライは最初から正しくやり直される。

注意点

出典

X の元の投稿を開く