TheSchwartz の worker はシグナルに対してデフォルトでは何もしないので、再起動させようと SIGHUP を送信したりすると job 処理の途中で割り込まれて死ぬ可能性があります。
自前でトラップして安全に再起動する方法は過去に TheSchwartz の worker を安全に停止する で書きました。3年前の記事ですが。
今回は、シグナルへの対処がなされていない古いスクリプトを修正したものの、既に動いている worker を安全に止めないと入れ替えられないのでどうするか、というお話。
安全な停止法を twitter で緩募したところ、以下のようなアドバイスを頂きました。しかし別の queue DB 使うのは確実そうだけどちょっと面倒ですよね……
緩募: signal trapしてないTheSchwartzのjob workerを安全に止める方法
@fujiwara queueに突っ込む可能性のあるプロセス全部止めて様子見
2011-04-25 11:37:31 via Echofon to @fujiwara
@Yappo 稼働中のwebappから突っ込まれるので止めづらいんです
2011-04-25 11:38:26 via YoruFukurou to @Yappo
@fujiwara そのwebappが別のqueueにつっこむとか。。
2011-04-25 11:38:59 via Echofon to @fujiwara
queue別に作ってそっちに逃がしてから古いの止めるとかか。うーむ
@fujiwara 別のDB/tableが面倒ですが、確実かもしれませんね..
2011-04-25 11:45:17 via Echofon to @fujiwara
今回は 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 を書くときには、動くだけではなく安全に止められるように作っておきたいものですね。