TheSchwartz の worker を Ctrl-C とか kill で止めた場合に、job の処理が半端な状態で終わられると困る、という話。以前 Deamon::Generic で TheSchwartz の worker をデーモン化する(2) - 酒日記 はてな支店 で諦めたんだけど、ちょっと必要に迫られたので考えてみたら一応できたっぽい。
package MyWorker; use strict; use warnings; use base qw( TheSchwartz::Worker ); sub sighandler { warn "caught signal @_\n"; no warnings 'redefine'; *TheSchwartz::work_once = sub { exit }; } sub work { my ($class, $job) = @_; $SIG{HUP} = $SIG{INT} = $SIG{TERM} = \&sighandler; $job->completed(); }
シグナルハンドラで TheSchwartz::work_once を exit するだけのサブルーチンで再定義してしまう。けっこう無理矢理だ。
なぜこれでうまく行くかというと、TheSchwartz::work は以下のようになっているので
sub work { my TheSchwartz $client = shift; my($delay) = @_; $delay ||= 5; while (1) { sleep $delay unless $client->work_once; } }
無限ループ内で延々 work_once が呼ばれる形 (処理すべき job がなければ work_once は偽を返すので、一定時間 sleep)。ということで、work_once を exit するように書き換えてやれば、そこでプロセス終了。
最初はシグナルハンドラでフラグをセットして、work の最後でフラグが立っていたら exit、みたいに書いたのだが、work を抜けてから job の後始末があるのでよろしくない。 なのでこんな風にしてみた。
「必要に迫られて」というのは、worker をずっと動かしているとだんだんメモリ消費量が増えていく、という現象が起きているから。もちろんそれはそれで原因を解明しないといけないのだけど、とりあえずは定期的に再起動して凌ごうかと。