trilha

System Design/03 - Qualidade/Performance Antipatterns2 min

Retry Storm

Perguntas-guia
  • Que problema isso resolve?
  • Quando usar / quando NÃO usar?
  • Qual o principal trade-off?
  • Como isso falha em produção?

Conceito

A reação à falha amplifica a falha. Um serviço degrada, os clientes repetem, a carga aumenta, o serviço piora — e o ciclo se realimenta.

serviço lento → clientes repetem → 3x mais carga
             → mais lento ainda  → mais repetições
             → colapso

O agravante é a multiplicação em cascata: se cada camada repete três vezes, quatro camadas encadeadas produzem 3⁴ = 81 requisições para uma única chamada original.

O efeito rebanho

Retry com intervalo fixo sincroniza os clientes: todos falham juntos, todos esperam o mesmo tempo, todos repetem no mesmo instante. O serviço recebe rajadas periódicas em vez de carga distribuída — e nunca consegue se recuperar entre elas.

É o mesmo fenômeno que impede a recuperação após uma queda: no momento em que o serviço volta, todos os clientes acumulados tentam ao mesmo tempo e o derrubam de novo.

As defesas

Defesa Efeito
Backoff exponencial Espaça as tentativas: 1 s, 2 s, 4 s, 8 s
Jitter Dessincroniza os clientes — essencial
Limite de tentativas Impede repetição infinita
Circuit Breaker Para de tentar quando é claro que não adianta
Retry budget Teto global: no máximo 10% do tráfego pode ser retry
Retry só na borda Evita a multiplicação em cascata
Não repetir 4xx Erro do cliente não melhora com repetição

Trade-offs

Jitter é a defesa mais importante e a mais esquecida. Backoff exponencial sem jitter continua sincronizado: todos os clientes esperam 1 s, depois 2 s, depois 4 s — juntos. O gráfico de carga vira uma série de picos, e o serviço não se recupera entre eles.

O trade-off do retry em si é entre resiliência e amplificação: repetir aumenta a chance de sucesso numa falha transitória e piora qualquer falha sistêmica. Como distinguir uma da outra em tempo real é difícil, a prática segura é limitar o orçamento total de retry — assim, mesmo diagnosticando errado, o dano é limitado.

A recomendação de repetir apenas na camada mais externa resolve a multiplicação: camadas internas falham rápido e propagam, e uma única camada decide se vale tentar de novo.

E vale lembrar que retry só é seguro em operação idempotente (Idempotent Operations). Repetir uma cobrança não idempotente cobra duas vezes.

Exemplo prático

# amplifica: sincronizado, sem teto, sem circuit breaker
for tentativa in range(5):
    try: return chamar()
    except: time.sleep(1)

# seguro: exponencial, com jitter, limitado
for tentativa in range(3):
    try: return breaker.chamar(operacao)
    except TransitorioError:
        if tentativa == 2: raise
        espera = min(2 ** tentativa, 30) * random.uniform(0.5, 1.5)
        time.sleep(espera)

Três diferenças importam. O backoff exponencial dá tempo real de recuperação. O jitter (uniform(0.5, 1.5)) espalha as tentativas de mil clientes por uma janela em vez de concentrá-las num instante. E o circuit breaker interrompe a insistência quando ficou claro que o serviço está fora — convertendo espera em falha rápida.

Relacionado


Parte de Performance Antipatterns · roadmap.sh/system-design

Buscar

Busca por título, seção e texto das notas