SIGNALを考慮してないTheSchwartz Job workerをなるべく安全に停止する

TheSchwartz の worker はシグナルに対してデフォルトでは何もしないので、再起動させようと SIGHUP を送信したりすると job 処理の途中で割り込まれて死ぬ可能性があります。

自前でトラップして安全に再起動する方法は過去に TheSchwartz の worker を安全に停止する で書きました。3年前の記事ですが。

今回は、シグナルへの対処がなされていない古いスクリプトを修正したものの、既に動いている worker を安全に止めないと入れ替えられないのでどうするか、というお話。

安全な停止法を twitter で緩募したところ、以下のようなアドバイスを頂きました。しかし別の queue DB 使うのは確実そうだけどちょっと面倒ですよね……


今回は job の投入され具合を観測したところ、そこまで高頻度ではなかったので、ちょっと強引な手段をとることに。

まず gdb で worker process に attach (ここでプロセスの動作が一時停止)。
bt でスタックトレースを表示。

# gdb
(gdb) attach 4841
(gdb) bt
#0  0x00002af1baec83c0 in __nanosleep_nocancel () from /lib64/libc.so.6
#1  0x00002af1baec8214 in sleep () from /lib64/libc.so.6
#2  0x00000000004bb95d in Perl_pp_sleep ()
#3  0x00000000004804ae in Perl_runops_standard ()
#4  0x000000000042e96e in perl_run ()
#5  0x000000000041bbc5 in main ()

sleep() は今回の job 処理中に呼ばれないので、sleep していたらそれは次の job がやって来るのを待っている状態に違いない。

ということで、ここで (gdbはそのまま、別端末から) シグナル送信。

# kill -TERM 4841
(gdb) detach
(gdb) quit

detach すると止まっていたプロセスが起きて、シグナルを受けて終了する。

逆に bt で sleep が見えなければ job の処理中なので、すぐ detach して処理を進めさせ、また適当なタイミングで attach して確認します。

ちょっとどうなのか、と思う手段ですが、まあいきなり kill を送り付けるよりは安全かなと。

worker を書くときには、動くだけではなく安全に止められるように作っておきたいものですね。