↓Pular para o conteúdo principal
  1. blog/
ARTIGO TÉCNICO

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.

·5 minutos

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:

1
2
3
4
5
exec 9>"$LOCK_FILE"
if ! flock -n 9; then
  echo "Já existe outra execução deste script."
  exit 1
fi

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:

1
2
3
4
10:00 execução A inicia
10:05 execução B inicia
10:08 execução A termina
10:10 execução C inicia

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.

1
2
exec 9>/run/lock/minha-manutencao.lock
flock -n 9 || exit 0

-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:

1
2
3
4
exec 200>/var/lock/minha-fila.lock
flock -x 200

# seção crítica

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:

1
2
calcular slot
→ atualizar arquivo de fila

não é necessário segurar o lock durante uma chamada externa de 20 minutos.

Eu separo locks por responsabilidade quando necessário:

1
2
retry.lock   → protege cálculo/estado da fila
launch.lock  → protege disparo da tarefa

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:

1
/run/lock/job.lock

não significa que alguém ainda possui o lock.

Por isso não use apenas:

1
2
3
if [[ -f "$LOCK_FILE" ]]; then
  exit 1
fi

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:

1
2
3
4
if ! flock -n 9; then
  printf '[%s] execução ignorada: lock ocupado\n' "$(date -Is)" >> "$LOG_FILE"
  exit 0
fi

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:

1
2
3
4
5
6
7
8
9
lock
+
preflight
+
consulta do estado atual
+
idempotência
+
validação final

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:

1
2
host A → /run/lock/job.lock
host B → /run/lock/job.lock

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:

  1. um script de criação OCI verifica dependências, abre um LOCK_FILE e usa flock -n para impedir duas execuções da mesma rotina;
  2. 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 #

1
2
3
4
5
6
7
8
9
[ ] job pode durar mais que o intervalo?
[ ] concorrência é permitida?
[ ] lock deve falhar ou esperar?
[ ] seção crítica está bem delimitada?
[ ] lock local é suficiente?
[ ] skip por lock fica registrado?
[ ] script continua idempotente?
[ ] existe timeout para chamadas externas?
[ ] estado final é validado?

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.

TEM UM CENÁRIO PARECIDO?

Me chama.

Manda o contexto, os sintomas e o que já foi testado. Bora organizar as evidências antes de sair mexendo.

Falar com Castro →
Sem enrolação. Com evidência.