Sobrecarga em tasks do Cron

Autoprojete-se no seguinte cenário da sua infra-estrutura: Existem diversas tarefas agendadas via Cron em seu sistema operacional. No entanto, por alguma razão, elas levam mais tempo para serem executadas do que o previsto. Por que? O que pode estar havendo? Muitas pessoas têm como conceito que as coisas movidas por tecnologias ou funcionam ou não funcionam. No entanto, existem alguns casos que perdas de integridade e confiabilidade não são tão brutas e repentinas. Elas ocorrem vagarosamente assim como um relógio de quartzo pode atrasar eventualmente, enquanto um relógio feito de isótopo de césio 133 apenas atrasará após centenas de anos de utilização, isto é, o início do seu respectivo clock.

 

Basicamente temos que esses atrasos podem significar que as tarefas começaram a se sobrepor e a serem executadas ao mesmo tempo. Se esses cronjobs estiverem atuando sobre os mesmos dados de um banco de dados, por exemplo, isso pode gerar uma corrupção de dados. Se eles estiverem fazendo um grande processamento de dados, esta ação poderá ter como consequência um elevado custo energético para a CPU da máquina. Por causa dessa alta carga, esses cronjobs iniciam um círculo vicioso resultando em mais problemas para o administrador do sistema operacional, uma vez que, os cronjobs mantêm os seus respectivos lançamentos e se sobrepõem uns aos outros.

 

No mundo GNU/Linux sempre estamos nos aperfeiçoando. E para que isto ocorra, problemas precisam surgir. O problema de sobrecarga do Cron apareceu no nosso cotidiano, e uma evolução das nossas ferramentas tornou-se necessária ou pelo menos a integração de soluções modulares disponibilizando-se como algo mais performático e eficaz.

 

Flock

O flock é uma ferramenta muito interessante para o gerenciamento de arquivos de bloqueio. Esses arquivos de bloqueio são usados para determinar se um script ou um aplicativo já está em execução (comparável a um arquivo PID que contém a identificação do processo do script sendo executado). Se o bloqueio existe, o cronjob não será iniciado. Se o bloqueio não existe, é seguro lançar o cron.

 

Veja o seguinte exemplo comum, onde um cron é executado a cada minuto no servidor.

$ crontab -l
* * * * * /usr/bin/php /www/app/cron.php

Se o script leva mais de um minuto para executar, eles começam a se sobrepor.

 

Para evitar isso, você pode mudar com o exemplo flock abaixo.

$ crontab -l
* * * * * /usr/bin/flock -w 0 /root/cron/cron.lock /usr/bin/php /www/app/cron.php

 

O exemplo a cima requer que o flock gerencie esses arquivos de bloqueio. Se isso ainda não existe no seu sistema, a instalação deve ser tão simples como um yum install flock ou apt-get install flock, dependendo de sua distribuição Linux.

 

No momento em que o flock começa, ele bloqueia o arquivo .lock que você especificar no comando. Você pode ver isso ao solicitar o usuário/script que está tendo o bloqueio sobre esse arquivo.

$ fuser -v /path/to/cron.lock
USER        PID ACCESS COMMAND
cron.lock:           root       7836 f…. flock
root       7837 f…. php

 

Ele vai mostrar o processo de IDs (PIDs) do script que está mantendo o bloqueio. Se nenhum script estiver mantendo o bloqueio, o comando fuser retornará simplesmente nada.

$ fuser -v /path/to/cron.lock

 

Então, o flock é uma boa maneira de evitar a sobreposição de cronjobs usando uma ferramenta de linha de comando adicional.
 

 

 

Pgrep

Outro método, sem o uso de arquivos de bloqueio, é utilizar um liner bash-one bem simples que verifica o arquivo de execução atual e o executa se ele não estiver funcionando. O grande segredo aqui é envolver seu crontask em um nome exclusivo do bash-script, assim:

$ cat /root/cron/cron.sh
#!/bin/bash
/usr/bin/php /www/app/cron.php

$ chmod +x /root/cron/cron.sh

 

No seu crontab, ele deve ser listado assim:

$ crontab -l
* * * * * /www/app/cron.sh

O comando acima irá, assim como o primeiro exemplo, executar o nosso script PHP a cada minuto através de um script. Para evitar a sobreposição, ele pode também ser alterado para isto:

 

$ crontab -l
* * * * * /usr/bin/pgrep -f /root/cron/cron.sh > /dev/null 2> /dev/null || /root/cron/cron.sh

O comando pgrep irá retornar falso se não encontrar um processo de execução correspondente ao primeiro argumento, /root/cron/cron.sh. Se ele retornar falso, vai processar a segunda parte da comparação OR  (a linha vertical dupla, ||). Se o processo de execução foi encontrado, pgrep irá retornar o ID do processo (PID) e Bash não vai continuar para a segunda parte do argumento OR, já que o primeiro já retornou verdadeiro.

 

A dica aqui é usar nomes de script muito originais. Se o nome é muito genérico (como “cron.sh”), pgrep pode retornar IDs de processo a partir de outras tarefas cron em execução e não executar o cron que você queria.

 

 

Utilizando bloqueio de arquivos dentro do script

Se os exemplos acima não estão disponíveis para você, você ainda pode usar o conceito de arquivos de bloqueio em seu aplicativo. Um dos primeiros comandos em seu script poderia ser verificar a existência de um arquivo de bloqueio. Se ele existir, o script poderia simplesmente sair (1) do aplicativo e parar de rodar. Se o bloqueio de arquivo não existir, o script poderia criá-lo e evitar que o próximo trabalho seja executado. Como último passo no seu script, você remove o arquivo de bloqueio para indicar que o script terminou e permitir que a próxima execução continue.