昨日から思い立ってData::Valveというものを書いてみました。XS混じりのスロットリング用モジュールです。

Data::Throttlerでも同じような事ができるわけですが、こっちは内部が非常にごつい。しかも拡張しにくい事この上ない。アクセッサーとか全くないんだぜ?

ってことでData::Throttlerとほぼ同等のAPIを持って、内部構成を完全に書き直しました。

Data::Valveは内部の基本スロットリングロジックはものすごく簡単なCで書いてます。単純にスロットリング判定が行われた時間を保存したリンクドリストを格納しておいて、その数と現在時刻からのインターバルを持って計算するだけです。

ただ、キモはこの計算を行うデータ構造体をシリアライズ・デシリアライズすることによって、外部媒体にこのデータを保存して、複数プロセスどころか複数ホスト間で共有できるようにしたところです。なので当然のごとくData::Valve::BucketStore::Memcachedというものを使ってMemcachedにデータを保存できるようになっています。
use Data::Valve; my $valve = Data::Valve->new( interval => 10, max_items => 5, bucket_store => { module => "Memcached", args => { memcached => { module => "libmemcached", args => { servers => [ '127.0.0.1:11211', '127.0.0.1:11212' ], namespace => "blah blah" } } } } );
これもData::Throttler::Memcachedで僕は一度やってますが、Data::Throttlerの子クラスとかだと内部データが異様に重いのでシリアライズするのが実は意外とボトルネックだったのと、ロッキングどうすればいいんじゃーと思って放っておいたりしたので、今回はその辺りもちゃんとやりました。

ロッキングにはKeyedMutexを使ってます。KeyedMutexはkeyedmutexdをどこかのサーバーに立ち上げておけば、そいつを使って複数クライアントがロックを共有できるという優れものです。CPANからKeyedMutexをインストールするとkeyedmutexdももれなく一緒についてきます。Data::Valveはデフォルトではローカルホストのkeyedmutexdに接続を試みますが、もちろん別ホストも指定できます。

Data::Valve->new( bucket_store => { module => "Memcached", args => { mutex => { sock => "hostname:port" }, .... );
ちなみに複数ホスト・複数プロセス間でスロットリングを共有する場合は、その設定をちゃんとシンクロさせるのは人間の責任です。

是非使ってみてください!