Oi a todos,
Estou rodando n8n em modo fila com múltiplos workers, e estou vendo um problema estranho após reiniciar workers ou deployments.
Minha configuração: Webhook → Fila → Worker → Processo → Banco de dados
Às vezes após uma reinicialização do worker:
Trabalhos antigos são processados novamente
Alguns trabalhos aparecem “travados” e depois tentam novamente inesperadamente
Algumas execuções criam escritas duplicadas no BD/chamadas de API
Eu já uso retries e tratamento de erro básico, mas acho que o problema está relacionado a como os trabalhos são confirmados ou recuperados após crashes do worker.
Exemplo da lógica de processamento:
if ($json.status !== “processed”) {
// continuar processando
}
Estou tentando entender:
• Como o modo fila do n8n lida com trabalhos inacabados após reinicialização
• Se os trabalhos são re-enfileirados automaticamente
• Melhor forma de deixar workflows seguros contra execução duplicada após crashes
Para pessoas usando modo fila em produção:
• Qual é o padrão recomendado para recuperação de crash e processamento idempotente?
Descreva o problema/erro/pergunta
Qual é a mensagem de erro (se houver)?
Por favor compartilhe seu workflow
(Selecione os nós no seu canvas e use os atalhos de teclado CMD+C/CTRL+C e CMD+V/CTRL+V para copiar e colar o workflow.)
Oi @Decoure_Ryan O que você está vendo é geralmente um comportamento normal em modo de fila. Se um worker falha ou reinicia antes de um trabalho ser totalmente concluído/reconhecido, a fila pode marcar esse trabalho como inacabado e reprocessá-lo depois. É por isso que você está vendo trabalhos antigos executarem novamente.
Tente assumir que trabalhos podem ser executados mais de uma vez e torne o processamento idempotente.
Por exemplo, antes de processar:
if ($json.status === “processed”) {
return ;
}
E use proteção em nível de banco de dados como:
ON CONFLICT DO NOTHING
ou chaves únicas para evitar inserções duplicadas.
Formas comuns de produção que você pode aplicar
Fila gerencia tentativas/recuperação
Banco de dados gerencia deduplicação/idempotência
Workers permanecem stateless
Bem-vindo @Decoure_Ryan à nossa comunidade! Sou o Jay e sou um criador verificado da n8n.
Para complementar o que Niffzy disse - a causa raiz é o mecanismo de recuperação de “stalled job” do Bull. Quando um worker é reiniciado sem completar graciosamente um job, o Bull marca esse job como stalled após QUEUE_BULL_STALLED_INTERVAL milissegundos (padrão 30000ms) e o recoloca na fila. Você pode ajustar isso com QUEUE_BULL_MAX_STALLED_COUNT=1 para limitar quantas vezes um job stalled é retentado, e QUEUE_BULL_STALLED_INTERVAL para controlar a janela de detecção. Para idempotência no nível da n8n, use $getWorkflowStaticData ou uma verificação de status no BD no início do workflow para interromper a execução se o execution_id já foi processado. Configurar uma constraint única no execution_id no seu BD é a salvaguarda mais confiável.
o Redis atua como broker e os workers executam os jobs, mas eu não assumiria garantia de exactly-once; após crash/restart, trate como at-least-once, revise N8N_GRACEFUL_SHUTDOWN_TIMEOUT e desenhe o workflow para lidar com reprocessamento.
(não estou gritando é para dar mais ênfase )
SEMPRE USE CHAVE ÚNICA
99% dos problemas poderiam ser resolvidos com isso
Ótima análise do @syed_noor. Uma coisa que eu adicionaria: BullMQ também tem uma configuração de lockDuration (padrão 30s) — se seu fluxo de trabalho levar mais tempo que isso, o lock expira e o job é marcado como stalled mesmo ainda estando em execução. Você pode aumentá-lo via QUEUE_BULL_STALLED_INTERVAL como mencionado, mas também certifique-se de que lockDuration está configurado apropriadamente na sua config do BullMQ.
Também vale a pena notar — a abordagem de chave de idempotência do Postgres é o padrão mais confiável que já vi em produção. Combine-a com o nó “Stop and Error” do n8n após a verificação do INSERT para sair limpamente de execuções duplicadas sem poluir seus logs de erro.
Boa adição na distinção de lockDuration — eu deveria ter destacado isso separadamente. O
QUEUE_BULL_STALLED_INTERVAL controla com que frequência o verificador é executado, mas lockDuration controla por quanto tempo um job pode estar ativo antes de ser considerado travado. Ambos precisam exceder seu tempo de execução de workflow mais longo.
A dica do nó Stop e Error também é sólida. Eu uso depois do INSERT de idempotência com a mensagem definida para o job_id — dessa forma quando você revisa execuções no n8n, pode imediatamente ver quais foram duplicatas legítimas versus falhas reais. Mantém a lista de execução limpa em vez de mostrar erros falsos positivos.
Para quem estiver implementando esse padrão, escrevi um detalhamento mais aprofundado de todas as seis dimensões de preparação para produção (idempotência é apenas uma delas) aqui:
O job_id na mensagem de Stop e Error é um detalhe inteligente - torna a triagem muito mais rápida quando você está examinando execuções. Uma coisa a mais que vale a pena adicionar a este padrão: defina o continueOnFail no nó de verificação de idempotência e roteie o caminho “já processado” para um nó No-op com um nome claro (por exemplo, “DUPLICATE - skipped”), em vez de depender apenas do caminho de erro. Mantém o grafo de execução legível e separa as omissões esperadas das falhas reais à primeira vista.