Retry Pattern
Como lidar com falhas transitórias em sistemas distribuídos reexecutando operações com estratégias de backoff para melhorar a resiliência.
Que problema resolve
Em sistemas distribuídos, as chamadas entre serviços podem falhar por razões transitórias: problemas momentâneos de rede, serviços reiniciando, limites temporários de rate limiting ou picos de carga. Essas falhas são temporárias e a operação provavelmente terá sucesso se for reexecutada.
Sin Retry:
Servicio A ──► Servicio B
❌ Error 503 (sobrecarga temporal)
Servicio A devuelve error al usuario
(aunque B se recupera en 2 segundos)
O problema é que, sem retentativas, uma falha transitória de milissegundos se transforma em um erro visível para o usuário, quando simplesmente esperar um momento e tentar de novo teria resolvido o problema.
Falhas transitórias comuns
| Tipo de falha | Causa | Duração típica |
|---|---|---|
| Timeout de rede | Congestionamento momentâneo | Milissegundos a segundos |
| HTTP 503 | Serviço reiniciando ou sobrecarregado | Segundos |
| HTTP 429 | Rate limit atingido | Segundos a minutos |
| Connection refused | Serviço iniciando | Segundos |
| DNS resolution failure | Cache DNS expirado | Milissegundos |
Como funciona
O padrão Retry intercepta as chamadas que falharam e as reexecuta automaticamente conforme uma estratégia configurada. A chave está em quando reexecutar, quantas vezes e quanto esperar entre as tentativas.
Con Retry:
Servicio A ──► Servicio B
❌ Intento 1: Error 503
⏳ Espera 1 segundo
❌ Intento 2: Error 503
⏳ Espera 2 segundos
✅ Intento 3: OK 200
El usuario recibe respuesta exitosa
Estratégias de espera (backoff)
| Estratégia | Fórmula | Exemplo (3 tentativas) | Uso |
|---|---|---|---|
| Fixed delay | delay constante | 1s, 1s, 1s | Casos simples |
| Exponential backoff | delay × 2^tentativa | 1s, 2s, 4s | Padrão recomendado |
| Exponential + jitter | random(0, delay × 2^tentativa) | 0.7s, 1.3s, 3.8s | Evita thundering herd |
| Linear backoff | delay × tentativa | 1s, 2s, 3s | Incremento previsível |
Exponential backoff com jitter
A estratégia mais recomendada é o exponential backoff com jitter (variação aleatória). Sem jitter, quando um serviço se recupera, todos os clientes tentam novamente ao mesmo tempo, criando um pico de carga que pode derrubá-lo de novo (thundering herd problem).
Sin jitter (thundering herd):
Cliente 1: retry en 1s, 2s, 4s
Cliente 2: retry en 1s, 2s, 4s ← Todos al mismo tiempo
Cliente 3: retry en 1s, 2s, 4s
Servicio B recibe 3 requests simultáneos en cada intervalo
Con jitter:
Cliente 1: retry en 0.7s, 1.8s, 3.2s
Cliente 2: retry en 1.2s, 2.3s, 4.5s ← Distribuidos en el tiempo
Cliente 3: retry en 0.9s, 1.5s, 3.9s
Servicio B recibe requests distribuidos
Parâmetros de configuração
| Parâmetro | Descrição | Valor típico |
|---|---|---|
| Max retries | Número máximo de retentativas | 3-5 |
| Initial delay | Tempo de espera antes da primeira retentativa | 100ms - 1s |
| Max delay | Tempo máximo de espera entre retentativas | 30s - 60s |
| Backoff multiplier | Fator de multiplicação exponencial | 2 |
| Retryable errors | Códigos de erro que justificam a retentativa | 408, 429, 500, 502, 503, 504 |
O que reexecutar e o que não
✅ Reintentar (fallos transitorios):
- HTTP 408 Request Timeout
- HTTP 429 Too Many Requests
- HTTP 500 Internal Server Error (con precaución)
- HTTP 502 Bad Gateway
- HTTP 503 Service Unavailable
- HTTP 504 Gateway Timeout
- Connection timeout / Connection refused
❌ NO reintentar (fallos permanentes):
- HTTP 400 Bad Request (datos inválidos)
- HTTP 401 Unauthorized (credenciales incorrectas)
- HTTP 403 Forbidden (sin permisos)
- HTTP 404 Not Found (recurso no existe)
- HTTP 409 Conflict (conflicto de estado)
- HTTP 422 Unprocessable Entity (validación fallida)
Vantagens
- Resiliência a falhas transitórias: A maioria dos erros de rede se resolve sozinha
- Transparente para o usuário: A retentativa acontece sem que o usuário perceba
- Implementação simples: É um dos padrões de resiliência mais fáceis de implementar
- Configurável: Os parâmetros se ajustam conforme o contexto e a criticidade
- Complementa outros padrões: Funciona bem com Circuit Breaker e Timeout
Trade-offs / Desvantagens
- Latência acumulada: Cada retentativa adiciona tempo de espera total
- Carga adicional: As retentativas geram mais tráfego para o serviço que já está falhando
- Operações não idempotentes: Reexecutar uma operação que já foi realizada pode causar duplicações
- Falsos positivos: Reexecutar um erro permanente desperdiça recursos
- Thundering herd: Sem jitter, as retentativas sincronizadas podem piorar a situação
- Complexidade de configuração: Parâmetros incorretos podem causar mais problemas do que soluções
Quando usar
- Chamadas HTTP a serviços externos ou microsserviços
- Operações de rede que podem falhar por razões transitórias
- Consultas a bancos de dados que podem falhar por conexões esgotadas
- Publicação de mensagens em brokers que podem estar temporariamente indisponíveis
- Qualquer operação idempotente que falha de forma intermitente
Quando evitar
- Operações não idempotentes em que reexecutar pode causar efeitos colaterais duplicados
- Quando o erro é claramente permanente (400, 401, 403, 404)
- Operações de longa duração em que o timeout total seria inaceitável
- Quando você já tem um Circuit Breaker aberto (o retry não deve tentar passar por um circuito aberto)
- Sistemas em que a latência adicional das retentativas é inaceitável
Tecnologias e implementações comuns
| Categoria | Opções |
|---|---|
| Bibliotecas | Resilience4j (Java), Polly (.NET), tenacity (Python), axios-retry (Node.js) |
| HTTP Clients | Configuração nativa em HttpClient (.NET), OkHttp (Java), Got (Node.js) |
| Cloud | AWS SDK (retry integrado), Azure SDK (retry policies), Google Cloud Client Libraries |
| Service Mesh | Istio, Linkerd, Envoy (retry no nível de infraestrutura) |
Relação com outros padrões
O Retry raramente é usado sozinho. Faz parte de uma cadeia de resiliência:
Solicitud
│
▼
┌──────────┐
│ Timeout │ Define cuánto esperar por intento
└────┬─────┘
▼
┌──────────┐
│ Retry │ Reintenta si el intento falla
└────┬─────┘
▼
┌──────────┐
│ Circuit │ Corta si hay demasiados fallos
│ Breaker │
└────┬─────┘
▼
Servicio downstream
- Timeout: Define quanto esperar em cada tentativa individual antes de considerá-la falha
- Circuit Breaker: Se as retentativas falham consistentemente, o circuit breaker se abre e evita mais tentativas
- Idempotência: Os serviços downstream devem ser idempotentes para que as retentativas sejam seguras
- Bulkhead: Isola os recursos para que as retentativas de um serviço não afetem os outros
Próximos passos
O Retry é a primeira linha de defesa contra falhas transitórias. Para definir quanto esperar em cada tentativa, explore o padrão Timeout. Para garantir que as retentativas não causem efeitos duplicados, revise o padrão de Idempotência.