Notre boucle d'attente a attendu 35 heures pour elle-même : pgrep -f et pkill -f via SSH
Opérations
Le 31 octobre, nous avons lancé une petite boucle via SSH : attendre la fin d'un job de traduction sur une autre machine, puis exécuter un job de suivi. Le job de traduction s'est terminé cette nuit-là. Quand nous avons regardé à nouveau deux jours plus tard, la boucle attendait depuis 35 heures, et le job de suivi n'avait jamais été exécuté.
Ce qu'était la boucle
ssh build-host 'while pgrep -f "i18n_job.py example.com" >/dev/null; do sleep 30; done; ./follow-up.sh'
pgrep -f compare un motif à la ligne de commande complète de chaque processus. SSH exécute la commande entre guillemets comme bash -c 'while pgrep -f "i18n_job.py example.com" …', et cette ligne de commande contient le motif. pgrep exclut son propre processus des résultats, mais pas le shell qui l'a lancé. Ainsi, tant que la boucle était active, elle trouvait une correspondance : elle-même.
Son jumeau : pkill -f tue le shell dans lequel il s'exécute
La même chose se produit avec pkill -f, avec des résultats pires :
ssh build-host 'pkill -f "worker.py --queue a"; nohup python3 worker.py --queue a &'
La ligne de commande du shell contient le motif, donc pkill tue le shell. Le worker est arrêté et n'est jamais relancé, et SSH se termine avec le code 255 sans message d'erreur. Cela ressemble à un problème de guillemets, ce que nous avons cru la première fois.
Entre septembre et fin octobre, nous sommes tombés dans l'un ou l'autre au moins une douzaine de fois : des redémarrages qui n'ont jamais eu lieu, une boucle qui a bloqué une vérification pendant trois heures et demie, une autre qui a bloqué une file de traduction pendant 50 minutes, et celle-ci.
Ce que nous faisons maintenant
- Un crochet dans le motif :
pgrep -f "[i]18n_job.py example.com". En tant qu'expression régulière,[i]correspond à la lettre i, donc le job correspond toujours ; la ligne de commande du shell contient les caractères[i], que l'expression ne correspond pas. Cela n'aide que si le motif simple n'apparaît nulle part ailleurs sur cette ligne : une commande de redémarrage plus tard sur la même ligne correspondra à nouveau. - Trouver, arrêter et démarrer dans des appels SSH séparés. Un appel liste les processus avec
pgrep -af, le suivant tue les PID qu'il a affichés, un troisième démarre le nouveau processus. - Attendre un PID, pas un motif :
tail --pid=1234 -f /dev/null, ouwhile kill -0 1234; do sleep 30; done. - Mieux encore : laissez systemd gérer la tâche. Exécutez-la en tant qu'unité et interrogez
systemctl is-active, ou lancez-la avecsystemd-run --wait. Il n'y a pas de piège à éviter. - Chaque attente a une échéance (
timeout 3h) et indique quand elle abandonne. Une boucle sans échéance échoue silencieusement.
Factory Floor, le Kit qui pilote nos machines, fonctionne via des unités systemd : il ne cherche jamais un processus par sa ligne de commande. Les boucles qui nous ont posé problème étaient celles que nous avions saisies à la main.