やや大袈裟な名前ですが「memcachedにおけるキャッシュシステムの Thundering Herd 問題への対策案」とか「キャッシュシステムの Thundering Herd 問題への対策案。その2 排他制御」で書いていたコードをモジュールにした
github: https://github.com/kazeburo/Cache-Isolator
機能としては、平行動作数を制御できるget_or_setと、一定の確率で少し早く有効期限が切れるキャッシュの保存、取得、削除あたりがあげられます
使い方ですが、まず、get_or_setの例。
my $isolator = Cache::Isolator->new(
cache => Cache::Memcached::Fast->new(...),
concurrency => 4, # get_or_setのcallbackの最大平行動作数。デフォルト1
interval => 0.01, #lockを確認するinterval
timeout => 10, #lockする最長時間
trial => 0, #lockを試行する最大回数
);
my $key = 'query:XXXXXX';
# get_or_set( key, callback, expires)
$isolator->get_or_set(
$key,
sub {
get_from_db($key);
},
3600
);
次に早期有効期限の例。get/set/deleteは普通とかわらずに使えます。
my $isolator = Cache::Isolator->new(
cache => Cache::Memcached::Fast->new(...),
early_expires_ratio => 10, #1/10の確率で早くexpires
expires_before => 10, # expiresする秒数
);
$isolator->get($key);
$isolator->set($key, $value, $expires);
$isolator->delete($key);
early_expiresでは、set時に2つキャッシュを作成し、片方のexpiresをexpires_beforeで指定した秒数早くして保存します。getするときは、early_expires_raitoの確率で早くexpiresするキャッシュを引き、通常のキャッシュよりも早い期限切れを表現しています。
この2つの機能は同時に使うことができるので、early_expiresをしつつ、get_or_setの平行動作数をしぼったりもできます。うまく組み合わせるとThundering Herd問題を回避できると思われます。
このモジュールではないけど、既に某blogサービスの一部の重いクエリに対してこの仕組みが導入されていたりします。かなりDBの負荷削減に効果ありました