Laravel

Como evitar duplicidade de processamento em jobs

Duplicidade em job é uma das coisas mais comuns (e mais perigosas) em produção. E não adianta lutar contra isso achando que “não vai acontecer”. Vai. Worker cai, timeout estoura, conexão oscila, retry acontece, o mesmo evento chega duas vezes, o deploy reinicia processo no meio… e pronto: job roda de novo.

O objetivo não é “garantir que nunca execute duas vezes”. O objetivo é garantir que executar duas vezes não cause dano.

Abaixo estão as estratégias que realmente funcionam em projetos Laravel médios e grandes.


1) Torne o job idempotente (regra número 1)

Idempotência significa: rodar uma vez ou dez vezes dá o mesmo resultado final.

Isso é o que separa um job saudável de um job que vai te dar dor de cabeça.

Exemplos práticos:

  • “enviar notificação”: marque como enviado e não envie novamente se já estiver enviado.
  • “gerar fatura”: use uma chave única e não crie duas faturas para o mesmo evento.
  • “sincronizar pedido”: atualize o registro existente em vez de criar um novo.

Em termos práticos, idempotência quase sempre vira: checar estado antes de executar e persistir o estado depois.


2) Use chave idempotente por evento (event_id / request_id)

Toda vez que o seu sistema gera um job, ele deveria ter um identificador estável do evento.

Exemplo:

  • event_id do webhook recebido
  • message_id da sua tabela
  • order_id + status (depende do caso)
  • um UUID de “processamento” gerado quando o evento nasceu

Aí você registra algo como:

  • “event_id X já foi processado”
  • “order_id Y já teve ação Z executada”

O melhor lugar para isso quase sempre é o banco, porque é fonte de verdade.


3) Garanta unicidade no banco (constraints salvam sistemas)

Se duplicidade causa duplicação de registro, não confie só em lógica de aplicação. Use o banco como proteção.

Exemplos:

  • índice unique em invoice(event_id)
  • unique em notifications(message_id, channel)
  • unique em syncs(external_id)

A aplicação tenta criar; se já existe, você trata e segue.

Isso é robusto porque:

  • funciona mesmo com múltiplos workers
  • não depende de timing perfeito
  • reduz risco de corrida

4) Locks distribuídos (Redis) para evitar concorrência

Locks são úteis quando você quer garantir que apenas um worker execute determinada seção crítica ao mesmo tempo.

Exemplo:

  • processar um mesmo order_id
  • consolidar um relatório
  • importar lote único

Em Laravel, dá pra fazer lock via cache (Redis):

  • lock por chave: lock:order:123
  • se não adquirir, outro worker já está executando

Importante: lock não substitui idempotência. Ele reduz concorrência, mas não garante que um retry nunca vai rodar depois.


5) Use “unique jobs” do Laravel (quando couber)

Laravel tem suporte para jobs únicos (dependendo da versão/config), mas isso é mais útil para evitar enfileirar duplicado, não para evitar execução duplicada em qualquer cenário.

Use se:

  • você quer impedir spam de job igual
  • o job é claramente único por chave
  • você entende as limitações (especialmente com retries e falhas)

Mesmo com unique job, eu continuo recomendando idempotência + banco.


6) Controle retries, timeout e backoff com critério

Muita duplicidade aparece porque:

  • job estoura timeout → worker mata → tenta de novo
  • job demora demais → você acha que falhou → reprocessa manualmente
  • dependência externa oscila → retries “na pancada”

Ajuste bem:

  • timeout realista (nem baixo demais, nem infinito)
  • tries coerente com o impacto
  • backoff crescente para não sobrecarregar dependência externa

O objetivo é evitar “tempestade de retries”.


7) Separe “efeito colateral” do “estado principal”

Quando duplicidade é perigosa, normalmente é porque o job faz um efeito colateral irreversível (ex: cobrar cartão, enviar WhatsApp, emitir nota). A abordagem mais segura é:

  1. persistir um estado interno (“pendente”, “processando”, “concluído”)
  2. só então executar o efeito colateral
  3. registrar resultado e contexto

Isso cria trilha de auditoria e evita “executar no escuro”.


8) Instrumente: trace por job e correlação

Você só descobre duplicidade tarde se não tiver rastreabilidade.

Boas práticas:

  • job_id/correlation_id em logs
  • logar início/fim do job com chave do evento
  • métrica de “job duplicado evitado” (sim, isso ajuda muito)

Quando dá problema, você quer saber: “por que rodou duas vezes?” em 2 minutos, não em 2 horas.


Um padrão simples que funciona muito bem

Se eu tivesse que resumir num padrão prático:

  • chave idempotente (event_id)
  • tabela de processamento (processed_events) com unique
  • lock Redis opcional em trechos críticos
  • constraints no banco para proteger criação duplicada
  • logs com correlação

Isso cobre 95% dos casos reais.


Em produção, duplicidade em job não é exceção. É comportamento esperado do mundo real (retries, falhas, concorrência). O caminho profissional é aceitar isso e desenhar o job para ser seguro.

Job bom é o que pode rodar duas vezes sem te acordar de madrugada.

Leonardo

Recent Posts

Como monitorar aplicação e servidor pelo WhatsApp (logs, erros e alertas)

WhatsApp é um canal excelente para alerta porque ele tem uma característica que e-mail e…

21 horas ago

AMP: o que é, para que serve, ainda vale a pena usar?

Durante alguns anos, o AMP foi tratado como solução quase obrigatória para quem queria desempenho…

1 dia ago

O que é Lighthouse e como usar para melhorar seu site

Performance, acessibilidade e boas práticas deixaram de ser “detalhes técnicos” e passaram a impactar diretamente…

2 dias ago

Fluxo n8n: API, WhatsApp, Log (com dedupe e tratamento de erro)

O cenário (realista) Você quer receber um evento via API (Webhook), enviar uma mensagem no…

3 dias ago

N8N: o que é, como funciona e quando faz sentido usar

Automação deixou de ser algo exclusivo de grandes sistemas. Hoje, boa parte das aplicações depende…

3 dias ago

Quando o WhatsApp vira canal operacional (e não só meio de envio)

Durante muito tempo, o WhatsApp foi visto apenas como um canal de comunicação direta: mensagens,…

5 dias ago