Como projetar sistemas tolerantes a falhas
1. Fundamentos da Tolerância a Falhas
Sistemas tolerantes a falhas são projetados para continuar operando corretamente mesmo quando componentes individuais falham. O princípio fundamental é que falhas são inevitáveis em sistemas distribuídos — servidores crasham, discos corrompem, redes particionam. A tolerância a falhas não elimina falhas, mas as gerencia de forma previsível.
Os quatro pilares da tolerância a falhas são: redundância (múltiplas instâncias de componentes críticos), detecção (identificação rápida de falhas), isolamento (contenção do impacto) e recuperação (retorno ao estado operacional).
Modelos de falhas comuns incluem:
- Falhas por parada: o nó simplesmente para de responder
- Falhas de omissão: mensagens são perdidas na rede
- Falhas de temporização: respostas chegam fora do prazo esperado
- Falhas bizantinas: nós se comportam de forma arbitrária ou maliciosa
Métricas críticas de confiabilidade:
- MTBF (Mean Time Between Failures): tempo médio entre falhas
- MTTR (Mean Time To Recovery): tempo médio para recuperação
- Disponibilidade: MTBF / (MTBF + MTTR), expressa como SLA/SLO/SLI
Por exemplo, um sistema com 99,99% de disponibilidade (quatro noves) pode ficar fora do ar apenas 52,56 minutos por ano.
# Exemplo: Cálculo de disponibilidade
MTBF = 720 horas (30 dias)
MTTR = 1 hora
Disponibilidade = 720 / (720 + 1) = 99,86%
2. Estratégias de Redundância e Replicação
Redundância é a base da tolerância a falhas. Existem três modos principais:
- Standby quente: réplica sincronizada em tempo real, failover instantâneo
- Standby morno: réplica com alguma defasagem, failover em segundos
- Standby frio: réplica sem dados atuais, failover demorado (minutos/horas)
Para replicação de dados, algoritmos de consenso como Raft e Paxos garantem consistência mesmo com falhas de nós. O quorum (maioria dos nós) decide operações de escrita.
Balanceamento de carga com health checks e circuit breakers distribui tráfego apenas para nós saudáveis.
# Configuração de replicação com quorum (Raft)
Nós no cluster: 5
Quorum para escrita: 3 (maioria)
Tolerância máxima de falhas: 2 nós
Disponibilidade com 2 falhas: cluster continua operacional
3. Detecção e Isolamento de Falhas
Detecção rápida é crucial. Heartbeats periódicos entre nós permitem identificar falhas. Timeouts devem ser configurados considerando latência de rede e carga do sistema.
O padrão Circuit Breaker protege serviços contra falhas em cascata. Possui três estados:
- Fechado: operação normal, chamadas passam
- Aberto: falhas detectadas, chamadas são rejeitadas imediatamente
- Semi-aberto: após timeout, permite algumas chamadas de teste
Bulkheads isolam recursos em pools separados, evitando que uma falha em um componente degrade todo o sistema.
# Implementação conceitual de Circuit Breaker
Estado: FECHADO
Contagem de falhas consecutivas: 0
Limiar para abertura: 5 falhas
Timeout para semi-aberto: 30 segundos
Quando falha >= 5:
Estado = ABERTO
Inicia timer de 30s
Quando timer expira:
Estado = SEMI_ABERTO
Permite 1 requisição de teste
Se requisição de teste falha:
Estado = ABERTO (reinicia timer)
Se requisição de teste sucede:
Estado = FECHADO (zera contagem)
4. Mecanismos de Retry e Backoff
Retentativas sem estratégia podem sobrecarregar sistemas já fragilizados. Exponential backoff com jitter é a abordagem recomendada.
# Estratégia de retry com exponential backoff e jitter
Tentativa 1: espera base = 1s
Tentativa 2: espera = 2s + jitter aleatório (0-500ms)
Tentativa 3: espera = 4s + jitter
Tentativa 4: espera = 8s + jitter
Tentativa 5: espera = 16s + jitter (máximo configurado)
Máximo de tentativas: 5
Princípios importantes:
- Idempotência: operações devem poder ser repetidas sem efeitos colaterais
- Limite máximo: definir teto de tentativas para evitar loops infinitos
- Dead letter queues: mensagens que excedem tentativas vão para fila de análise
5. Técnicas de Graceful Degradation
Quando um sistema não pode operar em capacidade total, a degradação graciosa permite manter funcionalidades essenciais.
Fallbacks fornecem alternativas quando serviços falham:
- Cache local com dados obsoletos como fallback de API externa
- Versão reduzida de funcionalidades não críticas
Throttling e rate limiting protegem contra sobrecarga:
- Limitar requisições por cliente (rate limiting)
- Reduzir gradualmente a capacidade conforme a carga aumenta (throttling)
# Exemplo de degradação funcional
Funcionalidades essenciais: login, visualização de saldo, transferências
Funcionalidades opcionais: notificações push, recomendações, chat
Sob carga alta:
- Desativar funcionalidades opcionais
- Servir dados de cache para visualizações
- Manter transferências com confirmação síncrona
6. Recuperação e Resiliência de Dados
Checkpointing salva periodicamente o estado do sistema, permitindo recuperação a partir de pontos conhecidos. Snapshots capturam o estado completo em um instante.
Write-Ahead Logging (WAL) registra operações antes de aplicá-las, garantindo que nenhuma transação seja perdida em caso de falha.
Estratégias de backup:
- Full backup: cópia completa dos dados
- Incremental backup: apenas alterações desde último backup
- Point-in-time recovery: restauração para momento específico
# Estratégia de checkpointing
Checkpoint a cada: 1000 transações
WAL armazena: últimas 5000 operações
Em caso de falha:
1. Restaurar último checkpoint
2. Reaplicar operações do WAL
3. Validar consistência dos dados
7. Testes de Resiliência e Chaos Engineering
Chaos Engineering testa a resiliência introduzindo falhas controladas em produção. Ferramentas como Chaos Monkey, Litmus e Gremlin automatizam esses testes.
Cenários comuns de injeção de falhas:
- Falha de nó (desligar servidor)
- Latência de rede (adicionar delay artificial)
- Partição de rede (isolar nós)
- Sobrecarga de CPU/memória/disco
# Plano de teste de resiliência
Cenário 1: Desligar 1 nó do cluster de 3
Resultado esperado: sistema continua operacional com 2 nós
Tempo de failover: < 5 segundos
Cenário 2: Injetar latência de 500ms em 30% das requisições
Resultado esperado: timeout e retry, sem perda de dados
Impacto máximo na latência: 2 segundos
Cenário 3: Sobrecarga de CPU em 80% por 5 minutos
Resultado esperado: degradação graciosa de funcionalidades
Funcionalidades essenciais mantidas
8. Monitoramento, Alertas e Post-Mortem
Monitoramento contínuo é essencial. As quatro métricas douradas (USE Method):
- Latência: tempo de resposta
- Erros: taxa de falhas
- Throughput: requisições por segundo
- Saturação: utilização de recursos
Alertas baseados em SLIs (indicadores) e SLOs (objetivos) com níveis de severidade:
- P0: indisponibilidade total, resposta imediata
- P1: degradação significativa, resposta em minutos
- P2: degradação menor, resposta em horas
O processo de post-mortem após incidentes deve focar em:
1. Análise de causa raiz (RCA)
2. Ações corretivas imediatas
3. Melhorias de longo prazo
4. Atualização de playbooks
# Exemplo de dashboard de monitoramento
SLI: Latência P99 de requisições
SLO: < 200ms para 99% das requisições
Alerta: P0 se P99 > 500ms por mais de 5 minutos
SLI: Taxa de erro de API
SLO: < 0,1% de requisições com erro 5xx
Alerta: P1 se taxa de erro > 1% por mais de 2 minutos
Referências
- The Chaos Engineering Book (Principles of Chaos) — Guia oficial sobre os princípios fundamentais do Chaos Engineering, incluindo experimentos controlados em produção
- Google SRE Book - Chapter 5: Eliminating Toil — Capítulo do livro de Engenharia de Confiabilidade do Google sobre práticas de tolerância a falhas
- AWS Well-Architected Framework - Reliability Pillar — Documentação da AWS sobre como projetar sistemas confiáveis e tolerantes a falhas na nuvem
- Microsoft Azure Resiliency Guidance — Guia de resiliência da Microsoft para arquiteturas tolerantes a falhas no Azure
- Netflix Tech Blog - Chaos Monkey — Artigo original da Netflix sobre o Chaos Monkey e a introdução do Chaos Engineering em produção
- Martin Fowler - Circuit Breaker Pattern — Explicação detalhada do padrão Circuit Breaker por Martin Fowler, com exemplos de implementação
- Raft Consensus Algorithm - Official Site — Documentação oficial do algoritmo Raft para replicação e consenso distribuído, com visualizações interativas