岩本隆史の日記帳(アーカイブ)

はてなダイアリーのサービス終了をうけて移行したものです。更新はしません。

ETagをどう生成するか

ETagとは何か

ETagはHTTPレスポンスヘッダのひとつで、RFC 2616の14.19(日本語訳)で規定されています。If-None-Matchリクエストヘッダを使った条件付きGETでの転送量軽減や、If-Matchを使った条件付きPUTでの競合検出などに使われる値です。

強いETag、弱いETag

ETag(エンティティタグ)には強弱があります。簡単にいえば、HTML内のアクセスカウンタが1上がっただけで変わるのが強いETag、変わらないのが弱いETagです。弱いETagは、リソースの意味が変わらなければ変わりません。

13.3.4では「HTTP/1.1 オリジンサーバにとってより望まれる動作とは強いエンティティタグと Last-Modified 値の両方を送る事である」とされています。なるべく強いETagを使いましょう、ということです。

Rails2.0の生成手法

http://blogs.ricollab.jp/webtech/2008/02/etag/」によれば、Rails2.0ではレスポンスボディのMD5をETagとしているようです。強いETagですね。単純な手法でありながら、HTTP1.1の望む挙動になっているので、開発者としては惹かれるところです。

Rails式の問題点

この手法に対し「ETagを使ってSpringとHibernateの転送量と負荷を削減する」では、2つの問題点が指摘されています。

  • サーバ上でページがレンダリングされた後、それがクライアントに送られる前に、ETagの値は計算される。もしETagの値がマッチすれば、レンダリングされたページはクライアントに送られないので、モデルのためにデータを引っぱってくる必要は実はない。
  • フッター部分に日付と時間を表示するといったことを行うページでは、内容が実際には変更されていなくても、毎回異なっているということになってしまう。

1点目について補足します。条件付きGETリクエストに応じてModelやViewの処理をおこない、せっかくレスポンスボディを作っても、ETagがIf-None-Matchの値と同じであれば、レスポンスボディを空にしなければなりません(「304 Not Modified」ではレスポンスボディを送ってはいけない)。処理が無駄になってしまうわけです。

なお2点目は、強いETagであれば当然の挙動なので、特に問題ないでしょう。

より洗練された手法

ETagを使ってSpringとHibernateの転送量と負荷を削減する」の「ETagインターセプタ」の項では、Modelに変更があればViewカウンタをインクリメントし、それをETagとして使う手法が紹介されています。ただし、強いETagであることを遵守しようとすると、ビューテンプレートなどの変更についてもトラックしなければならないのではないかと思います。

他の案として、Modelの更新処理がおこなわれた際に、影響のあるリソースのHTMLを生成してキャッシュし、そのETagを紐づけておく手法も考えられます。あるいはバッチ処理で充分かもしれません。ニコニコ動画では、トップページを10分おきにバッチ更新し、HTMLをmemcachedにキャッシュしているそうです(『WEB+DB PRESS Vol.42』、59ページ)。