Pular para o conteúdo principal

Conceitoavançado

Tempestades de Retentativa

Visão Geral

Retentativa é a defesa mais básica contra falha transitória. Ver retentativas.

Sob falha generalizada, ela vira o problema: todos os clientes repetem ao mesmo tempo, a carga sobre o destino degradado multiplica, e o sistema não consegue se recuperar — mesmo depois de a causa original ter passado.

Esse último ponto é o que torna o fenômeno perigoso: o sistema fica preso num estado ruim que se sustenta sozinho, e sair dele exige intervenção.

Problema

Um serviço fica lento. Os clientes fazem timeout e repetem três vezes.

carga normal 1.000 req/s
serviço degrada, 3 tentativas cada
carga sobre ele 3.000 req/s ← no momento em que ele menos suporta

Com cadeias de chamadas, a multiplicação é composta:

A → B → C, cada um com 3 tentativas
1 requisição em A → 3 em B → 9 em C

Nove chamadas ao serviço mais profundo para uma requisição do usuário. Uma degradação leve em C vira sobrecarga total.

Conceitos Centrais

Repita em um nível, não em todos

A regra que evita a multiplicação composta.

Cada camada que repete multiplica pelo fator dela. Numa cadeia de quatro serviços com três tentativas cada, uma requisição pode gerar 81 chamadas ao mais profundo.

A decisão precisa ser explícita: qual camada repete? Tipicamente a mais próxima do usuário, ou a que tem o contexto para decidir se vale a pena.

E as demais precisam não repetir — o que exige que alguém verifique, porque bibliotecas de cliente frequentemente repetem por padrão sem que ninguém tenha configurado.

Ver malha de serviço — o caso em que a malha repete e a aplicação também.

Orçamento de retentativa

Limitar o número de tentativas por requisição não impede a tempestade — mil clientes com três tentativas cada continuam gerando três mil chamadas.

O controle que funciona é limitar a proporção:

orçamento: retentativas ≤ 10% das requisições iniciais
→ sob falha generalizada, a carga extra é limitada a 10%
→ o destino recebe 1.100 req/s em vez de 3.000

Quando o orçamento se esgota, novas retentativas são recusadas imediatamente. Isso preserva a capacidade de repetir para falhas isoladas — que é o caso legítimo — e impede a amplificação quando a falha é generalizada.

É o controle mais eficaz desta seção, e o menos implementado.

Variação não é opcional

Sem variação aleatória, clientes que falharam juntos repetem juntos:

sem variação falha em massa → todos repetem em 1s, 2s, 4s → três picos sincronizados
com variação as tentativas se espalham no tempo

Ver backoff. Espera crescente sem variação é pior que não ter espera nenhuma, porque cria a ilusão de proteção enquanto mantém a sincronização.

Não repita o que não é retentável

Repetir uma requisição inválida gasta capacidade e não pode dar certo.

retentável timeout, erro de servidor, indisponibilidade, excesso de requisições
não retentável requisição malformada, não autorizado, não encontrado, conflito

O caso de excesso de requisições merece nota: ele é retentável, e o destino frequentemente informa quanto esperar. Ignorar essa informação e repetir imediatamente é o que transforma limite de taxa em tempestade.

Recuperação metaestável

O fenômeno que torna tudo isso grave.

Um sistema entra num estado em que a própria carga de retentativa mantém a degradação — mesmo depois de a causa original ter desaparecido.

1. causa original: banco lento por 30 segundos
2. clientes repetem, carga triplica
3. banco continua saturado pela carga de retentativa
4. causa original passou; o estado ruim persiste
5. sem intervenção, não sai sozinho

Sair exige reduzir a carga: rejeitar requisições, desligar clientes, esvaziar filas. Aumentar capacidade frequentemente não resolve, porque a carga de retentativa cresce para ocupá-la.

Reconhecer esse padrão durante um incidente é o que evita horas tentando escalar.

Fila é amplificador silencioso

Quando a retentativa acontece numa fila, a amplificação não é visível como carga — é visível como profundidade crescente.

Uma mensagem que falha e volta para a fila é processada de novo, falha de novo, e consome capacidade indefinidamente. Ver poison messages e dead-letter queues.

Limite de tentativas com destino final não é detalhe — é o que impede uma mensagem consumir a capacidade de toda a fila.

Modelo Mental

Retentativa ajuda contra falha isolada e amplifica falha generalizada. O controle não é o número de tentativas — é a proporção delas.

Quando Usar

Retentativa é adequada quando:

  • A falha é plausivelmente transitória.
  • A operação é idempotente.
  • Há orçamento definido.
  • Existe espera com variação.
  • Apenas uma camada repete.

Quando Não Usar

Em várias camadas.

Sem orçamento.

Sem variação.

Para erros permanentes.

Sem idempotência.

Como resposta a sobrecarga. Se o destino está saturado, repetir piora. Ver circuit breaker.

Alternativas

  • Circuit breaker — parar de tentar quando a falha é persistente. Complementa a retentativa.
  • Degradação graciosa — responder sem a dependência.
  • Fila com atraso — deixar a infraestrutura cuidar da repetição, com controle.
  • Falhar rápido — quando o orçamento de tempo do chamador não comporta espera.

Trade-offs

Com retentativaSem
Absorve falha transitóriaPropaga
Amplifica falha generalizadaNão amplifica
Latência maior no caso ruimFalha imediata
Exige idempotênciaDispensa
Com orçamentoSem
Amplificação limitadaMultiplicação livre
Algumas retentativas recusadasTodas tentadas
Recuperação possívelEstado metaestável

Modos de Falha

Amplificação composta. Cadeia com retentativa em cada nível.

Estado metaestável. A carga de retentativa sustenta a degradação.

Picos sincronizados. Sem variação.

Retentativa de erro permanente. Capacidade gasta sem chance de sucesso.

Fila crescendo por reprocessamento.

Duplicação de efeito. Sem idempotência.

Retentativa contra limite de taxa. Ignora o prazo informado e piora.

Erros Comuns

Não verificar se camadas intermediárias repetem. Cliente HTTP, service mesh e balanceador costumam repetir por padrão. Somadas às tentativas da aplicação, multiplicam a carga sem que ninguém tenha configurado isso explicitamente.

Não definir orçamento. Sem um teto de proporção de retentativas sobre o total de chamadas, elas crescem justamente quando a taxa de erro sobe — que é quando o sistema menos aguenta.

Espera crescente sem variação. Todos os clientes que falharam juntos esperam o mesmo tempo e voltam juntos. A onda se repete em intervalos cada vez maiores e o serviço nunca estabiliza.

Repetir tudo indistintamente. Erro de validação e recusa por sobrecarga não melhoram com insistência; repeti-los consome capacidade e adia a recuperação.

Não limitar tentativas em fila. Mensagem que volta indefinidamente ocupa consumidores em trabalho que nunca vai concluir, e a fila de trabalho útil para atrás dela.

Escalar capacidade durante estado metaestável. Quando a carga é dominada por retentativas, mais capacidade atende mais retentativas e realimenta o ciclo. É preciso cortar a fonte antes de aumentar a vazão.

Exemplo Real

Uma plataforma de mobilidade teve uma indisponibilidade de 3 horas que começou com uma degradação de 40 segundos.

A sequência:

Origem. Uma consulta lenta deixou o banco de motoristas com latência elevada por 40 segundos.

Amplificação. O aplicativo repetia 3 vezes. O gateway repetia 2 vezes. O serviço intermediário repetia 3 vezes. A carga sobre o banco passou de 800 para cerca de 14 mil requisições por segundo.

Metaestabilidade. A degradação original passou em 40 segundos. A carga de retentativa manteve o banco saturado por horas.

Escalada ineficaz. O time dobrou as instâncias de aplicação. Isso piorou — mais instâncias significavam mais clientes repetindo contra o mesmo banco.

Saída. Após 3 horas, o time desligou o tráfego do aplicativo por 5 minutos. Sem carga, o banco se recuperou em segundos. O tráfego foi religado gradualmente.

A investigação encontrou que ninguém sabia que havia três camadas repetindo — cada configuração tinha sido feita por um time diferente, em momentos diferentes, todas razoáveis isoladamente.

As correções:

Retentativa em uma camada só. Apenas o cliente móvel repete, com espera crescente e variação. Gateway e serviços intermediários pararam de repetir — verificado por teste automatizado que falha se uma biblioteca de cliente repetir por padrão.

Orçamento de 10% por serviço, com recusa imediata acima disso.

Circuit breaker no acesso ao banco de motoristas, com degradação graciosa: sem o serviço, o aplicativo mostra motoristas do cache com aviso de atraso.

Descarte de carga no gateway: acima de um limite, requisições são recusadas com prazo sugerido, em vez de enfileirar.

Procedimento de recuperação documentado, incluindo o passo que resolveu — reduzir a carga a zero e religar gradualmente. Ele foi contraintuitivo na hora, e é o que funciona em estado metaestável.

Nos dezoito meses seguintes, três degradações semelhantes se recuperaram sozinhas em menos de dois minutos.

A conclusão registrada: a decisão que mais prolongou o incidente foi dobrar a capacidade. Ela era a reação natural, e alimentava exatamente o mecanismo que mantinha a degradação.

Conceitos Relacionados

Exercício Prático

Trace uma requisição do seu sistema e conte quantas camadas repetem — incluindo bibliotecas de cliente, gateway e malha de serviço.

Multiplique os fatores. Esse é o número de chamadas que uma requisição pode gerar no serviço mais profundo durante uma degradação.

Perguntas de Entrevista

  • Por que orçamento de proporção funciona melhor que limite de tentativas?
  • O que é estado metaestável e por que escalar não resolve?
  • Por que espera crescente sem variação é pior que nenhuma espera?

Para Aprofundar

  • Bronson, Nathan et al. Metastable Failures in Distributed Systems. HotOS, 2021.
  • Nygard, Michael. Release It!. 2ª ed. Pragmatic Bookshelf, 2018.
  • Brooker, Marc. Timeouts, Retries, and Backoff with Jitter. Amazon Builders' Library, 2019.
Terminou de ler este documento?Seu progresso fica salvo apenas neste navegador.