Se o seu sistema depende de webhooks para avisar outros serviços sobre eventos, uma hora a entrega vai falhar. Rede instável, servidor fora do ar, timeout. A pergunta não é se isso acontece, é quando. E é exatamente para esse cenário que existe o webhook retry exponencial: uma política de reenvio que aumenta o intervalo entre tentativas de forma progressiva. Em vez de martelar o destinatário com tentativas a cada 5 segundos, você espera 1 segundo, depois 2, depois 4, depois 8. Simples no conceito, mas com armadilhas na prática. Este guia cobre o passo a passo para implementar sem transformar sua fila em um caos.
O objetivo deste guia é fazer você sair com um sistema de retry funcional, seguro e testável. Antes de começar, você vai precisar de: um serviço que envie webhooks (pode ser um script simples), um armazenamento de estado (banco de dados ou cache como Redis), e um agendador de tarefas (como Sidekiq, Celery, ou até um cron). Se você ainda não tem um sistema de filas, a implementação vai exigir criar uma estrutura própria. O restante do guia assume que você tem pelo menos uma fila básica.
Passo 1: Defina o número máximo de tentativas
O primeiro passo é decidir quando desistir. Sem um limite, seu sistema vai reenviar o mesmo webhook para sempre, consumindo recursos e enchendo logs. Uma prática comum é limitar a 5 ou 7 tentativas. Depois disso, o webhook entra em uma fila de abandono (abandon queue), onde um humano ou um processo separado pode analisar o que aconteceu. No seu código, isso significa um contador persistido por webhook, incrementado a cada falha.
Erro comum: não persistir o contador. Se o seu worker reiniciar no meio do processo, você perde a contagem e o retry recomeça do zero. Guarde o número de tentativas junto com o payload do webhook, não em memória.
Passo 2: Calcule o intervalo com base na tentativa
O cálculo do intervalo é o coração do retry exponencial. A fórmula básica é: intervalo = base * (2 ^ tentativa). Com base de 1 segundo, a primeira tentativa (tentativa 0) espera 1 segundo, a segunda espera 2, a terceira espera 4, e assim por diante. Esse crescimento exponencial dá ao servidor receptor tempo para se recuperar de um pico de tráfego ou de uma queda temporária.
Na prática, você raramente usa a fórmula pura. O problema é o efeito manada: se 100 webhooks falham ao mesmo tempo, todos vão tentar reenviar no mesmo segundo, criando outro pico. Para suavizar isso, some um valor aleatório (jitter) ao intervalo. Por exemplo: intervalo = base * (2 ^ tentativa) + random(0, 1000ms). O jitter espalha as tentativas no tempo e reduz a probabilidade de sobrecarga. Não pule essa etapa.
Passo 3: Implemente a lógica de agendamento
Agora que você tem o intervalo, precisa agendar o reenvio. Se você usa uma fila como Sidekiq ou Celery, pode usar o suporte nativo a retry com atraso. No Sidekiq, a opção retry aceita um array de intervalos em segundos. No Celery, você define max_retries e usa countdown para o atraso. Se a fila não oferece isso, crie uma tabela de agendamento com o timestamp da próxima tentativa e um worker que busca os itens vencidos.
Dica: use um timestamp absoluto, não um contador de segundos. Se o worker estiver ocupado e processar o item 10 segundos depois do previsto, o intervalo real será maior que o calculado. Isso não é um problema, mas o timestamp absoluto facilita o debug e mantém a ordem.
Passo 4: Registre cada tentativa em log
Você precisa saber o que aconteceu com cada webhook. Registre em log: o ID do webhook, o número da tentativa, o intervalo usado, o status HTTP retornado, e o corpo da resposta (se houver). Isso não é burocracia, é diagnóstico. Quando um webhook falha na terceira tentativa, você vai olhar o log para entender se o servidor estava retornando 500 ou se o payload estava inválido.
Um erro comum é logar apenas o erro final. Sem o histórico, você não sabe se a falha foi consistente ou intermitente. Guarde também o tempo total entre a primeira tentativa e a última, para avaliar se a política de retry está adequada.
Passo 5: Trate falhas permanentes separadamente
Nem toda falha merece retry. Um webhook que retorna 400 (Bad Request) indica que o payload está errado, e reenviar o mesmo payload não vai mudar nada. Nesse caso, não há retry exponencial que resolva. Você deve separar os erros em duas categorias: temporários (5xx, timeout, conexão recusada) e permanentes (4xx, payload inválido). Para os permanentes, mova direto para a fila de abandono, sem gastar tentativas.
Isso evita um problema clássico: o servidor receptor responde 400 porque seu webhook está com um campo obrigatório faltando, e o seu sistema tenta 7 vezes antes de desistir. Cada tentativa é um log inútil e um recurso gasto. Classifique o erro antes de decidir o retry.
Passo 6: Teste com cenários reais
Testar retry exponencial não é testar o caminho feliz. Você precisa simular falhas: derrube o servidor receptor, retorne 500, feche a conexão no meio da resposta. Ferramentas como WireMock ou um servidor local que você controla são úteis. Configure o sistema para enviar webhooks para um endpoint de teste e varie os códigos de resposta.
Um teste específico é o de efeito manada: dispare 50 webhooks ao mesmo tempo com falha e veja se os reenvios se espalham no tempo. Se todos caírem no mesmo segundo, seu jitter não está funcionando. Ajuste o intervalo aleatório até que a distribuição fique razoável.
Passo 7: Monitore e ajuste os intervalos
Após colocar em produção, monitore as taxas de sucesso por tentativa. Se a maioria dos webhooks falha na primeira tentativa e é resolvida na segunda, seu intervalo base está bom. Se muitos falham na quarta ou quinta, talvez o servidor receptor precise de mais tempo, ou sua base está curta. Não existe um valor universal: base de 1 segundo funciona para muitos casos, mas serviços lentos podem precisar de 5 ou 10 segundos.
Uma métrica útil é o tempo médio de resolução: quanto tempo entre a primeira falha e o sucesso final. Se esse tempo for maior que o esperado, reduza o número de tentativas ou aumente a base. Se for menor, você pode estar tentando demais. O ajuste é iterativo.
Passo 8: Prepare a fila de abandono
Depois do número máximo de tentativas, o webhook vai para a fila de abandono. Essa fila não é um limbo, é um ponto de decisão. Você pode ter um job que envia um alerta para o time, ou um painel onde um humano revisa o payload e decide se reenvia manualmente. O importante é que nada seja descartado silenciosamente.
Na prática, a fila de abandono pode ser uma tabela no banco com status failed e um campo last_error. Periodicamente, um script tenta reprocessar os itens com mais de 24 horas, caso o problema tenha sido resolvido. Isso dá uma segunda chance sem sobrecarregar o sistema.
Checklist final
- Limite de tentativas definido e persistido.
- Intervalo calculado com fórmula exponencial e jitter.
- Agendamento com timestamp absoluto.
- Logs com histórico de cada tentativa.
- Erros permanentes separados dos temporários.
- Testes com falhas simuladas e verificação de efeito manada.
- Monitoramento de taxas de sucesso e tempo de resolução.
- Fila de abandono com revisão periódica.
Com esses passos, seu sistema de webhooks vai sobreviver a quedas de rede, picos de tráfego e servidores instáveis sem transformar o reenvio em um novo problema. O retry exponencial não é uma solução mágica, é uma disciplina: você decide quando tentar, quanto esperar e quando parar. E é exatamente essa previsibilidade que evita que uma falha simples vire uma cascata de requisições.
FAQ
O que é webhook retry exponencial?
É uma técnica de reenvio de webhooks com falha onde o intervalo entre tentativas cresce de forma exponencial, por exemplo, 1s, 2s, 4s, 8s. Isso reduz a carga no servidor receptor e aumenta a chance de sucesso em falhas temporárias.
Qual é a diferença entre retry exponencial e retry fixo?
No retry fixo, o intervalo é constante, como 5s entre cada tentativa. No exponencial, o intervalo dobra a cada falha. O exponencial é mais eficiente porque dá mais tempo para o servidor se recuperar e evita sobrecarga.
O que é jitter no contexto de retry?
Jitter é um valor aleatório adicionado ao intervalo calculado. Ele espalha as tentativas no tempo, evitando que muitos webhooks falhos sejam reenviados ao mesmo tempo, o que causaria um novo pico de tráfego.
Quantas tentativas devo configurar?
O número varia, mas 5 a 7 tentativas é um intervalo comum. O importante é que haja um limite e que, após ele, o webhook vá para uma fila de abandono para análise manual ou reprocessamento posterior.
Como tratar erros 400 vs 500 no retry?
Erros 400 indicam problema no payload, então não devem ser reenviados. Erros 500 indicam problema no servidor, que pode ser temporário, então merecem retry. Separe os dois tipos na sua lógica.
O que é a fila de abandono (abandon queue)?
É um local onde os webhooks que falharam em todas as tentativas são armazenados. Eles não são descartados, mas ficam para revisão humana ou reprocessamento agendado, como após 24 horas.