Dev Study
← 分散システムの土台
レッスン 7 / 13·約 9 分·第12期 分散システムの土台

冪等は性質ではなく約束 — 規定と、実務が足した鍵

リクエストがアプリケーションに届くまで
  1. ブラウザURL入力
  2. DNS名前→IP
  3. ルータ経路選択
  4. ロードバランサ振り分け
  5. Nginx受け口
  6. アプリ処理今ここ
  7. DB保存

前回、同じ「再送」が層によって別の意味になることを見ました。今回はアプリ層でリトライを安全にする側の約束を見ます。

まず結論

RFC 9110 が定める「冪等」は、同じ要求を複数回送ってもサーバ上の意図された効果が1回ぶんと同じになる、というメソッドの性質です。応答まで同じという意味ではありません。

POST はこの性質を持ちません。それでも POST を安全にリトライしたい実務は、規定にない一意な鍵を後から持ち込み「これは前に処理した要求だ」と識別できるようにします。規定と実装慣行を分けて見ます。

? 読む前に予測

冪等なサーバに、本文が同じ POST を2回送りました。2回目は必ず「重複」として弾かれるでしょうか。

当てることが目的ではありません。外した予測ほど記憶に残るので、直感で選んでください。

抽象原理

規定が言っているのは「効果が同じ」だけ

前回、同じ「再送」でも層が変われば意味が変わることを見ました。ではアプリ層で「もう一度送ってよい」と言えるのはどういうときか。§9.2.2 の定義はこうです。

A request method is considered "idempotent" if the intended effect on the server of multiple identical requests with that method is the same as the effect for a single such request.

揃うことを求められるのは the intended effect on the server、サーバ上の意図された効果だけです。同じ節が但し書きを添えています。

Like the definition of safe, the idempotent property only applies to what has been requested by the user; a server is free to log each request separately, retain a revision control history, or implement other non-idempotent side effects for each idempotent request.

冪等な要求でも、サーバは要求ごとにログを書いてよく、履歴を残してよく、その他の非冪等な副作用を持ってよい。さらにリトライの根拠を述べた箇所には though the response might differ(応答は異なってよい)とあります。

つまり冪等とは 何もかもが同じになる性質ではなく、利用者が要求した効果の部分だけを揃えるという限定つきの約束です。

具体実物

どのメソッドが該当するのか

§9.2.2 の列挙はこうです。

Of the request methods defined by this specification, PUT, DELETE, and safe request methods are idempotent.

safe の一覧は §9.2.1 にあり、the GET, HEAD, OPTIONS, and TRACE methods are defined to be safe です。

RFC 9110 §9.2.1 / §9.2.2 / §18.2 からの整理表
メソッド safe idempotent 根拠
GET / HEAD / OPTIONS / TRACE ○ ○ §9.2.1 で safe と明示
PUT / DELETE × ○ §9.2.2 で明示
POST × × §18.2 Table 7 で no と明示

最後の行の根拠欄に注意してください。§9.2.2 の本文は冪等なメソッドを列挙する形で、POST を名指ししません。一方 §18.2 のメソッド登録表(Table 7)は POST の Idempotent 欄を no と明示しています。本文の列挙だけで「書いていない」と言うと、登録表を見落とします。

冪等かはリトライの規定に効きます。同節はこう定めています。

A client SHOULD NOT automatically retry a request with a non-idempotent method unless it has some means to know that the request semantics are actually idempotent, regardless of the method, or some means to detect that the original request was never applied.

自動リトライを禁じているのではなく 例外の条件を書いているのが要点です。この regardless of the method が実務側への蝶番になります。

✓ ここまでの確認

「RFC 9110 は POST が冪等でないと書いている」の評価はどれですか。

抽象原理

メソッドの性質で足りないなら、識別を足す

TCP の再送が安全なのは「もう一度送る」からではありません。RFC 9293 §3.4 は、全オクテットにシーケンス番号が振られ、確認応答は累積的だと述べたうえでこう書きます。

This mechanism allows for straightforward duplicate detection in the presence of retransmission.

同 §3.4.3(The TCP Quiet Time Concept)は、この重複検出が the unique binding of segment data to sequence space(データとシーケンス空間との一意な結び付き)に依存すると述べます。受信側が「前に見た番号だ」と言えるから送信側は再送できます。

POST に無いのはこの結び付きです。だから実務は規定の外側で鍵を持ち込み、regardless of the method が言う「メソッドによらず、この要求の意味は実際には冪等だと知る手段」を自前で用意します。

規定で足りるところと、実務が足すところ
もう一度送ってよいかリトライの判断
メソッドの性質で判断する(RFC 9110 §9.2.2)
PUT / DELETE / safe冪等。自動リトライの対象
POST登録表で Idempotent = no
例外条件 regardless of the method を自前で満たす
一意な鍵を付ける受信側が「前に処理した要求」と識別
2回目は処理せず記録を返す規定ではなく実装慣行
  • ・上2段が RFC 9110 の規定、下1段は実装慣行
  • ・RFC 9110 に冪等キーの記載は無い。標準ヘッダではない
具体実物

実測 — 止めるのは本文ではなく鍵

実装慣行の側を実際に動かします。以下は Docker 上(macOS Darwin 25.5.0 / Docker 28.4.0、コンテナは python:3.12-slim、Debian 13 / Python 3.12.14 / curl 8.14.1、2026-09-17T23:33 UTC、1試行)で、鍵を扱う自作サーバに同じ本文 item=book&qty=1 の POST を送った生出力です。

同じ鍵で3回 → 本文はそのままで鍵だけ変えて1回コマンド出力
--- 送信 1 回目 (Idempotency-Key: 8f3d1a2c-0000-4000-8000-abcdefabcdef) ---
Idempotent-Replayed: false
{"order_id": 4, "processed": true, "replayed": false}
--- 送信 2 回目 (同じ鍵) ---
Idempotent-Replayed: true
{"order_id": 4, "processed": true, "replayed": true}
--- 送信 3 回目 (同じ鍵) ---
Idempotent-Replayed: true
{"order_id": 4, "processed": true, "replayed": true}
 
=== 本文は同じだが鍵を変えて1回送る ===
Idempotent-Replayed: false
{"order_id": 5, "processed": true, "replayed": false}

サーバ側のログです(4行とも生出力、省略なし)。

処理したのか、記録を返したのかコマンド出力
[srv] PROCESSED order id=4 body=item=book&qty=1 key=8f3d1a2c-...
[srv] REPLAYED stored result for key=8f3d1a2c-... (NOT processed again)
[srv] REPLAYED stored result for key=8f3d1a2c-... (NOT processed again)
[srv] PROCESSED order id=5 body=item=book&qty=1 key=99999999-...

本文は4回とも完全に同一です。 それでも最後の1回は新しい注文 order_id: 5 として通りました。2回目・3回目を止めたのは本文ではなく 鍵の一致です。

「冪等とは何度やっても同じ結果になること」という素朴な理解では、この2つの違いを説明できません。

鍵なしで同じ POST を3回送った別の実測(同環境、1試行)では、本文が同じでも order_id が 1・2・3 と3件でき、order_count は 3 でした。HTTP は本文の同一性を検査しません。

✓ ここまでの確認

冪等なメソッドで同じ要求を2回処理し、サーバがアクセスログを2行書きました。冪等性の違反ですか。

⇄ 2つを見比べる
具体例 A

TCP のシーケンス番号(第23週)

全オクテットにシーケンス番号が振られており、RFC 9293 §3.4 はこの番号付けと累積的な確認応答の仕組みが再送時の素直な重複検出を可能にすると述べます。§3.4.3 は、重複検出がデータとシーケンス空間との一意な結び付きに依存すると明記します。

受信側が見ているもの表
届いたもの 受信側の判断 アプリに渡るか
新しい番号 未受信の範囲 渡る
再送(同じ番号) 前に見た範囲 落とす
具体例 B

HTTP POST に足した冪等キー

POST にはこの結び付きがありません。そこでクライアントが一意な鍵を生成して付けます。Stripe 公式は、最初の要求の結果を鍵ごとに保存し、同じ鍵の後続要求には同じ結果を返すと説明します。規定ではなく実装慣行である点が違います。

本教材の実測で観測されたもの表
届いたもの サーバの動作 台帳
新しい鍵 処理する 1件増える
同じ鍵 記録を返すだけ 増えない

トランスポート層の番号と、アプリ層の鍵。共通する構造は何でしょうか。

「2回目が届いたとき、受信側は何を手がかりに前回と同じだと言えるのか」を考えてみてください。

この章のまとめ

  • §9.2.2 の冪等の定義は「同一要求を複数回送ったときのサーバ上の意図された効果が1回ぶんと同じ」。応答は含まず(though the response might differ)、要求ごとのログなど非冪等な副作用は許される。
  • §9.2.2 が挙げるのは PUT・DELETE・safe なメソッド。POST はそこに含まれず、§18.2 の登録表(Table 7)は POST の Idempotent 欄を no と明示する。
  • 自動リトライの規定には regardless of the method という例外条件があり、実務の冪等キーはこれを自前で満たす手段。Idempotency-Key は RFC 9110 に記載が無く、標準ヘッダではない。
  • 実測では、本文が同一でも鍵が違えば別の要求として処理された。同一と見なす範囲を決めているのは識別のほう。
  • 鍵の仕組みは「やり直す」のではなく「1回だけ実行し、以後は保存した結果を返す」。ただし鍵の寿命・パラメータの一致・実行開始前の失敗という有効範囲がある。

自分の現場で確かめる

読んだだけでは身につきません。手元の環境で実際に見てみてください。

環境:

理解度チェック

読んだ直後は「分かった」と感じやすいものです。実際に解いて確かめてください。

問題 1 / 4正解: 0

RFC 9110 §9.2.2 が定める「冪等」の定義はどれですか。

用語集(2語)
冪等 (idempotent)
§9.2.2 の定義では、同一要求を複数回送ったときのサーバ上の意図された効果が1回ぶんと同じであること。応答が同じであることは含まない。
冪等キー (idempotency key)
再送を受信側が識別できるよう、クライアントが生成して付ける一意な文字列。RFC 9110 に記載はなく、Stripe などの実装慣行。