varnishはQUERY_STRINGも含めてキャッシュする

今動いているサービスが

(INTERNET)-[varnish]-[Starman/Catalyst]

というような構成でvarnishでCSSや画像なんかの静的ファイルをキャッシュしているんだけど、 現状デプロイしたけどキャッシュが更新されなくて見えてる画像が新しくなってないよって現象がある。 一応varnishを再起動すればキャッシュはクリアされるけど、 一つのvarnishの下に複数のサービスがぶら下がっているので、 一つのサービスのデプロイのためにそれ以外の全てのサービスのキャッシュをクリアするのも大変微妙。

で、そういえば、先日Railsのimage_tagを真似て作ってみたMyApp::View::Plugin::Tsを使って画像のURLなどに更新時刻を付加すれば、 varnishがそこをみて新しくキャッシュしてくれるようになるんじゃないか?と思った。

そのためにはvarnishがパスだけでなくてQUERY_STRINGも含めて (例えばfoo.jpgとfoo.jpg?123456を別物と見て)キャッシュしてくれている必要がある。

というわけで、今回はそれを確認してみた。

varnishのインストールと設定、起動

インストールはbrewで一発。8/10現在、3.0.0がインストールされた。

$ brew install varnish

今回の設定ファイルを作成:

$ vim /usr/local/Cellar/varnish/3.0.0/etc/varnish/cache_test.vcl
backend cache_test {
  .host = "127.0.0.1";
  .port = "3000";
}

# リクエストを受け付けると必ず走る処理
sub vcl_recv {
  if (req.request != "GET" && req.request != "HEAD") {
    return(pipe); # このトランザクションではこれ以降キャッシュを利用せずにAppサーバに繋ぎに行く
  }
  if (req.http.Cache-Control ~ "no-cache") {
    return(pass); # キャッシュを利用せずにAppサーバに繋ぎに行く
  }
  return(lookup); # キャッシュの中から利用できるものがあれば利用する
}

# Appサーバに繋ぎに行くときに走る処理
sub vcl_fetch {
  if (beresp.status == 500) {
    set beresp.saintmode = 10s;
    return(restart);
  }
  set beresp.grace = 5m;
  if (req.url ~ "\.(png|gif|jpg|css|js|ico)$") {
    unset beresp.http.set-cookie;
    set beresp.ttl = 3600s;
  }
}

上記の設定ファイルを読みこむよう設定に追記:

# 設定ファイルの実体は /usr/local/Cellar/varnish/3.0.0/etc/varnish/default.vcl
$ vim /usr/local/etc/varnish/default.vcl
include '/usr/local/Cellar/varnish/3.0.0/etc/varnish/cache_test.vcl';

varnishを起動する。

$ varnishd -a 127.0.0.1:8000 -f /usr/local/Cellar/varnish/3.0.0/etc/varnish/cache_test.vcl

これで http://127.0.0.1:8000 にアクセスするとvarnishを経由して http://127.0.0.1:3000 にアクセスするようになった。

実験

適当なファイルを配信するサーバを立てる。 ここで使ってるwebrickコマンドは、適当に書いたwebrickサーバを立てるスクリプト。 HTTPサーバであればなんでもいい。

$ mkdir -p ~/dev/sample/varnish
$ cd ~/dev/sample/varnish
$ cp ~/Pictures/foo.jpg .
$ webrick -p 3000

そして、このwebrickサーバで配信している foo.jpg にアクセスしたときにvarnishがキャッシュするのか、 つまりwebrickサーバにアクセスが行くのかどうかを確認してみた。

# webrick 直でアクセスするとログは流れる
$ open http://localhost:3000/foo.jpg

# varnish 経由でアクセス
# 画像はキャッシュされていないのでwebrickのログが流れる
$ open http://localhost:8000/foo.jpg

# varnish 経由でアクセス、2回目
# 画像はキャッシュされているのでwebrickのログは流れない
$ open http://localhost:8000/foo.jpg

# varnish 経由でアクセス、QUERY STRING付き
# 先ほど/foo.jpgはキャッシュされたけど、/foo.jpg?123456はキャッシュされていないのでwebrickのログは流れる
$ open http://localhost:8000/foo.jpg?123456

# varnish 経由でアクセス、QUERY STRING付き、2回目
# 直前のアクセスで/foo.jpg?123456はキャッシュされたのでwebrickのログは流れない
$ open http://localhost:8000/foo.jpg?123456

まとめ

varnishはQUERY_STRING付きでキャッシュしてくれているので、 画像やCSSなどの静的ファイルは更新日時などを付加してリンクすれば適宜キャッシュしなおされる。 端末側も新しくキャッシュしてくれる(はずな)ので常に最新のファイルを読むようになる。

でも、-M演算子やFile.mtimeのコストってどうなんだろう? Appサーバにリクエストが行かなくなるだけマシになるのかな?

参考資料