Mais cedo ou mais tarde, todo job roda duas vezes. O worker cai depois de processar e antes do ack, o visibility timeout expira no meio da execução, uma partição de rede divide a verdade entre dois nós. Sistema de filas desenhado para execução única acumula cobrança duplicada, e-mail repetido e estoque furado. Projete cada handler para execução pelo menos duas vezes.
Como garantir idempotência na prática?
Três técnicas cobrem quase todos os casos. Chaves naturais dedupam por usuário, ação e janela: UNIQUE(user_id, action, DATE(created_at)) barra o segundo processamento do mesmo evento no dia. Idempotency tokens guardados sob constraint unique transformam retry em no-op; o segundo insert falha e o handler devolve sucesso. Updates condicionais retornam linhas afetadas: zero linhas significa trabalho já feito por outra execução.
Visibility timeout: quanto ajustar?
Ponto de partida: p99 da duração do job multiplicado por dois. Timeout curto demais gera duplicatas em massa; longo demais atrasa o retry de jobs perdidos. Cada ferramenta implementa o mecanismo à sua forma: BullMQ renova locks, SQS expõe o parâmetro nativo VisibilityTimeout, Sidekiq usa reliable fetch com super_fetch. Monitore jobs que estouram o timeout; eles são a fonte das duplicatas.
Retry sem thundering herd
Backoff exponencial com jitter espalha as tentativas: 1s, 4s, 16s com ruído aleatório impede que mil workers martelem o serviço externo no mesmo segundo. Classifique o erro antes de agendar: 5xx e timeout são retryable; validação rejeitada é permanente e vai direto para a dead-letter queue. Max attempts esgotado alimenta a DLQ com o payload original, pronto para replay manual durante o incidente.
Hierarquia de filas
Fila única mistura captura de pagamento com exportação de relatório, e o backlog de bulk mata a criticidade. Separe três níveis com workers dedicados: critical (payments, antifraude), default (fluxo normal) e bulk (e-mails, exports). A exportação de 200 mil registros deixa de atrasar o webhook de captura.
Observabilidade e jobs recorrentes
- Profundidade da fila, wait time p95, processing time p95 e failure rate por classe de job
- Alerta dispara na tendência de crescimento da profundidade; valor absoluto alto pode ser rotina de horário de pico
Job recorrente precisa de lock distribuído: SETNX no Redis com TTL ou advisory lock do Postgres (pg_advisory_lock). Sem lock, dois schedulers dão fire duplo no mesmo cron, e o token de idempotência vira a última linha de defesa. Coloque o lock; custa uma linha e evita o ticket de madrugada.
Curtiu o conteúdo?
Construo produtos web e soluções com IA do jeito certo — arquitetura sólida, código sustentável e entrega real.
Vamos conversar