Por que webhook duplicado acontece com tanta frequência
Em linguagem direta: o webhook não é uma ligação de telefone com confirmação perfeita. É mais parecido com uma mensagem que pode atrasar, cair ou ser reenviada.
Quando uma entrega falha no caminho ou retorna timeout, o emissor normalmente tenta de novo. O problema não é o retry em si, e seu backend tratar a segunda tentativa como um evento novo.
Na prática, provedores como Stripe tratam webhooks como entrega assíncrona com retries automáticos, eventos possivelmente duplicados e sem garantia de ordenação estrita.
- Timeout na resposta do consumidor mesmo com processamento concluído
- Retry automático do provedor após status 5xx
- Reprocessamento manual durante incidente
- Consumidor sem estratégia de deduplicação
Idempotência na prática: o que significa
Uma operação idempotente permite executar a mesma ação mais de uma vez sem alterar o resultado final após a primeira execução válida.
No contexto de webhooks: se o evento X chegar 3 vezes, seu sistema deve aplicar os efeitos de negócio apenas uma vez. O cliente final não pode ser cobrado, ativado ou notificado em triplo.
A definição formal do HTTP (RFC 7231) reforça que idempotência é exatamente essa propriedade de repetir uma requisição sem mudar o efeito pretendido após a primeira execução.
Padrão recomendado: chave idempotente + tabela de processamento
A forma mais segura para a maioria dos SaaS é persistir uma chave única do evento antes de executar efeitos colaterais.
Se a chave já existir, a requisição vira no-op (ou retorna o mesmo resultado da primeira execução).
No Postgres, `INSERT ... ON CONFLICT` é uma base forte para esse controle, com semântica atômica de insert/update mesmo sob concorrência alta.
Traduzindo para negócio: você para de apagar incêndio de duplicidade e passa a ter previsibilidade na operação.
CREATE TABLE webhook_event_locks (
idempotency_key TEXT PRIMARY KEY,
source TEXT NOT NULL,
received_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
status TEXT NOT NULL DEFAULT 'processing'
);
-- tentativa de claim atômico
INSERT INTO webhook_event_locks (idempotency_key, source, status)
VALUES ($1, $2, 'processing')
ON CONFLICT (idempotency_key) DO NOTHING;
Fluxo seguro de processamento
O fluxo ideal precisa ser atômico e simples de operar em incidente. Evite regra escondida em vários lugares porque isso vira bug caro.
- 1) Validar assinatura e schema do webhook
- 2) Gerar ou ler chave idempotente (event_id do provedor ou hash confiável)
- 3) Tentar inserir lock idempotente
- 4) Se lock falhar: reconhecer duplicado e encerrar
- 5) Se lock passar: processar negócio e gravar status final
- 6) Em falha recuperável: permitir retry com controle explícito
Onde guardar a chave: Redis ou Postgres?
Redis é excelente para altíssima taxa e baixa latência, mas sozinho pode perder estado dependendo da estratégia de persistência.
Postgres traz garantia forte e trilha de auditoria. Para muitos SaaS, Postgres como fonte da verdade é um bom ponto de partida.
Uma estratégia comum em produção é combinar os dois: Redis para absorver pico e Postgres para consistência durável.
Se você está no início, prefira simplicidade: comece com Postgres bem modelado e evolua para camada híbrida quando volume justificar.
- Redis: rápido, bom para cache e janelas curtas de deduplicação
- Postgres: consistente, auditável, mais simples para regras de negócio
- Abordagem híbrida: claim rápido no Redis + confirmação final no banco
Erros comuns que causam retrabalho
Muitos times implementam retry antes de idempotência. Isso aumenta a frequência de duplicidade e pressiona suporte.
Outro erro comum é usar payload inteiro como chave sem normalização, quebrando deduplicação por pequenas variações irrelevantes.
- Atualizar estoque/faturamento antes de registrar chave idempotente
- Não expirar ou arquivar chaves antigas (crescimento sem controle)
- Considerar somente status HTTP e ignorar semântica de negócio
- Não ter métricas de duplicidade para observabilidade operacional
- Assumir que eventos chegam em ordem cronológica
Checklist de implementação para seu time
Se você precisa sair do modo reativo em webhooks, este checklist reduz bastante incidente nas primeiras semanas.
- Defina o campo canônico de idempotência por provedor
- Implemente claim atômico antes de efeitos colaterais
- Padronize resposta para duplicado (2xx com no-op)
- Crie métricas: eventos recebidos, duplicados, retries, falhas finais
- Documente playbook de reprocessamento manual com segurança
- Use backoff exponencial com jitter para evitar avalanche em incidentes
Conclusão
Idempotência não é opcional para webhook em produção: ela separa fluxo confiável de operação caótica.
Quando idempotência, retry e rastreabilidade trabalham juntos, seu time reduz incidente, acelera suporte e protege receita.