« 負荷に応じてキャッシュを自動調節する Perl モジュール | メイン | FizzBuzz - Perl 使って50バイト »

2007年05月10日

Cache::Adaptive の使い方

 昨日のエントリが好評のようだったので、いろいろ問題を修正したバージョンを CPAN にアップロードしました (Cache-Adaptive-0.02 - search.cpan.org) 。ついでに、開発中のウェブサービスに仕込んで、トップページでベンチマークを取ってみました。こんな感じです。

cache-adaptive.gif

 キャッシュが効いていない状態で、0.1 秒強かかっているページについて、同時アクセスを増やしていっても問題ないのがおわかりいただけると思います。同時接続数が増えるにつれキャッシュヒットする率が上がり、平均レスポンスタイムが小さくなるのはご愛嬌。ちなみに、使った設定は以下のようなものでした。ログ出力のコードも入っていて長めですが、調整の参考になれば。アクセス数がそれほど多くないサイトで長期間のキャッシュを行いたいような場合もパラメータを変更すれば対応できると思います。
 ちなみに、この例の戦略は、URI の負荷に比例し、ロードアベレージの2乗に比例するようにキャッシュ時間を設定するというものです。だから、アクセス数がさらに増えても問題ない... と思います。有効性が確信できたら、もっと使いやすいようにサブクラス化してもいいかもしれませんね。

    $self->{cache} = Cache::Adaptive->new({
        backend         => Cache::FileCache->new({
            namespace => 'html_cache',
            max_size  => 10 * 1024 * 1024,
        }),
        expires_initial => 0.6,
        expires_min     => 0.3,
        increase_factor => 1.25,
        decrease_factor => 0.8,
        expires_max     => 20,
        purge_after     => 30,
        check_interval  => 10,
        check_load      => sub {
            my ($entry, $params) = @_;
            my $la = sysctl('vm.loadavg');
            my $score =
                $params->{load} * 8
                    * ($la->[0] <= 1 ? $la->[0] : $la->[0] ** 2);
            my $now = asctime(localtime(int($entry->{build_at})));
            chomp $now;
            printf(
                STDERR "Cache-Adaptive [$now] Load: %f %f %f\n",
                $params->{load},
                $la->[0],
                $score);
            int($score) - 1;
        },
        log             => sub {
            my $params = shift;
            return unless $params->{type} eq 'miss';
            my $now = asctime(localtime(int($params->{at})));
            chomp $now;
            printf(
                STDERR "Cache-Adaptive [$now] Miss: %lf %lf %s\n",
                $params->{entry}->{expires_in},
                $params->{entry}->{expires_at},
                $params->{key});
        },
    });

以下、余談:
 Cache::Adaptive の開発は、最初の公開段階まではさくさくいったのですが、その後、はまりました。0.02 で複数のプロセスがキャッシュ更新を同時に行わないような排他処理 (ただし、ブロックしない) を入れたのですが、これがなぜか、稀にデッドロックをおこしてしまって...

 同僚の竹迫さんにも見てもらったりして、結局、その時まで使っていたバックエンド Cache::SizeAwareFileCache の問題?だということがわかりました。Cache::SizeAwareFileCache はキャッシュエントリの更新時だけでなく取得時にもキャッシュファイルを更新するようになっており (換言すると set が成功するかどうかは optimistic)、この結果、更新タイミングによっては保存した情報が、他プロセスの getter により上書きされてしまっていたのです orz

 これは想定外! というわけで、Cache::Adaptive のバックエンドとして Cache::SizeAwareFileCache を指定しないでください。Cache::Memcached とかどうなんだろ...

投稿者 kazuho : 2007年05月10日 17:26 このエントリーを含むはてなブックマーク このエントリーを含むはてなブックマーク

トラックバック

このエントリーのトラックバックURL:
http://labs.cybozu.co.jp/cgi-bin/mt-admin/mt-tbp.cgi/1261