Nossa espera de 35 horas esperou por si mesma: pgrep -f e pkill -f via SSH
Operações
Em 31 de outubro, iniciamos um pequeno loop via SSH: esperar até que um trabalho de tradução em outra máquina terminasse e, em seguida, executar um trabalho subsequente. O trabalho de tradução terminou naquela noite. Quando olhamos novamente dois dias depois, o loop estava esperando havia 35 horas e o trabalho subsequente nunca havia sido executado.
O que o loop era
ssh build-host 'while pgrep -f "i18n_job.py example.com" >/dev/null; do sleep 30; done; ./follow-up.sh'
pgrep -f corresponde a um padrão na linha de comando completa de cada processo. O SSH executa o comando entre aspas como bash -c 'while pgrep -f "i18n_job.py example.com" …', e essa linha de comando contém o padrão. O pgrep exclui seu próprio processo dos resultados, mas não o shell que o iniciou. Portanto, enquanto o loop estava vivo, ele encontrava uma correspondência: ele mesmo.
Seu gêmeo: pkill -f mata o shell em que é executado
A mesma coisa acontece com pkill -f, com resultados piores:
ssh build-host 'pkill -f "worker.py --queue a"; nohup python3 worker.py --queue a &'
A linha de comando do próprio shell contém o padrão, então o pkill mata o shell. O worker é parado e nunca é iniciado novamente, e o SSH sai com o código 255 sem uma mensagem de erro. Parece um problema de aspas, que foi o que pensamos na primeira vez.
Entre setembro e o final de outubro, caímos em um ou outro pelo menos uma dúzia de vezes: reinicializações que nunca aconteceram, um loop que manteve uma verificação por três horas e meia, outro que manteve uma fila de tradução por 50 minutos e este aqui.
O que fazemos agora
- Um colchete no padrão:
pgrep -f "[i]18n_job.py example.com". Como expressão regular,[i]corresponde à letra i, então o trabalho ainda corresponde; a linha de comando do shell contém os caracteres[i], que a expressão não corresponde. Isso só ajuda se o padrão simples não aparecer em nenhum outro lugar nessa linha: um comando de reinicialização mais adiante na mesma linha corresponde novamente. - Encontrar, parar e iniciar em chamadas SSH separadas. Uma chamada lista os processos com
pgrep -af, a próxima mata os PIDs que ele imprimiu, uma terceira inicia o novo processo. - Espere por um PID, não por um padrão:
tail --pid=1234 -f /dev/null, ouwhile kill -0 1234; do sleep 30; done. - Melhor ainda: deixe o systemd cuidar do trabalho. Execute-o como uma unidade e consulte
systemctl is-active, ou inicie-o comsystemd-run --wait. Não há padrão para errar. - Cada espera tem um prazo limite (
timeout 3h) e informa quando desiste. Um loop sem prazo limite falha silenciosamente.
Factory Floor, o kit que executa nossas máquinas, funciona por meio de unidades systemd: ele nunca procura um processo pela linha de comando. Os loops que nos causaram problemas foram aqueles que digitamos manualmente.