Il nostro loop di attesa ha atteso 35 ore per se stesso: pgrep -f e pkill -f via SSH
Ops
Il 31 ottobre abbiamo avviato un piccolo loop via SSH: attendere il completamento di un lavoro di traduzione su un'altra macchina, quindi eseguire un lavoro successivo. Il lavoro di traduzione è terminato quella notte. Quando abbiamo ricontrollato due giorni dopo, il loop era in attesa da 35 ore e il lavoro successivo non era mai stato eseguito.
Cos'era il loop
ssh build-host 'while pgrep -f "i18n_job.py example.com" >/dev/null; do sleep 30; done; ./follow-up.sh'
pgrep -f confronta un pattern con la riga di comando completa di ogni processo. SSH esegue il comando tra virgolette come bash -c 'while pgrep -f "i18n_job.py example.com" …', e quella riga di comando contiene il pattern. pgrep esclude il proprio processo dai risultati, ma non la shell che lo ha avviato. Quindi, finché il loop era attivo, trovava una corrispondenza: se stesso.
Il suo gemello: pkill -f uccide la shell in cui viene eseguito
La stessa cosa accade con pkill -f, con risultati peggiori:
ssh build-host 'pkill -f "worker.py --queue a"; nohup python3 worker.py --queue a &'
La riga di comando della shell stessa contiene il pattern, quindi pkill uccide la shell. Il worker viene fermato e non viene mai riavviato, e SSH esce con il codice 255 senza messaggi di errore. Sembra un problema di virgolettatura, che è ciò che abbiamo pensato la prima volta.
Tra settembre e la fine di ottobre siamo incappati nell'uno o nell'altro almeno una dozzina di volte: riavvii mai avvenuti, un loop che ha trattenuto un controllo per tre ore e mezza, un altro che ha trattenuto una coda di traduzioni per 50 minuti, e questo.
Cosa facciamo ora
- Una parentesi quadra nel pattern:
pgrep -f "[i]18n_job.py example.com". Come espressione regolare,[i]corrisponde alla lettera i, quindi il lavoro corrisponde ancora; la riga di comando della shell contiene i caratteri[i], che l'espressione non corrisponde. Aiuta solo se il pattern semplice non appare da nessun'altra parte su quella riga: un comando di riavvio più avanti sulla stessa riga corrisponderà di nuovo. - Trova, ferma e avvia in chiamate SSH separate. Una chiamata elenca i processi con
pgrep -af, la successiva uccide i PID stampati, una terza avvia il nuovo processo. - Attendi su un PID, non su un pattern:
tail --pid=1234 -f /dev/null, oppurewhile kill -0 1234; do sleep 30; done. - Meglio ancora, lascia che systemd gestisca il job. Esegui il job come unit e interroga
systemctl is-active, oppure avvialo consystemd-run --wait. Non c'è alcun pattern da sbagliare. - Ogni attesa ha una scadenza (
timeout 3h) e indica quando si arrende. Un loop senza scadenza fallisce silenziosamente.
Factory Floor, il kit che gestisce le nostre macchine, funziona tramite unit systemd: non cerca mai un processo dalla riga di comando. I loop che ci hanno creato problemi erano quelli che avevamo scritto a mano.