Skip to main content

第02章:部:HTTPとAPI設計の冪等性(6〜9章)🌐🧩

🎯この章のゴール

  • 「冪等性がないと何が壊れるのか」を、タイムアウトと再送の流れで説明できるようになる🙂📌
  • 「成功したのに返事が届かない」が、なぜ超やっかいかが腑に落ちる🙃💥

Concept


1) 現実:ネットワークは“だいたい不安定”📶💫

アプリって、常にこういうことが起きます👇

  • 電波が弱い / Wi-Fiが切り替わる📱📡
  • サーバーが混んでて返事が遅い🐢
  • 途中のネットワーク(プロキシ等)で切れる🧵✂️
  • プラットフォーム側の都合で接続が切られる⏱️💥(あとで詳しく)

そして人間はこうする👇 「あれ?反応ない…もう一回押そ」 🔁😅

ここで冪等性がないと、同じ処理が2回走る事故が起きます😱


2) “最悪”はこれ:成功したのに返事が届かない🙃📨

一番こわいのは、これ👇

  • サーバー側では 処理が成功している
  • でもクライアント(ブラウザ/アプリ)は レスポンスを受け取れなかった

この状態だと、クライアントから見るとこうです👇 「成功した?失敗した?わかんない…」 🤷‍♀️💭

例えば Cloud Run のドキュメントでも、タイムアウトすると接続は閉じられて 504 になる一方で、コンテナ(サーバー側)が処理を継続してしまう可能性がある、と明記されています。つまり「返事は切れたけど、裏では進んでた」が普通に起こりえます。(Google Cloud Documentation)

だからこそ、HTTPの仕様でも「通信障害でレスポンスが読めなかったとき、同じリクエストを再送すること」を強く意識していて、冪等なメソッドは自動再送しやすい、でも 非冪等は慎重にという話が出てきます。(RFCエディタ)


3) 「タイムアウト→再送→二重実行」事故の典型パターン📨📨💥

パターンA:ユーザー再送(連打)👆👆

1回目:ボタン押す 2回目:反応が遅くて、もう一回押す → 同じ注文が2つ作られる二重決済ポイント二重付与…😱💳

パターンB:アプリ側のリトライ(自前実装)🔁🧠

ブラウザの fetch() は、ネットワークエラー等でしか reject しない(例:HTTP 504 でも reject しない)ので、アプリ側で response.ok を見て「再試行しよ!」って書きがちです。(MDN Web Docs) この「再試行コード」が、冪等性を前提にしてないと事故ります😵‍💫

パターンC:プラットフォーム/ライブラリ側のリトライ(勝手に起きる)🤖🔁

  • Google Cloud Storage のように、クライアントライブラリが自動リトライすることがあります(エラーや接続不安定への対策)(Google Cloud)
  • Firebase のバックグラウンド処理は、設定次第で失敗時にリトライが続く(最悪、長期間)ので、重複実行に耐える設計が必須です。(Firebase)

つまり結論👇 リトライは「書いた覚えがなくても」起きる 😇🌧️


4) 事故のイメージ図:二重注文が生まれるまで🖼️💥

たとえば「注文作成(POST /orders)」が冪等じゃない世界で…👇

t=0s   クライアント:POST /orders 送信 📨
t=1s サーバー:DBに注文レコード作成 ✅(注文#123)
t=2s ネットワーク:返事が遅くてクライアント側タイムアウト ⏱️💥
t=3s クライアント:失敗だと思って再送 POST /orders 📨
t=4s サーバー:またDBに注文レコード作成 ✅(注文#124)
t=5s ユーザー:注文が2個ある…😱

ポイントはここ👇

  • サーバーは悪くない(普通に処理しただけ)
  • クライアントも悪くない(失敗に見えたから再送しただけ)
  • 悪いのは「再送が起きる世界で、非冪等な操作をそのままにした」こと⚠️

5) 「じゃあ、どうするの?」の方向性(超ざっくり)🧭✨

この章では“理由”が主役なので、解決は軽く触れるだけにします🙂

  • 冪等にしやすい操作(PUT/DELETE)を選ぶ HTTP では PUT/DELETE(と safe なメソッド)は冪等として定義されています。(RFCエディタ)
  • POST のような“増える操作”には「冪等キー」などの仕組みを足す🔑 IETF の Internet-Draft でも Idempotency-Key ヘッダーで、POST/PATCH を**フォールトトレラント(再送に強く)**する狙いが説明されています。([datatracker.ietf.org][6]) Stripe でも、接続エラー時に安全に再送するために冪等キーを使うよう案内されています。(Stripe ドキュメント) AWS も「分散システムでは exactly once が難しいので、トークンで重複を吸収しよう」という考えをベストプラクティスとして書いています。(AWS ドキュメント)

📝演習:タイムアウト時の“最悪シナリオ”を図にしよう🖊️🖼️

次の3つを、矢印でつないだ図にしてみてね(絵が雑でもOK!)😊

  1. クライアント(ブラウザ/アプリ)📱💻
  2. サーバー(API)🖥️
  3. DB(保存)🗄️

そして、図の中にこのイベントを書き込む👇

  • 「送信」📨
  • 「DB保存」✅
  • 「タイムアウト」⏱️
  • 「再送」🔁
  • 「二重作成」💥

できたら最後に一言👇 「どの瞬間から “成功か失敗か不明” になる?」 🤔


🤖AI活用:事故パターンを“図解テキスト”にしてもらう🧩✨

AI にこう頼むと、理解が速いよ〜🙂

  • 注文作成APIがタイムアウトして再送されたときに起こる事故を、t=0〜のタイムラインで図解して」⏱️
  • 成功したのに返事が届かないケースを、クライアント視点/サーバー視点で分けて説明して」👀
  • 「同じ処理が2回走ると困る例を、決済以外で5つ挙げて」🧾🎫📦📩

出てきた答えに対しては最後にこれだけチェック✅

  • 何が二重になる?
  • どの条件で再送される?

✅この章のまとめ(ここだけ覚えればOK)🌸

  • リトライは「必ず起きるもの」😇🔁
  • 一番危ないのは “成功したのに返事が届かない” 🙃📨
  • だから、変更系の操作(注文/決済/在庫など)は 再送に耐える設計が必要⚠️
  • 次の章以降で、用語整理 → HTTP設計 → 冪等キー、の順に固めていくよ🔑✨

[6]: https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-idempotency-key-header-03 "

            draft-ietf-httpapi-idempotency-key-header-03

"