Vår vänteslinga väntade i 35 timmar på sig själv: pgrep -f och pkill -f över SSH
Drift
Den 31 oktober startade vi en liten slinga över SSH: vänta tills ett översättningsjobb på en annan maskin är klart, kör sedan ett uppföljningsjobb. Översättningsjobbet blev klart samma natt. När vi tittade igen två dagar senare hade slingan väntat i 35 timmar, och uppföljningsjobbet hade aldrig körts.
Vad slingan var
ssh build-host 'while pgrep -f "i18n_job.py example.com" >/dev/null; do sleep 30; done; ./follow-up.sh'
pgrep -f matchar ett mönster mot hela kommandoraden för varje process. SSH kör det citerade kommandot som bash -c 'while pgrep -f "i18n_job.py example.com" …', och den kommandoraden innehåller mönstret. pgrep utesluter sin egen process från resultaten, men inte skalet som startade den. Så länge slingan levde hittade den alltså en matchning: sig själv.
Dess tvilling: pkill -f dödar skalet det körs i
Samma sak händer med pkill -f, med värre resultat:
ssh build-host 'pkill -f "worker.py --queue a"; nohup python3 worker.py --queue a &'
Skalets egen kommandorad innehåller mönstret, så pkill dödar skalet. Workern stoppas och startas aldrig igen, och SSH avslutas med kod 255 utan felmeddelande. Det ser ut som ett citeringsproblem, vilket vi trodde första gången.
Mellan september och slutet av oktober råkade vi ut för det ena eller det andra minst ett dussin gånger: omstarter som aldrig hände, en slinga som höll en kontroll i tre och en halv timme, en annan som höll en översättningskö i 50 minuter, och den här.
Vad vi gör nu
- En hakparentes i mönstret:
pgrep -f "[i]18n_job.py example.com". Som reguljärt uttryck matchar[i]bokstaven i, så jobbet matchar fortfarande; skalets kommandorad innehåller tecknen[i], vilket uttrycket inte matchar. Det hjälper bara om det enkla mönstret inte förekommer någon annanstans på den raden: ett omstartskommando senare på samma rad matchar igen. - Hitta, stoppa och starta i separata SSH-anrop. Ett anrop listar processerna med
pgrep -af, nästa dödar de PID:er som skrivits ut, ett tredje startar den nya processen. - Vänta på ett PID, inte ett mönster:
tail --pid=1234 -f /dev/null, ellerwhile kill -0 1234; do sleep 30; done. - Bättre än så: låt systemd äga jobbet. Kör det som en enhet och fråga
systemctl is-active, eller starta det medsystemd-run --wait. Det finns inget mönster att göra fel på. - Varje väntan får en tidsgräns (
timeout 3h) och säger till när den ger upp. En loop utan tidsgräns misslyckas tyst.
Factory Floor, verktygspaketet som kör våra maskiner, fungerar via systemd-enheter: det söker aldrig efter en process via dess kommandorad. De loopar som bet oss i rumpan var de vi skrev för hand.