Varnish の hit_for_pass ってなに?

結論

「キャッシュしない」ことをキャッシュするのが hit for pass キャッシュオブジェクト。

同じ URL に、同時にアクセスされたとき、

  • キャッシュが見つからない場合 …… 先頭のリクエストだけをバックエンドに送り、残りはそのレスポンスを待つ。
  • hit for pass キャッシュが見つかった場合 …… 他のリクエストを気にせず、全部バックエンドに送る。

という動作になる。

発端

Varnish を3.0にバージョンアップしたら vcl_fetch のreturn (pass); がエラーになった。どうやら、単純に return (hit_for_pass); に置き換えればいいらしい。

The difference in behaviour of pass in vcl_recv and vcl_fetch confused people, so to make it clearer that they are different, you must now do return(hit_for_pass) when doing a pass in vcl_fetch.

Upgrading from Varnish 2.1 to 3.0 — Varnish version trunk documentation

vcl_recv の pass と、vcl_fetch の pass が紛らわしいから、hit_for_pass に変えたよ、と書いてある。

hit for pass 機能の詳しい説明は、以下にあった。

同時アクセスで、キャッシュが存在しない場合、最初のレスポンスを待つ??

これを読むまで、キャッシュが無ければ単純に全てバックエンドに送られるものだと思っていた。言い換えると、人気の高いページのキャッシュが消えた場合、バックエンドにアクセスが集中するものと思っていた。

どうやら、そうではないらしい。

実験

hit_for_pass の挙動は、実験で簡単に確認できた。

default.vcl。実験用の Varnish 設定。全リクエストのキャッシュを参照し、レスポンスは全てキャッシュしない(= hit for pass キャッシュを作成する)。

backend apache {
  .host = "127.0.0.1";
  .port = "80";
}

sub vcl_recv {
  set req.backend = apache;
  return (lookup);
}

sub vcl_fetch {
  # 3.0用
  return (hit_for_pass);

  # 2.1用
  # return (pass);
}

test.php。バックエンドがリクエストを受けたタイミングを確認するためのPHP。ログを書いてスリープして、ログを書く。

<?php

error_log("TEST 1");
sleep(5);
error_log("TEST 2");

?>OK

で、これを連続して3回リクエストする。

$ curl http://localhost:8080/test.php &
$ curl http://localhost:8080/test.php &
$ curl http://localhost:8080/test.php &

すると、出力されるログは、

[Sun Nov 27 13:06:47 2011] [error] [client 127.0.0.1] TEST 1 ←先頭のリクエスト
[Sun Nov 27 13:06:52 2011] [error] [client 127.0.0.1] TEST 2
[Sun Nov 27 13:06:52 2011] [error] [client 127.0.0.1] TEST 1 ←2番目のリクエスト
[Sun Nov 27 13:06:52 2011] [error] [client 127.0.0.1] TEST 1 ←3番目のリクエスト
[Sun Nov 27 13:06:57 2011] [error] [client 127.0.0.1] TEST 2
[Sun Nov 27 13:06:57 2011] [error] [client 127.0.0.1] TEST 2

で、先頭のリクエストを待っていることがわかる。もう一度、同じURLに3回リクエストすると、今度は、

[Sun Nov 27 13:07:55 2011] [error] [client 127.0.0.1] TEST 1
[Sun Nov 27 13:07:55 2011] [error] [client 127.0.0.1] TEST 1
[Sun Nov 27 13:07:56 2011] [error] [client 127.0.0.1] TEST 1
[Sun Nov 27 13:08:00 2011] [error] [client 127.0.0.1] TEST 2
[Sun Nov 27 13:08:00 2011] [error] [client 127.0.0.1] TEST 2
[Sun Nov 27 13:08:01 2011] [error] [client 127.0.0.1] TEST 2

と、待たずにバックエンドに送られる。hit for pass キャッシュを見ていることがわかる。

Varnish 2.1.3 と 3.0.2 で確認した。


参考まで、Varnish の起動・終了に使ったスクリプト。

start.sh

#! /bin/sh

DIR=/Users/koseki/tmp/varnish
VARNISHD=/usr/local/varnish/sbin/varnishd

$VARNISHD -a :8080 \
          -T localhost:6082 \
          -P $DIR/varnishd.pid \
          -n $DIR/work \
          -f $DIR/default.vcl \
          -S $DIR/secret \
          -s file,$DIR/varnish_storage.bin,10M

stop.sh

#! /bin/sh

DIR=`dirname $0`
cat $DIR/varnishd.pid | xargs kill
rm $DIR/varnishd.pid