なぜ「同じボタン」を2回押すと大変なことになるのか?——HTTPの「POST」と「冪等性(べきとうせい)」の秘密
こんにちは!インフラエンジニアとして、日々パケットの「渋滞」や「迷子」と向き合っている筆者です。
皆さんはWebサイトを見ていて、「送信ボタンを連打しないでください」という警告を見かけたことはありませんか?あるいは、うっかり二重クリックしてしまい、注文が2件飛んでしまった経験がある方もいるかもしれません。
実はこれ、Web通信の根幹を支えるHTTPというルール(プロトコル)において、POSTメソッドが持つ「非冪等(ひべきとう)性」という性質が深く関わっています。
今回は、ネットワークの難解な定義を一旦横に置いて、私たちの身近な「郵便システム」に例えながら、なぜPOSTは「危なっかしい」のか、そしてどう制御すればいいのかを紐解いていきましょう。
—
1. そもそも「HTTPメソッド」って何?
Webブラウザ(皆さんのスマホやPC)が、Webサーバー(情報の保管場所)と会話するための「挨拶の仕方」だと思ってください。
- GET(ゲット): 「この情報を見せて!」(例:Webページを開く)
- POST(ポスト): 「この情報を送るから、登録・処理しておいて!」(例:お問い合わせ送信、注文確定)
ここで重要なのが、「GETは何度やっても安全だが、POSTはやりすぎると事故る」というルールです。
郵便で例えると…
- GET: 「カタログを見せてください」と何度頼んでも、届くカタログは同じですよね。これが「冪等(べきとう)」な状態です。
- POST: 「この手紙を届けてください」と10回頼んだら、10通の手紙が届いてしまいます。これが「非冪等(ひべきとう)」な状態です。
サーバー側からすれば、「同じ手紙が10通も届いたら、10回分お金を請求しちゃうよ!」ということが起きてしまうわけです。
—
2. なぜPOSTは「非冪等」なのか?
「冪等(べきとう)」という言葉、初見だと呪文みたいですよね。数学用語で、「何度計算しても結果が同じになる」という意味です。
HTTP/1.1の仕様では、POSTは「サーバー上の状態を変化させるもの」と定義されています。
サーバーは「あ、新しい注文が来た!」「新しいユーザーが登録された!」と、内部のデータベースを書き換えます。
この時、ネットワークの通信環境は完璧ではありません。たまにパケットが消えたり、サーバーの反応が遅れたりします。すると、ユーザーのブラウザは「あれ?返事がないな。もう一回送ろう!」と再送(リトライ)をしてしまいます。
サーバーは「あ、また新しい注文だ!」と勘違いして、2つ目の処理を始めてしまう。これが、Web開発における「二重投稿問題」の正体です。
—
3. どうやって防ぐ?——現場で使う「トークン」の魔法
では、どうやって「これは2回目の再送ですよ」とサーバーに教えればいいのでしょうか?
もっとも一般的な手法は、「注文番号(トークン)」をあらかじめ渡しておくことです。これを現場では「冪等性キー(Idempotency Key)」と呼んだりします。
具体的な流れ
1. 下準備: 注文画面を開くとき、サーバーが「今回限りの使い捨て番号(例:abc-123)」をブラウザに渡す。
2. 送信: ユーザーが「購入ボタン」を押す際、この「abc-123」を一緒にサーバーへ送る。
3. 照合: サーバーは「abc-123」を受け取ったら、データベースに「この番号で処理済みか?」を確認する。
4. 結果: もし2回目のリクエストが来ても、サーバーは「あ、この番号はもう処理したから無視しよう」と判断できる。
—
4. 実装のヒント(コード例)
Webアプリケーションでこの仕組みを作る際、サーバー側のコードは概念的にこのような動きをします。
疑似コード:サーバー側での二重投稿チェックのイメージ
def handle_order(request):
# クライアントから送られてきた一意なID(トークン)を取得
request_id = request.headers.get(“X-Idempotency-Key”)
# データベースにこのIDが既に存在するか確認
if db.exists(“processed_requests”, request_id):
# 既に処理済みなら、エラーではなく「成功した時の結果」をそのまま返す
return “注文は既に受け付けています(重複分は無視しました)”
# まだ処理していなければ、データベースに保存
db.save(“processed_requests”, request_id)
process_payment() # 決済処理など
return “注文が確定しました!”
—
まとめ:ネットワークは「信頼できない」前提で動く
インフラやネットワークの世界では、「通信は必ず失敗するし、必ず重複する」という前提で設計を考えるのがプロの流儀です。
- GETは「何度でもやり直してOK(参照)」
- POSTは「サーバーの状態を変える(更新・作成)」
この違いを意識するだけで、皆さんが作るアプリケーションやインフラ設計の強度が一段と上がります。
最初は難しく感じるかもしれませんが、まずは「手紙を10回送ったらどうなるかな?」と想像するだけで、ネットワークの挙動がぐっと身近に感じられるはずです。次回のブログでは、このHTTP通信がさらに高速化する「HTTP/2」の世界について深掘りしていきますね。
それでは、また次回の記事でお会いしましょう!
コメント