Bucla noastră de așteptare a așteptat 35 de ore pentru ea însăși: pgrep -f și pkill -f prin SSH
Operațiuni
Pe 31 octombrie am pornit o buclă mică prin SSH: așteaptă până se termină o sarcină de traducere pe o altă mașină, apoi rulează o sarcină ulterioară. Sarcina de traducere s-a terminat în acea noapte. Când am verificat din nou două zile mai târziu, bucla aștepta de 35 de ore, iar sarcina ulterioară nu a rulat niciodată.
Ce era bucla
ssh build-host 'while pgrep -f "i18n_job.py example.com" >/dev/null; do sleep 30; done; ./follow-up.sh'
pgrep -f caută un șablon în linia de comandă completă a fiecărui proces. SSH rulează comanda între ghilimele ca bash -c 'while pgrep -f "i18n_job.py example.com" …', iar acea linie de comandă conține șablonul. pgrep exclude propriul proces din rezultate, dar nu și shell-ul care l-a pornit. Așadar, cât timp bucla era activă, găsea o potrivire: pe ea însăși.
Gemena ei: pkill -f omoară shell-ul în care rulează
Același lucru se întâmplă cu pkill -f, cu rezultate mai grave:
ssh build-host 'pkill -f "worker.py --queue a"; nohup python3 worker.py --queue a &'
Linia de comandă a shell-ului conține șablonul, deci pkill omoară shell-ul. Worker-ul este oprit și nu mai este pornit niciodată, iar SSH iese cu codul 255 fără mesaj de eroare. Pare o problemă de ghilimele, ceea ce am crezut și noi prima dată.
Între septembrie și sfârșitul lui octombrie am căzut în una sau alta cel puțin de o duzină de ori: reporniri care nu au avut loc niciodată, o buclă care a blocat o verificare timp de trei ore și jumătate, alta care a blocat o coadă de traduceri timp de 50 de minute și aceasta.
Ce facem acum
- Un parantez pătrat în șablon:
pgrep -f "[i]18n_job.py example.com". Ca expresie regulată,[i]se potrivește cu litera i, deci sarcina se potrivește în continuare; linia de comandă a shell-ului conține caracterele[i], pe care expresia nu le potrivește. Ajută doar dacă șablonul simplu nu apare nicăieri altundeva pe acea linie: o comandă de repornire mai târziu pe aceeași linie se potrivește din nou. - Găsiți, opriți și porniți în apeluri SSH separate. Un apel listează procesele cu
pgrep -af, următorul omoară PID-urile afișate, al treilea pornește noul proces. - Așteptați după un PID, nu după un șablon:
tail --pid=1234 -f /dev/null, sauwhile kill -0 1234; do sleep 30; done. - Mai bine încă: lasă systemd să preia sarcina. Ruleaz-o ca unitate și întreabă
systemctl is-active, sau pornește-o cusystemd-run --wait. Nu există niciun tipar de greșit. - Fiecare așteptare are un termen limită (
timeout 3h) și spune când renunță. O buclă fără termen limită eșuează silențios.
Factory Floor, kitul care ne rulează mașinile, funcționează prin unități systemd: nu caută niciodată un proces după linia de comandă. Bucla care ne-a pus în dificultate a fost cea pe care am tastat-o manual.