Por que usar flock em scripts de manutenção executados pelo cron
Como flock evita duas execuções concorrentes de scripts operacionais e por que lock, estado e tempo limite precisam fazer parte do desenho da automação.
Cron não sabe se a execução anterior terminou.
Se um job está agendado a cada cinco minutos e uma execução demora oito, o scheduler pode iniciar uma segunda instância enquanto a primeira ainda trabalha.
Em automações reais que uso para OCI e orquestração via Semaphore, esse risco foi tratado explicitamente com flock.
Um dos scripts usa um lock exclusivo logo no preflight:
| |
Outro fluxo precisou serializar chamadas à API do orquestrador porque jobs concorrentes podiam disputar o mesmo cache/repositório.
Esse é o problema que flock resolve: concorrência acidental, não a lógica da tarefa em si.
O bug não aparece quando tudo é rápido #
Considere:
*/5 * * * * /usr/local/bin/manutencao.sh
Enquanto manutencao.sh leva dois minutos, parece perfeito.
No dia em que uma API fica lenta, um backup cresce ou uma chamada cloud entra em retry, o processo passa de cinco minutos.
Agora a linha do tempo vira:
| |
Dependendo do script, isso pode significar:
- dois backups escrevendo no mesmo destino;
- duas rotações removendo arquivos;
- dois deploys alterando o mesmo estado;
- dois jobs chamando a mesma API;
- duas tentativas de criar o mesmo recurso;
- corrupção de arquivo temporário/estado.
flock -n falha rápido #
Para manutenção periódica, muitas vezes quero que a segunda execução simplesmente desista.
| |
-n significa non-blocking.
Se outro processo possui o lock, a execução não fica esperando indefinidamente.
Isso combina bem com cron quando o próximo ciclo já acontecerá depois.
Em outros casos, eu quero esperar #
Em uma fila de jobs que não pode perder execução, o comportamento pode ser diferente:
| |
Aqui o processo espera o lock exclusivo.
Foi esse tipo de serialização que apareceu em uma automação real antes de um POST para o Semaphore: vários jobs vencendo próximos uns dos outros podiam criar corrida no checkout/cache Git do orquestrador.
O lock transformou chamadas concorrentes em uma pequena fila.
Lock deve proteger a seção certa #
Um erro comum é colocar lock em torno de trabalho demais.
Se apenas esta etapa precisa ser serializada:
| |
não é necessário segurar o lock durante uma chamada externa de 20 minutos.
Eu separo locks por responsabilidade quando necessário:
| |
Isso reduz contenção e deixa o motivo do lock explícito.
Arquivo de lock não é “estado de execução” por si só #
Com flock, o lock está associado ao file descriptor/processo.
O simples fato de o arquivo existir:
| |
não significa que alguém ainda possui o lock.
Por isso não use apenas:
| |
Um arquivo pode sobrar depois de reboot/crash.
flock pergunta ao kernel se o lock está realmente ocupado.
Cron precisa de logs também #
Evitar concorrência resolve uma classe de erro, mas pode esconder outra se a segunda execução apenas sai silenciosamente.
Eu registraria:
| |
Assim é possível medir se o job está frequentemente demorando mais do que seu intervalo.
Muitos skips podem indicar que a frequência ou duração precisa ser revista.
Lock não substitui idempotência #
Mesmo com flock, o script pode ser interrompido depois de alterar metade do estado.
Na próxima execução, ele precisa reconhecer o que já foi feito.
Para automação de infraestrutura, eu combino:
| |
O lock evita duas mãos mexendo ao mesmo tempo. Ele não garante que cada mão sabe o que está fazendo.
Em jobs distribuídos, lock local tem limite #
flock num arquivo local protege processos que enxergam o mesmo filesystem/kernel.
Se o mesmo cron roda em dois servidores diferentes:
| |
os locks são independentes.
Nesse cenário, pode ser necessário:
- eleger um único executor;
- usar lock distribuído;
- usar fila;
- usar banco/Redis/consul como coordenação;
- mover a responsabilidade para o orquestrador.
Não chame flock local de lock distribuído.
O que a fonte deste artigo sustenta #
Há duas implementações reais recuperadas:
- um script de criação OCI verifica dependências, abre um
LOCK_FILEe usaflock -npara impedir duas execuções da mesma rotina; - um agendador de tarefas adicionou locks separados para serializar cálculo de retry e disparos para o Semaphore, depois de identificar risco de concorrência em checkout/cache.
Os nomes internos, endpoints, tokens e identificadores reais foram omitidos porque não são necessários para demonstrar o padrão.
Checklist #
| |
O principal aprendizado #
Cron dispara por horário, não por estado.
Se a tarefa não pode rodar duas vezes ao mesmo tempo, essa regra precisa estar no próprio desenho da automação.
flock é uma solução pequena para evitar uma classe grande de incidentes.
Me chama.
Manda o contexto, os sintomas e o que já foi testado. Bora organizar as evidências antes de sair mexendo.