flock: Prevenindo Execuções Simultâneas com Bloqueio de Arquivo

flock: Prevenindo Execuções Simultâneas com Bloqueio de Arquivo

O Que é flock?

Conclusão: flock é um comando do util-linux que usa um bloqueio baseado em arquivo para impedir que um script ou tarefa seja executado duas vezes ao mesmo tempo, em uma única linha.

O que você vai aprender:

  • O padrão para prevenir sobreposição de cron jobs com flock
  • Quando usar os modos exclusivo / compartilhado / não-bloqueante / timeout
  • Um snippet pronto para autolock de um script

Os padrões práticos

  • Proteger um único comando --> flock -n /var/lock/job.lock cmd
  • Proteger um script inteiro --> coloque o boilerplate FLOCKER no início
  • Quer esperar --> -w segundos; falhar imediatamente --> -n

Premissas (ambiente alvo)

  • flock vem com o util-linux e está presente em praticamente todas as distros Linux
  • Bloqueios são advisories (cooperativos): só se aplicam entre processos que usam flock
  • Suporte em NFS / CIFS é limitado (coberto abaixo)

Por Que Prevenir Execuções Simultâneas?

Conclusão: Se um backup ou tarefa batch reinicia enquanto a execução anterior ainda está em andamento, você arrisca corrupção de dados, processamento duplicado e carga descontrolada. O flock previne a sobreposição estruturalmente.

Suponha que uma tarefa batch execute a cada 5 minutos via cron, mas uma execução leva mais de 5 minutos. A próxima execução sobrepõe a anterior, e dois processos escrevem no mesmo arquivo ao mesmo tempo. O resultado típico é um dos seguintes:

  • Arquivos de saída e logs corrompidos ou intercalados
  • Processamento duplicado (emails duplos, cobranca dupla)
  • Processos acumulando e esgotando memória / CPU

Você poderia gerenciar um lockfile PID manualmente, mas se o processo morrer via kill -9 ou perda de energia, um lock obsoleto permanece e futuras execuções nunca iniciam. Com flock, o kernel libera o bloqueio automaticamente quando o processo termina, então não há limpeza para fazer.

Como Usar o flock?

Conclusão: A forma básica é flock lockfile comando. O lockfile é criado automaticamente se não existir, e o bloqueio é liberado no momento em que o comando termina.

flock tem três sintaxes.

# Forma 1: bloquear um arquivo/diretorio e executar um comando
flock /var/lock/mytask.lock command args

# Forma 2: executar um unico comando pelo shell com -c
flock /var/lock/mytask.lock -c 'command1 && command2'

# Forma 3: bloquear um numero de file descriptor ja aberto
flock 200

Um exemplo mínimo. Execute echo mantendo um bloqueio exclusivo:

flock -x /tmp/myapp.lock echo 'executando sob bloqueio'
  • O lockfile (/tmp/myapp.lock) é criado se não existir
  • -x solicita um bloqueio exclusivo (é o padrão, então pode ser omitido)
  • O bloqueio é liberado assim que echo termina

O lockfile é uma "chave", não "dados". Pode estar vazio, e você nunca precisa excluí-lo. Deixar o arquivo no lugar não causa problema, porque o estado do bloqueio está vinculado ao file descriptor aberto, não à existência do arquivo.

Quais São as Principais Opções do flock?

Conclusão: As quatro que você precisa conhecer são -n (não esperar), -w (limite de tempo), -s (compartilhado) e -E (código de saída em falha).

Opção Significado
-x, --exclusive Bloqueio exclusivo (escrita). Padrão
-s, --shared Bloqueio compartilhado (leitura). Múltiplos detentores permitidos
-n, --nonblock Falhar imediatamente em vez de esperar (código de saída 1)
-w, --timeout sec Esperar até N segundos, depois falhar. Frações permitidas (-w 0.5)
-u, --unlock Liberar o bloqueio explicitamente
-E, --conflict-exit-code N Código de saída em falha -n / -w (padrão 1)
-o, --close Fechar o fd antes de executar o comando (filho não o mantea)
-c, --command Executar um único comando pelo shell (sh -c)

-w 0 é equivalente a --nonblock (zero segundos de espera significa nenhuma espera).

Como Impedir que um cron Job Se Sobreponha?

Conclusão: Envolva a linha do cron em flock -n lockfile. Se a execução anterior ainda estiver em andamento, esta execução sai silenciosamente e nenhuma sobreposição ocorre.

Este é o caso de uso mais comum do flock. Escreva a linha do crontab assim:

# Backup a cada 5 minutos. Pular esta execucao se a anterior ainda estiver em andamento.
*/5 * * * * /usr/bin/flock -n /var/lock/backup.lock /opt/scripts/backup.sh
  • -n significa sair imediatamente em sobreposição (pular em vez de esperar)
  • Se a tarefa anterior terminou, o bloqueio é adquirido e a tarefa executa
  • Por convenção, coloque o lockfile em /var/lock/ ou /run/lock/

Se você preferir esperar um pouco quando as execuções se sobrepuserem, use -w:

# Esperar ate 30 segundos, depois desistir
*/5 * * * * /usr/bin/flock -w 30 /var/lock/backup.lock /opt/scripts/backup.sh

Dentro do cron, escreva flock como um caminho absoluto (/usr/bin/flock) para que funcione mesmo quando o PATH é mínimo.

Como Fazer um Script se Auto-Bloquear?

Conclusão: Coloque um bloco de file descriptor ou o boilerplate FLOCKER no início do script, e ele se protege contra execuções simultâneas independentemente de como é invocado.

Em vez de escrever flock em cada linha do cron, construa o bloqueio dentro do próprio script. Dois padrões comuns.

Padrão 1: Abordagem com file descriptor

#!/bin/bash
exec 200>/var/lock/myscript.lock
flock -n 200 || { echo "ja em execucao"; exit 1; }

# --- tudo abaixo executa como uma secao critica exclusiva ---
echo "fazendo trabalho..."
sleep 10
  • exec 200>... abre o lockfile no fd 200 (200 é uma convenção; qualquer número livre de 9-255 funciona)
  • flock -n 200 bloqueia esse fd; se não puder, o branch || sai
  • Quando o script termina, o fd fecha e o bloqueio é liberado automaticamente

Padrão 2: Boilerplate FLOCKER (do man page)

#!/bin/bash
[ "${FLOCKER}" != "$0" ] && exec env FLOCKER="$0" flock -en "$0" "$0" "$@" || :

# --- tudo abaixo sempre executa sob o bloqueio ---
echo "fazendo trabalho..."

Isso usa o próprio script como lockfile e verifica a variável de ambiente FLOCKER para decidir se já re-executou através do flock. Cole no início e o autolock está feito.

Quando você bloqueia o próprio arquivo do script (flock -en "$0"), não funciona bem com scripts que se reescrevem via > (scripts auto-atualizáveis). É mais seguro bloquear um arquivo dedicado que você nunca sobrescreve.

Não-Bloqueante vs Timeout: Qual Usar?

Conclusão: Use -n se não houver problema em descartar a execução sobreposta; use -w segundos se uma breve espera permitir que o trabalho prossiga. Verifique o código de saída para detectar falha.

# Falhar imediatamente (para batches onde pular uma sobreposicao e aceitavel)
flock -n /var/lock/job.lock ./job.sh

# Esperar ate 10 segundos (para trabalho onde uma breve contencao deve ser absorvida)
flock -w 10 /var/lock/job.lock ./job.sh

Detecte se o bloqueio falhou verificando o código de saída. Quando -n / -w falha, o código de saída é 1 por padrão e pode ser alterado com -E.

flock -n -E 99 /var/lock/job.lock ./job.sh
if [ $? -eq 99 ]; then
    echo "pulado porque outro processo esta em execucao"
fi

Se o bloqueio for bem-sucedido e o comando executar, o código de saída do flock é o próprio código de saída do comando. Escolha um valor -E que seu comando nunca retorne, para distinguir uma falha de bloqueio de um resultado real do comando.

Quais São as Armadilhas Comuns do flock?

Conclusão: As principais armadilhas são falta de suporte em NFS, perda do bloqueio ao recriar o arquivo de dados com >, e a localização do lockfile.

2. Recriar o arquivo de dados remove o bloqueio

O bloqueio está vinculado ao file descriptor aberto. Se você bloquear o arquivo de dados de saída e depois reconstruí-lo com >, o inode muda e o bloqueio perde o sentido.

# Ruim: bloquear data.txt enquanto recria data.txt
flock data.txt sh -c '> data.txt; generate >> data.txt'

Bloqueie um lockfile dedicado que você nunca sobrescreve.

3. flock não detecta deadlock

Se múltiplos bloqueios forem obtidos em ordem cruzada, podem ocorrer deadlocks, mas flock em si não os detecta. Mantenha um bloqueio por script e evite designs que abranjam múltiplos bloqueios.

Evite essas armadilhas e o flock se torna uma forma poderosa e robusta de eliminar execuções simultâneas em poucos caracteres.

Resumo e Próxima Leitura