Nuestro bucle de espera esperó 35 horas por sí mismo: pgrep -f y pkill -f sobre SSH
Operaciones
El 31 de octubre iniciamos un pequeño bucle sobre SSH: esperar hasta que un trabajo de traducción en otra máquina termine, y luego ejecutar un trabajo posterior. El trabajo de traducción terminó esa noche. Cuando volvimos a mirar dos días después, el bucle había estado esperando 35 horas y el trabajo posterior nunca se había ejecutado.
Qué era el bucle
ssh build-host 'while pgrep -f "i18n_job.py example.com" >/dev/null; do sleep 30; done; ./follow-up.sh'
pgrep -f hace coincidir un patrón con la línea de comandos completa de cada proceso. SSH ejecuta el comando entre comillas como bash -c 'while pgrep -f "i18n_job.py example.com" …', y esa línea de comandos contiene el patrón. pgrep excluye su propio proceso de los resultados, pero no el shell que lo inició. Así que, mientras el bucle estaba vivo, encontraba una coincidencia: él mismo.
Su gemelo: pkill -f mata al shell en el que se ejecuta
Lo mismo ocurre con pkill -f, con peores resultados:
ssh build-host 'pkill -f "worker.py --queue a"; nohup python3 worker.py --queue a &'
La línea de comandos del propio shell contiene el patrón, por lo que pkill mata al shell. El worker se detiene y nunca se vuelve a iniciar, y SSH sale con el código 255 sin un mensaje de error. Parece un problema de comillas, que es lo que pensamos la primera vez.
Entre septiembre y finales de octubre caímos en uno u otro al menos una docena de veces: reinicios que nunca ocurrieron, un bucle que mantuvo una comprobación durante tres horas y media, otro que mantuvo una cola de traducción durante 50 minutos, y este.
Qué hacemos ahora
- Un corchete en el patrón:
pgrep -f "[i]18n_job.py example.com". Como expresión regular,[i]coincide con la letra i, por lo que el trabajo sigue coincidiendo; la línea de comandos del shell contiene los caracteres[i], que la expresión no coincide. Solo ayuda si el patrón simple no aparece en ningún otro lugar de esa línea: un comando de reinicio más adelante en la misma línea vuelve a coincidir. - Buscar, detener e iniciar en llamadas SSH separadas. Una llamada lista los procesos con
pgrep -af, la siguiente mata los PIDs que imprimió, una tercera inicia el nuevo proceso. - Esperar por un PID, no por un patrón:
tail --pid=1234 -f /dev/null, owhile kill -0 1234; do sleep 30; done. - Mejor aún: deja que systemd se encargue del trabajo. Ejecútalo como una unidad y consulta
systemctl is-active, o arráncalo consystemd-run --wait. No hay ningún patrón que puedas hacer mal. - Cada espera tiene un plazo límite (
timeout 3h) e indica cuándo se rinde. Un bucle sin plazo límite falla silenciosamente.
Factory Floor, el kit que ejecuta nuestras máquinas, funciona mediante unidades de systemd: nunca busca un proceso por su línea de comandos. Los bucles que nos dieron problemas fueron los que escribimos a mano.