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