Sistema distribuído sempre tem componentes degradados. A pergunta é: o que acontece com SEUS clientes quando o de cima quebra? Default: cascading failure. Cliente espera; thread blocks; pool exhausts; outros clientes morrem. Catástrofe via deps.
Este módulo é resilience aplicada: timeouts, retries com backoff, jitter, idempotência (revisitada), circuit breaker, bulkhead, hedged requests, load shedding, graceful degradation, chaos engineering. Você sai sabendo desenhar serviço que fica de pé quando deps caem.
2. Teoria Hard
2.1 Failure modes
Hard failure: serviço down. Connection refused.
Slow failure: serviço lento. Timeout ou retorna após segundos.
Cascading failure: 1 dep cai → consumer A satura → consumer B (depende de A) satura.
Slow failure é pior que hard. Consumer não detecta rápido.
2.2 Timeouts
Sempre defina timeout em qualquer dep call (HTTP, DB, Redis, queue). Sem timeout, espera infinitamente em bug do upstream.
Timeouts por camada:
Connection timeout (estabelecer): segundos.
Request/socket timeout: depende do operação. p99 + buffer.
Total timeout (deadline): garantir que cliente não espera depois do que vale.
Em microservices: deadline propagation. Upstream passa "tempo restante" pra downstream.
2.3 Retries
Retry em failures transientes (network blip, momento de leader election). NUNCA em failures permanentes (4xx auth errors).
Quando retry:
Idempotent operation, ou idempotency key.
Erro transiente (5xx, 429, network).
Não tem lock no caller (pra não cascading bigger).
Como:
Backoff exponencial: 2^n * base + jitter. Sem jitter, cluster sincroniza retries (thundering herd).
Jitter: full jitter random(0, base * 2^n) é robusto.
Cap: limite total de tentativas (3-5) e delay máximo (30s, 60s).
Budget per request: total time de retries < deadline.
Retry storm: cada layer retries N vezes. 3 layers x 3 retries = 27x carga em downstream falhando. Normalmente: retries só na camada mais externa (cliente edge); inner calls fail-fast.
2.4 Idempotency revisited
Retry sem idempotência = duplicação. Problema crítico em pagamentos, mensagens, mutations.
Padrões:
HTTP idempotency-key header (Stripe, etc.).
DB unique constraints (insert + dedup via ON CONFLICT).
Outbox + dedup consumer.
Operations naturally idempotent (UPDATE x SET status='paid' WHERE id=Y).
2.5 Circuit breaker
Pattern (Michael Nygard, "Release It!"). Estados:
Closed: requests passam. Conta failures.
Open: failures excederam limite. Requests fail fast sem chamar dep.
Half-open: após cooldown, libera N requests pra testar. Se OK → closed. Se falha → open novamente.
Benefícios:
Para de bombear dep down.
Cliente recebe resposta rápida em vez de esperar timeout.
Self-healing.
Libs: opossum (Node), resilience4j (Java), gobreaker (Go), failsafe-go.
Configurar:
Threshold (% failure ou absoluto).
Window (rolling).
Cooldown.
Half-open trial size.
2.6 Bulkhead
Isole recursos por consumer/tipo de request. Falha de um não derruba outros.
Exemplos:
Pools separados de threads/conexões por dep.
Tenants críticos com pool separado.
Limite de concurrent requests por endpoint.
Em K8s: pods separados por workload (rate-sensitive vs batch).
2.7 Rate limiting (revisitado)
Server-side: protege contra abuse e overload. Cliente-side: protege downstream.
Em sistemas multi-tenant, rate limit por tenant (vimos em 02-11). Garante 1 tenant não engole capacity.
2.8 Load shedding
Quando overloaded, rejeite requests "novos" pra preservar SLO em-curso. Vimos em 03-10.
Indicators:
Queue depth.
Concurrent in-flight requests.
CPU saturation.
Event loop lag (Node).
Rejeição com 503 + Retry-After. Cliente robusto recua.
2.9 Hedged requests
Send mesma request a 2 backends; aceita primeira resposta. Reduz tail latency.
Quando: leituras idempotentes onde 99th percentile importa muito (search queries).
Cuidado: dobra carga. Faça só pra slow requests (após p95 estimado, dispare segunda).
Google publicou paper "The Tail at Scale", clássico do tema.
2.10 Fallback e graceful degradation
Quando dep falha, responder algo útil em vez de erro:
Quando consumir budget rapidamente, freeze deploys e foque em estabilidade.
Cultura SRE.
2.18 Padrões aplicados ao Node
AbortController: cancela fetch se cliente desconectou.
p-retry, async-retry libs com backoff/jitter.
opossum circuit breaker.
bottleneck rate limiter / concurrency.
Timeout via AbortSignal.timeout(ms).
2.19 Hedging requests (Tail at Scale)
Dean & Barroso (2013, "The Tail at Scale") observam: P99 latency em service distribuído explode com fan-out. Pra agregação que chama N backends, P99 do todo é dominado pelo lento.
Solution: hedged requests.
Send request a A.
Espera tempo curto (P50 do A típico).
Se A não respondeu, send request a B (replica).
Pega primeira resposta, cancela outra.
Custo: 5-10% mais requests. Benefício: P99 cai 20-40% típico.