2014/10/04

Sub::Retry にタイムアウトありの Sub::Retry::Extended 書いた

Sub::Retry は呼び出しのインターフェースがクールでさくっと使いやすい。そんなわけでよく利用するのだけど、タイムアウトを仕込もうと思うと例えば以下のようなコードになる。

リトライする時に 5 秒超えてたらタイムアウト。

use Sub::Retry;
use Time::HiRes qw/gettimeofday tv_interval/;

my $t = [gettimeofday];
retry 5, 1, sub {
    print "$_[0]\n";
    sleep 2;
    die;
}, sub {
    my $res = shift;
    (defined $res || tv_interval($t) > 5) ? 0 : 1;
};

わりと簡単に見えるけど、実用上は全体的なタイムアウト以外に一回ずつのトライにもタイムアウト設定したいなどの要求がでると思われるのだが、そうするとさらにもうひとつカウンタが必要になって複雑なコードになってしまう。しかも、タイムアウトロジックは毎度毎度ほぼ同じなのでモジュールが隠ぺいしてくれた方が幸福度が高い。

そういうわけで、Sub::Retry を拡張するぞーっと思ったんだけど、既存インターフェースだと引数増やすのが憚られるというか、あんまりこれ以上増やしたくないなーという印象だったので、まるっとコードを頂きつつ Sub::Retry::Extended を書いてみた。

retryX という厨二病的な関数にハッシュでもろもろ渡します。

use Sub::Retry::Extended;
use Cache::Memcached::Fast;

my $cache = Cache::Memcached::Fast->new;

my $ret = retryX(
    code => sub {
        $cache->get('foo');
    },
    retry_if => sub {
        my $res = shift;
        defined $res ? 0 : 1;
    },
    times => 3,
    delay => 0.1,
    each_timeout  => 1.5,
    total_timeout => 2.5,
);

each_timeout が都度実行ごとのタイムアウトで、total_timeout がリトライ全体でのタイムアウト値になる。

いまんとこそれだけっす!!うぇーい!

サイト内検索