分散Webサーバ環境での分割配送時の412エラーの原因:エンティティのタイムスタンプ不一致

タイトル案が浮かばずに日本語になってませんでしたが、Hotさんにいいネタを頂いたので使わせていただきました。少しいじっちゃいましたが(そして改悪の可能性が)
ちなみに旧タイトル(笑):負荷分散した複数台Webサーバ環境でRangeヘッダによる分割配送時に412エラーが出る場合(日本語でおk

以下のような状況を前提として、"412エラー"が出るという状態に遭遇し、ちょっとハマったのでメモです。

  • ロードバランサなどで負荷分散するシステムを構築している
  • Webサーバ(Apache httpdとか)を運用している
  • それぞれのサーバでホストしているファイルやサービスは同一である

で、さらに以下の状況だと「起こりやすい」のではないかと思われます。

  • DoCoMo向けにiアプリを提供している。
  • jarファイルのサイズが100KB以上である。(これは必須条件ではないような気もするが)

上記条件を満たすと、DoCoMoゲートウェイサーバによって、Rangeヘッダを使って複数回のリクエストに分割されることまでは確認したつもりです。

まず、"412エラー"ですが、If-Matchとか If-Unmodified-Sinceとか、そういったIf系のヘッダの条件にマッチしなかった場合に返却されるエラーです。タイムスタンプであるとか、ETagを条件としてIf-系のリクエストヘッダは作られているのですが、ここでのポイントは「タイムスタンプ」です。

「同一のファイルやサービスを複数台のWebサーバで提供している」ということから、バイナリとしての内容は同一(=md5sumは同一の結果)となります。ところが、タイムスタンプは、ファイルの転送・デプロイ手法によって必ずしもサーバ間で同じものになっているとは限らないのです。この状況下で、

  • ファイルの冒頭部分を取得するリクエストが到着する → Aサーバに分配される
  • Aサーバは、Aサーバのファイルの情報を元にして Last-Modified や ETag を返却する
  • ファイルの次の部分を取得するリクエストが到着する → Bサーバに分配される
    • このとき、If-系のヘッダは Aサーバからもらった情報を元に構築されている
  • Bサーバは、If-の条件と自分の持っているファイルを照らし合わせるが、タイムスタンプが一致しないので 412エラーとなる

ということになります。ロードバランサが(偶然であれ)同じサーバに割り振っているとこの問題は起きないので、再現性もまちまちです。

対策としては デプロイ手法や、運用ルールなどでタイムスタンプを全サーバ間で一致させるようにする ということになりそうです。