HTTPステータスコード499のとき、GETリクエストが即座に再送される?
nginx 0.8.x をリバースプロキシにして、unicorn + rails の構成。
なんか、全く同じパラメータのGETリクエストが2回飛んで来ることがある。
それで、一回しか処理しないつもりのものが二回処理されて地獄発動。
アクセスログをみてみると、同じ時間に全く同じパラメータのGETリクエストが2個ついている。
GETパラメータにはキャッシュ回避のためのランダム値をリクエスト時に必ず更新して入れるようにしてるので、その値が全く同一であることから、クライアント側でわざわざリトライ処理をしていないことは明白。
で、ログをよく見てみると、HTTPのステータスコードが、片方は499、もう片方は200となってることがわかった。
んで、検索したところ、↓のような情報が見つかった。
Why does Nginx return 499 errors? http://technosophos.com/node/187
どうも、クライアント側がレスポンス受け取り切らない内に接続が切れたらつくらしい。
想像するに、ブラウザ側の都合でリクエストを強制切断して、ブラウザが気を利かせてもう一回リクエストを再送した模様。
今回の事例、ブラウザは Opera だったんだけど、Operaの癖なのか、ブラウザ全般の挙動なのかはよくわかってない。
まあ、重複処理防止策を講じろよ、ということですよね。
なにか、手軽な方法があればいいんですが、セッション的なことやってないっぽいんですよねぇ。。。
■2012/08/10追記
記事をよく読んだら、nginxのHTTPコード499は、クライアントには送信してないですね。
クライアント側はHTTPコード200として受け取っているみたい。
レスポンス中にクライアント側から切断されたときに、あくまでもnginxがログに記録するだけみたい。
■2013/07/01追記
結構HTTPステータスコード499で戸惑ってる人居るみたいですね。
どういう仕様なのか具体的に特定する作業はしてませんが、事実として同じリクエストが重複して行われることは確実なので、結局重複実行防止の対策をするしか解決方法はないと思います。
動作の特徴として、全く同じパラメータが送信されてくることが分かっているので、リクエスト毎に変わる使い捨てのトークンをパラメータに付加して、同じトークンだったら処理しないという実装でしのぎました。
■2013/08/23追記
もともとHTTPのGETメソッドは「データ取得」を目的として定義されてるものなので、GETメソッドをリクエストしたことでサーバー側に何か変化が発生するようなことは、使い方として想定してない模様。
二重に処理して困るような、サーバー側に変化をさせるようなリクエストはPOSTメソッドを使おうぜ、というのがHTTPの作法としては正しいのかもしれません。
POSTメソッドだとHTTPステータス499が出ないのかどうかは、試してないので不明ですが、ステータス499のときはまったく同じパラメータでリクエストが再送されてきていることから、再送側は再送しても副作用がないことを想定しているっぽいですね。