Consistência vs disponibilidade: aplicando o teorema CAP em decisões reais
1. Fundamentos do Teorema CAP e sua relevância prática
O teorema CAP, formulado por Eric Brewer em 2000, estabelece que um sistema distribuído pode oferecer no máximo duas das três propriedades simultaneamente: Consistência (todos os nós veem os mesmos dados ao mesmo tempo), Disponibilidade (cada requisição recebe uma resposta, mesmo que não seja a mais recente) e Tolerância a Partições (o sistema continua operando mesmo com falhas de comunicação entre nós).
Na prática, a tolerância a partições não é opcional. Redes falham, servidores caem, cabos são rompidos. Em sistemas distribuídos modernos, partições de rede são inevitáveis. Portanto, a escolha real se reduz a: CP (consistência + tolerância a partições, sacrificando disponibilidade) ou AP (disponibilidade + tolerância a partições, sacrificando consistência).
A decisão entre CP e AP não é binária para todo o sistema — ela pode (e deve) variar por funcionalidade, baseada em requisitos de negócio específicos.
2. Consistência forte: quando e como aplicá-la
Sistemas CP garantem que todos os nós retornem o estado mais recente dos dados. Isso é crítico em cenários como:
- Transações financeiras (saldo de conta, transferências)
- Sistemas de reserva (assentos de avião, quartos de hotel)
- Controle de estoque com limites rígidos
O custo é latência maior e indisponibilidade temporária durante partições. Quando um nó não consegue garantir consistência, ele simplesmente recusa a requisição.
Exemplo prático: banco de dados relacional com replicação síncrona
Configuração: PostgreSQL com replicação síncrona (quórum de confirmação)
Parâmetros:
- synchronous_commit = on
- synchronous_standby_names = 'standby1, standby2'
Fluxo:
1. Cliente envia INSERT na tabela "transacoes"
2. Nó primário grava o WAL (Write-Ahead Log)
3. Primário aguarda confirmação de pelo menos 1 standby
4. Apenas após confirmação, retorna sucesso ao cliente
Comportamento durante partição:
- Se standby1 cair, primário aguarda timeout (configurável)
- Se timeout estourar, primário pode:
- Recusar escritas (CP puro)
- Degradar para assíncrono (AP temporário)
3. Disponibilidade máxima: estratégias para sistemas AP
Sistemas AP priorizam responder sempre, mesmo que com dados desatualizados. Isso é essencial para:
- Redes sociais (feed de notícias, likes)
- E-commerce de alta escala (catálogo de produtos)
- IoT e sensoriamento contínuo
A consistência eventual permite que diferentes nós tenham visões diferentes dos dados por um período, até que a reconciliação ocorra.
Exemplo prático: cache distribuído com replicação assíncrona
Configuração: Redis Cluster com replicação assíncrona
Parâmetros:
- cluster-enabled yes
- cluster-node-timeout 15000
- replica-read-only yes
- replica-serve-stale-data yes
Fluxo de escrita:
1. Cliente envia SET "usuario:123" "nome:Maria" para nó A (primário)
2. Nó A responde imediatamente OK ao cliente
3. Em background, nó A replica para nó B (réplica)
4. Se B cair durante replicação, A continua aceitando escritas
Conflito durante partição:
- Nó A e nó C ficam isolados entre si
- Cliente 1 escreve "email:maria@x.com" no nó A
- Cliente 2 escreve "email:maria@y.com" no nó C
- Quando rede é restaurada, conflito precisa ser resolvido:
- Última escrita vence (LWW)
- Fusão manual (CRDTs)
- Valor com timestamp maior é mantido
4. Zonas cinzentas: consistência ajustável e compromissos intermediários
Nem tudo é preto no branco. Modelos de consistência intermediária permitem ajustar o trade-off:
- Consistência causal: operações relacionadas causalmente são vistas na mesma ordem por todos os nós
- Consistência de leitura após escrita: um cliente sempre lê o que acabou de escrever
- Consistência monotônica de leitura: leituras subsequentes nunca retornam dados mais antigos
Quóruns configuráveis (R + W > N) permitem sintonizar o ponto ideal entre CP e AP.
Exemplo prático: configuração de quóruns no Cassandra
Keyspace: "dados_usuario"
Fator de replicação (N) = 3
Opções de consistência por operação:
Caso 1: Consistência forte (CP)
- WRITE: ALL (W=3)
- READ: ALL (R=3)
- R+W = 6 > N=3 → leitura sempre vê a escrita mais recente
- Custo: latência alta, indisponível se 1 nó cair
Caso 2: Equilíbrio (consistência ajustável)
- WRITE: QUORUM (W=2)
- READ: QUORUM (R=2)
- R+W = 4 > N=3 → consistência forte mantida
- Tolerância: 1 nó pode falhar sem impacto
Caso 3: Máxima disponibilidade (AP)
- WRITE: ONE (W=1)
- READ: ONE (R=1)
- R+W = 2 < N=3 → leitura pode retornar dados obsoletos
- Tolerância: 2 nós podem falhar
Comando para configurar consistência por consulta:
```text
cqlsh> CONSISTENCY QUORUM;
cqlsh> SELECT * FROM dados_usuario.perfil WHERE id = '123';
## 5. Tomada de decisão baseada em requisitos de negócio
A escolha entre CP e AP deve ser orientada por requisitos não funcionais:
| Requisito | CP | AP |
|-----------|----|----|
| Latência máxima (p99) | 50ms | 5ms |
| Tolerância a falhas | 1 nó | 3 nós |
| Atomicidade | Sim | Não |
| Consistência | Imediata | Eventual |
**Matriz de decisão por funcionalidade**:
```text
Funcionalidade: "Transferência bancária"
- Requer: consistência forte, atomicidade, rollback
- Escolha: CP (banco relacional com replicação síncrona)
- Custo: latência ~200ms, indisponível durante partição
Funcionalidade: "Notificação de transferência"
- Requer: disponibilidade, baixa latência
- Escolha: AP (fila de mensagens com consistência eventual)
- Custo: notificações podem chegar fora de ordem ou duplicadas
Funcionalidade: "Consulta de saldo"
- Requer: consistência de leitura após escrita (para o próprio cliente)
- Escolha: Consistência ajustável (QUORUM no Cassandra)
- Custo: latência moderada, tolerância a 1 falha
6. Padrões arquiteturais para mitigar trade-offs do CAP
Sagas e compensação: sequência de transações locais com ações compensatórias em caso de falha. Permite consistência sem bloqueio global.
Saga para "Compra com cartão de crédito":
1. Reservar estoque (CP local)
2. Debitar cartão (CP local)
3. Confirmar pedido (CP local)
4. Se passo 2 falhar → compensar passo 1 (devolver estoque)
Event Sourcing + CQRS: separa comandos (escritas consistentes) de consultas (leituras disponíveis). A escrita é sempre CP, a leitura pode ser AP.
Arquitetura:
- Command side: banco relacional (CP) para validação e gravação de eventos
- Query side: cache Redis (AP) para consultas rápidas
- Sincronização: stream de eventos (Kafka) do command para query
Cache de borda com consistência ajustável: CDNs e caches geograficamente distribuídos podem operar AP para leituras, enquanto escritas vão para a origem CP.
7. Estudo de caso real: sistema de pagamento com requisitos mistos
Cenário: Plataforma de pagamentos online com duas funcionalidades conflitantes:
- Transações financeiras (CP): débito, crédito, estorno — exigem consistência forte
- Notificações (AP): email, SMS, push — exigem disponibilidade máxima
Solução híbrida:
Arquitetura implementada:
Camada CP:
- Banco PostgreSQL com replicação síncrona (3 nós)
- Quórum de escrita: ALL
- Quórum de leitura: ALL (para saldos)
- Timeout de partição: 5 segundos (depois recusa)
Camada AP:
- Fila RabbitMQ com confirmação assíncrona
- Workers consomem eventos da fila
- Notificações podem ser entregues até 5 minutos após transação
- Tolerância: fila pode acumular mensagens durante partição
Gateway de integração:
- Transação financeira → rota CP (PostgreSQL)
- Após confirmação → evento publicado na fila AP
- Se fila falhar, transação não é revertida (evento é retentado)
Lições aprendidas:
- Monitorar tempo de recuperação de partições (RTO) — definir metas por funcionalidade
- Medir divergência de dados entre nós — aceitar até X segundos de atraso para dados AP
- Implementar circuit breakers para evitar cascata de falhas entre camadas CP e AP
- Testar cenários de partição regularmente (simular falha de nó, latência de rede)
Métricas de monitoramento:
Métrica Alerta
-------------------------------------------
Latência p99 de escrita CP > 500ms
Taxa de rejeição por partição > 0.1%
Atraso de replicação AP > 30s
Número de conflitos resolvidos > 10/min
Referências
- Teorema CAP explicado pela AWS — Comparação entre ACID e BASE com exemplos práticos de sistemas distribuídos
- Documentação oficial do Apache Cassandra sobre consistência — Detalhamento dos níveis de consistência e quóruns configuráveis
- Artigo "CAP Twelve Years Later" por Eric Brewer — Revisão do teorema CAP com novas perspectivas sobre consistência ajustável
- Documentação do PostgreSQL sobre replicação síncrona — Configuração de quóruns de confirmação e comportamento durante partições
- Padrão Saga na documentação da Microsoft — Implementação de sagas para consistência em sistemas distribuídos
- Event Sourcing e CQRS no Martin Fowler — Padrões arquiteturais para separar leituras consistentes de escritas disponíveis
- Documentação do Redis Cluster sobre replicação assíncrona — Estratégias de replicação e resolução de conflitos em sistemas AP