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:

  1. Transações financeiras (CP): débito, crédito, estorno — exigem consistência forte
  2. 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