Our wait loop waited 35 hours for itself: pgrep -f and pkill -f over SSH

Ops

On 31 October we started a small loop over SSH: wait until a translation job on another machine finishes, then run a follow-up job. The translation job finished that night. When we looked again two days later, the loop had been waiting for 35 hours, and the follow-up had never run.

What the loop was

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

pgrep -f matches a pattern against the full command line of every process. SSH runs the quoted command as bash -c 'while pgrep -f "i18n_job.py example.com" …', and that command line contains the pattern. pgrep leaves its own process out of the results, but not the shell that started it. So as long as the loop was alive, it found a match: itself.

Its twin: pkill -f kills the shell it runs in

The same thing happens with pkill -f, with worse results:

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

The shell's own command line contains the pattern, so pkill kills the shell. The worker is stopped and never started again, and SSH exits with code 255 without an error message. It looks like a quoting problem, which is what we took it for the first time.

Between September and the end of October we fell into one or the other at least a dozen times: restarts that never happened, a loop that held a check for three and a half hours, another that held a translation queue for 50 minutes, and this one.

What we do now

Factory Floor, the kit that runs our machines, works through systemd units: it never looks for a process by its command line. The loops that bit us were the ones we typed by hand.

Updated: