Unser Warteschleifen-Loop wartete 35 Stunden auf sich selbst: pgrep -f und pkill -f über SSH
Ops
Am 31. Oktober starteten wir eine kleine Schleife über SSH: Warten, bis ein Übersetzungsauftrag auf einer anderen Maschine abgeschlossen ist, dann einen Folgeauftrag ausführen. Der Übersetzungsauftrag war noch in derselben Nacht fertig. Als wir zwei Tage später erneut nachsahen, hatte die Schleife 35 Stunden gewartet, und der Folgeauftrag war nie ausgeführt worden.
Was die Schleife war
ssh build-host 'while pgrep -f "i18n_job.py example.com" >/dev/null; do sleep 30; done; ./follow-up.sh'
pgrep -f vergleicht ein Muster mit der vollständigen Befehlszeile jedes Prozesses. SSH führt den in Anführungszeichen gesetzten Befehl als bash -c 'while pgrep -f "i18n_job.py example.com" …' aus, und diese Befehlszeile enthält das Muster. pgrep lässt den eigenen Prozess aus den Ergebnissen heraus, aber nicht die Shell, die ihn gestartet hat. Solange die Schleife also aktiv war, fand sie eine Übereinstimmung: sich selbst.
Sein Zwilling: pkill -f tötet die Shell, in der es läuft
Dasselbe passiert mit pkill -f, mit schlimmeren Folgen:
ssh build-host 'pkill -f "worker.py --queue a"; nohup python3 worker.py --queue a &'
Die Befehlszeile der Shell selbst enthält das Muster, daher tötet pkill die Shell. Der Worker wird gestoppt und nie wieder gestartet, und SSH beendet sich mit dem Exit-Code 255 ohne Fehlermeldung. Es sieht aus wie ein Problem mit den Anführungszeichen, was wir beim ersten Mal auch so interpretierten.
Zwischen September und Ende Oktober sind wir mindestens ein Dutzend Mal in die eine oder andere Falle getappt: Neustarts, die nie stattfanden, eine Schleife, die eine Prüfung dreieinhalb Stunden lang blockierte, eine andere, die eine Übersetzungswarteschlange 50 Minuten lang blockierte, und diese hier.
Was wir jetzt tun
- Eine Klammer im Muster:
pgrep -f "[i]18n_job.py example.com". Als regulärer Ausdruck entspricht[i]dem Buchstaben i, sodass der Auftrag weiterhin gefunden wird; die Befehlszeile der Shell enthält die Zeichen[i], die der Ausdruck nicht abgleicht. Das hilft nur, wenn das einfache Muster nirgends sonst in dieser Zeile vorkommt: Ein Neustart-Befehl später in derselben Zeile würde wieder gefunden werden. - Finden, stoppen und starten in separaten SSH-Aufrufen. Ein Aufruf listet die Prozesse mit
pgrep -afauf, der nächste tötet die ausgegebenen PIDs, ein dritter startet den neuen Prozess. - Warten auf eine PID, nicht auf ein Muster:
tail --pid=1234 -f /dev/nulloderwhile kill -0 1234; do sleep 30; done. - Noch besser: Überlassen Sie systemd den Job. Führen Sie ihn als Unit aus und fragen Sie mit
systemctl is-activeab, oder starten Sie ihn mitsystemd-run --wait. Es gibt kein Muster, das man falsch machen kann. - Jedes Warten hat eine Deadline (
timeout 3h) und sagt an, wann es aufgibt. Eine Schleife ohne Deadline schlägt still fehl.
Factory Floor, das Kit, das unsere Maschinen betreibt, arbeitet mit systemd-Units: Es sucht nie nach einem Prozess anhand seiner Befehlszeile. Die Schleifen, die uns Probleme bereitet haben, waren die, die wir von Hand eingegeben haben.