Onze wachtlus wachtte 35 uur op zichzelf: pgrep -f en pkill -f via SSH
Ops
Op 31 oktober startten we een kleine lus via SSH: wacht tot een vertaaltaak op een andere machine klaar is, voer daarna een opvolgende taak uit. De vertaaltaak was die avond klaar. Toen we twee dagen later weer keken, had de lus 35 uur gewacht en was de opvolgende taak nooit uitgevoerd.
Wat de lus was
ssh build-host 'while pgrep -f "i18n_job.py example.com" >/dev/null; do sleep 30; done; ./follow-up.sh'
pgrep -f zoekt een patroon in de volledige commandoregel van elk proces. SSH voert de aangehaalde opdracht uit als bash -c 'while pgrep -f "i18n_job.py example.com" …', en die commandoregel bevat het patroon. pgrep laat zijn eigen proces buiten de resultaten, maar niet de shell die het heeft gestart. Zolang de lus leefde, vond hij dus een match: zichzelf.
Zijn tweeling: pkill -f beëindigt de shell waarin het draait
Hetzelfde gebeurt met pkill -f, met slechtere gevolgen:
ssh build-host 'pkill -f "worker.py --queue a"; nohup python3 worker.py --queue a &'
De commandoregel van de shell bevat het patroon, dus pkill beëindigt de shell. De worker wordt gestopt en nooit meer gestart, en SSH sluit af met code 255 zonder foutmelding. Het lijkt op een probleem met aanhalingstekens, en daar hielden we het de eerste keer ook voor.
Tussen september en eind oktober liepen we minstens een dozijn keer in de ene of de andere val: herstarts die nooit plaatsvonden, een lus die een controle drieënhalf uur vasthield, een andere die een vertaalwachtrij 50 minuten vasthield, en deze.
Wat we nu doen
- Een haakje in het patroon:
pgrep -f "[i]18n_job.py example.com". Als reguliere expressie komt[i]overeen met de letter i, dus de taak matcht nog steeds; de commandoregel van de shell bevat de tekens[i], waar de expressie niet op matcht. Het helpt alleen als het platte patroon nergens anders op die regel staat: een herstartopdracht later op dezelfde regel matcht weer. - Zoeken, stoppen en starten in aparte SSH-oproepen. Eén oproep somt de processen op met
pgrep -af, de volgende beëindigt de PIDs die hij heeft afgedrukt, een derde start het nieuwe proces. - Wacht op een PID, niet op een patroon:
tail --pid=1234 -f /dev/null, ofwhile kill -0 1234; do sleep 30; done. - Nog beter: laat systemd de taak beheren. Voer het uit als een unit en vraag
systemctl is-active, of start het metsystemd-run --wait. Er is geen patroon dat je verkeerd kunt doen. - Elke wacht heeft een deadline (
timeout 3h) en zegt wanneer het opgeeft. Een lus zonder deadline faalt stilzwijgend.
Factory Floor, de kit die onze machines aanstuurt, werkt via systemd-units: het zoekt nooit een proces op basis van de commandoregel. De lussen die ons parten speelden, waren die welke we met de hand hadden ingetypt.