我們的等待迴圈自己等了自己 35 小時:透過 SSH 使用 pgrep -f 與 pkill -f

維運

10 月 31 日,我們透過 SSH 啟動了一個小型迴圈:等待另一台機器上的翻譯工作完成,然後執行後續工作。翻譯工作當晚就完成了。但當我們兩天後再次查看時,迴圈已經等待了 35 小時,而後續工作從未執行。

迴圈的內容

ssh build-host 'while pgrep -f "i18n_job.py example.com" >/dev/null; do sleep 30; done; ./follow-up.sh'

pgrep -f 會將模式與每個程序的完整命令列進行比對。SSH 會將引號內的命令以 bash -c 'while pgrep -f "i18n_job.py example.com" …' 的方式執行,而該命令列中包含此模式。pgrep 會排除自己的程序,但不會排除啟動它的 shell。因此,只要迴圈還活著,它就會找到匹配項:也就是它自己。

它的孿生兄弟:pkill -f 會殺掉它所執行的 shell

同樣的情況也會發生在 pkill -f 上,而且結果更糟:

ssh build-host 'pkill -f "worker.py --queue a"; nohup python3 worker.py --queue a &'

shell 自己的命令列中包含此模式,因此 pkill 會殺掉 shell。worker 被停止且從未再次啟動,而 SSH 會以錯誤碼 255 退出,且沒有錯誤訊息。這看起來像是引號問題,我們第一次也是這麼認為。

從 9 月到 10 月底,我們至少十幾次掉入其中一種情況:從未發生的重啟、一個卡住檢查達三個半小時的迴圈、另一個卡住翻譯佇列達 50 分鐘的迴圈,以及這次的情況。

我們現在的做法

Factory Floor 是驅動我們機器的套件,透過 systemd 單元運作:它從不透過命令列尋找程序。讓我們吃虧的迴圈,正是那些我們手動輸入的迴圈。

已更新: