Retry vs Circuit Breaker: quando usar cada padrão de resiliência
Ano passado, um colega me perguntou: "qual a diferença entre retry e circuit breaker?" Na hora, respondi com a confiança de quem acha que sabe, e depois passei a noite lendo documentação. A verdade é que os dois padrões parecem primos, mas não são gêmeos. Um tenta de novo, o outro desiste por um tempo. Usar o errado no lugar certo transforma um downtime de segundos em horas.
Retry e circuit breaker são padrões de resiliência para sistemas distribuídos. Ambos lidam com falhas, mas em momentos opostos: retry atua na tentativa, circuit breaker atua na proteção do sistema. A escolha entre eles, ou a combinação, depende do tipo de falha que você enfrenta.
O que é Retry (e quando ele funciona)
Retry é o padrão mais intuitivo: uma chamada falhou, tente de novo. Ele faz sentido quando a falha é transitória, um timeout de rede, um pico de latência, um banco que demorou meio segundo a mais. O retry espera um intervalo (exponencial ou fixo) e repete a operação.
No Spring Boot, por exemplo, a anotação @Retryable com backoff permite configurar tentativas e intervalo. Um caso clássico: serviço A chama serviço B e recebe um 500 Internal Server Error por queda momentânea de conexão. Com retry, após 200ms, a segunda tentativa funciona.
O problema: retry não distingue falha transitória de falha permanente. Se o serviço B está fora do ar por 10 minutos, cada requisição vai gerar 3 tentativas, cada uma consumindo recursos. O resultado é uma avalanche de chamadas inúteis que sobrecarrega o sistema já fragilizado.
O que é Circuit Breaker (e quando salva o sistema)
Circuit breaker é o padrão que desiste antes de tentar. Ele monitora a taxa de falhas de uma chamada remota. Quando o número de erros ultrapassa um limite configurável (ex.: 5 falhas em 10 segundos), o circuito abre e todas as chamadas seguintes são rejeitadas imediatamente, sem tentar, retornando um fallback ou lançando exceção.
Depois de um tempo de espera (ex.: 30 segundos), o circuito entra em estado half-open e permite uma chamada de teste. Se bem-sucedida, o circuito fecha. Se falhar, volta a abrir.
O ganho: evita que o sistema cliente espere por respostas que nunca virão, libera threads e reduz a pressão sobre o serviço downstream. É o padrão certo quando a falha é persistente, serviço lento, banco sobrecarregado, dependência externa fora do ar.
Quando usar cada um: tabela de decisão
| Critério | Retry | Circuit Breaker | |---|---|---| | Tipo de falha | Transitória (timeout, perda de pacote) | Persistente (serviço fora, lentidão estrutural) | | Duração esperada | Segundos | Minutos ou mais | | Risco de sobrecarga | Alto em falhas longas | Baixo (corta chamadas) | | Exemplo típico | Conexão TCP perdida e restaurada | API de pagamento com instabilidade | | Combinação com fallback | Raro | Comum (retorna resposta padrão) |
Retry + Circuit Breaker: a combinação que funciona
Na prática, os dois padrões não são mutuamente exclusivos. A abordagem madura é combiná-los: retry para falhas transitórias dentro de uma janela curta, circuit breaker para proteger o sistema quando o retry falha.
Um exemplo concreto: serviço de notificação que chama uma API de e-mail. Configure retry com 3 tentativas e backoff de 500ms. Se todas falharem, o circuit breaker abre por 30 segundos. Durante esse período, chamadas são rejeitadas e um fallback registra a notificação em fila para reprocessamento posterior.
Essa arquitetura impede que o serviço de e-mail, já lento, receba mais requisições enquanto se recupera. É o padrão usado em sistemas como Netflix Hystrix e Resilience4j.
E o timeout? (o terceiro padrão que todo mundo esquece)
Timeout não é retry nem circuit breaker, mas é pré-requisito para ambos. Sem timeout, uma chamada pode travar por 30 segundos enquanto o retry tenta de novo, e o circuit breaker nunca abre porque a thread está bloqueada esperando resposta.
Configure timeouts agressivos: 2 a 5 segundos para chamadas síncronas. Um timeout curto transforma uma falha lenta em falha rápida, permitindo que retry e circuit breaker atuem corretamente.
Veredito: para quem busca X, escolha Y
- Para falhas rápidas e esporádicas (rede instável, pico de latência): escolha retry com backoff exponencial e limite de 2 a 3 tentativas.
- Para falhas longas e estruturais (serviço fora do ar, banco sobrecarregado): escolha circuit breaker com limiar de falhas e half-open.
- Para sistemas críticos (pagamento, autenticação): use retry + circuit breaker + fallback. O retry tenta de novo rapidamente; o circuit breaker corta quando insiste demais; o fallback dá uma resposta segura enquanto o serviço se recupera.
Não existe bala de prata. O melhor padrão é aquele que você testou com o comportamento real do seu sistema. Simule falhas, meça tempos de recuperação e ajuste os thresholds.
Perguntas frequentes
Retry e circuit breaker são a mesma coisa?
Não. Retry repete uma operação que falhou, assumindo que a falha é temporária. Circuit breaker monitora a taxa de falhas e interrompe chamadas quando o serviço está instável, evitando sobrecarga.
Posso usar retry sem circuit breaker?
Sim, desde que as falhas sejam exclusivamente transitórias e de curta duração. Em sistemas com dependências externas, o retry sem proteção pode causar falhas em cascata.
Como configurar retry e circuit breaker juntos?
Configure retry com poucas tentativas (2 a 3) e backoff curto. Após o retry esgotar, o circuit breaker abre por um período (ex.: 30 segundos). Durante esse tempo, chamadas vão para fallback.
O que é half-open no circuit breaker?
É o estado de teste após o circuito abrir. O sistema permite uma chamada para verificar se o serviço se recuperou. Se bem-sucedida, o circuito fecha. Se falhar, volta a abrir.
Qual biblioteca usar em Spring Boot?
Resilience4j é a biblioteca mais comum e substitui o antigo Hystrix. Oferece anotações @Retry, @CircuitBreaker, @TimeLimiter e @Bulkhead.
Retry resolve falha de banco de dados?
Depende. Se a falha for um deadlock transiente, sim. Se for um índice corrompido ou tabela cheia, retry só piora, é melhor um circuit breaker com fallback para fila de reprocessamento.