O Dilema da Computação — Teorema CAP, CP vs AP e PACELC
Quando começamos a estudar sistemas distribuídos, existe uma pergunta que aparece constantemente:
É possível construir um sistema que seja consistente, disponível e tolerante a falhas de rede ao mesmo tempo?
A resposta, de acordo com o Teorema CAP, é que, diante de uma partição de rede, não é possível garantir simultaneamente as três propriedades.
Esse conceito foi proposto por Eric Brewer em 2000 e posteriormente provado formalmente por Seth Gilbert e Nancy Lynch em 2002.
O CAP é um dos conceitos fundamentais de System Design, porque ajuda a entender por que bancos de dados distribuídos precisam fazer escolhas difíceis.
Porém, existe um problema.
Muitas explicações apresentam o CAP como:
Escolha 2:
C — Consistency
A — Availability
P — Partition Tolerance
Essa explicação é didática, mas pode ser enganosa.
Na prática, o verdadeiro dilema aparece quando ocorre uma partição de rede.
É nesse momento que precisamos escolher entre:
CP
ou:
AP
E entender essa diferença é fundamental para projetar sistemas distribuídos.
1. O que é o Teorema CAP?
CAP representa três propriedades:
C — Consistency
A — Availability
P — Partition Tolerance
Em português:
Consistência
Disponibilidade
Tolerância a Partições
A ideia central é:
Em um sistema distribuído, quando ocorre uma partição de rede, não é possível garantir simultaneamente consistência forte e disponibilidade total.
Isso significa que:
P acontece
│
▼
Escolha entre
│
├── C
│
└── A
Ou seja:
CP
ou:
AP
2. Por que o CAP existe?
Para entender o CAP, precisamos primeiro entender que sistemas distribuídos dependem de comunicação entre máquinas.
Imagine:
DATABASE
│
┌────────┴────────┐
▼ ▼
Server A Server B
Os servidores precisam conversar:
Server A ◄────────► Server B
Mas redes podem falhar.
Podemos ter:
Server A X Server B
A conexão entre os dois servidores foi perdida.
Agora temos:
┌─────────────┐ X ┌─────────────┐
│ Server A │ │ Server B │
│ │ │ │
│ Dados A │ │ Dados B │
└─────────────┘ └─────────────┘
Os dois servidores continuam funcionando.
Porém, eles não conseguem mais se comunicar.
Isso é uma:
Network Partition
Ou simplesmente:
Partição de rede.
3. O que significa Consistency?
No contexto do CAP, Consistency não significa simplesmente "os dados estão corretos".
O conceito é mais específico.
Uma definição simplificada é:
Toda leitura recebe o valor mais recente ou um erro.
Imagine:
Banco
Saldo = R$ 100
Executamos:
UPDATE saldo
SET saldo = 50
Depois disso:
WRITE → saldo = 50
Se o sistema possui consistência forte:
READ
│
▼
R$ 50
Não deveríamos receber:
R$ 100
de uma réplica que ainda não foi atualizada.
A consistência forte tenta garantir que os clientes tenham uma visão coerente dos dados.
4. O que significa Availability?
Availability significa que toda requisição recebida por um nó disponível deve receber uma resposta.
Imagine:
Server A
│
▼
Request
│
▼
Resposta
Mesmo que outro servidor esteja indisponível:
Server A X Server B
│ │
▼ X
Resposta Indisponível
O Server A continua respondendo.
O sistema prioriza:
Continuar funcionando.
Mesmo que isso signifique que diferentes clientes possam temporariamente observar dados diferentes.
5. O que significa Partition Tolerance?
Partition Tolerance significa que o sistema consegue continuar operando mesmo quando existe uma falha de comunicação entre partes da rede.
Imagine:
REDE
│
X
│
┌──────┴──────┐
│ │
▼ ▼
Server A Server B
A comunicação entre os dois lados falhou.
Um sistema tolerante a partições consegue continuar funcionando de alguma maneira.
Isso é extremamente importante porque, em sistemas distribuídos reais:
Partições de rede são inevitáveis.
Cabos falham.
Switches falham.
Roteadores falham.
Datacenters ficam isolados.
Regiões inteiras podem perder conectividade.
Portanto, na prática, sistemas distribuídos precisam considerar P.
6. O famoso "escolha 2 de 3"
É comum encontrar este diagrama:
Consistency
/\
/ \
/ \
/ \
/ \
/ \
/ \
/ \
Availability ─────────── Partition
E ouvir:
"Você precisa escolher dois dos três."
Essa explicação é útil para iniciantes, mas é uma simplificação enganosa.
O problema é que ela sugere que podemos simplesmente escolher:
C + A
e abandonar:
P
Mas em um sistema distribuído real, não podemos simplesmente escolher ignorar partições.
Quando dois servidores não conseguem se comunicar, precisamos lidar com essa situação.
Por isso, a discussão real é:
Quando ocorre uma partição:
CP
ou
AP
O P é praticamente obrigatório em sistemas distribuídos.
A verdadeira escolha acontece entre:
Consistência
vs
Disponibilidade
durante uma partição.
7. A situação crítica: Network Partition
Imagine duas réplicas:
REDE
│
┌──────┴──────┐
▼ ▼
Replica A Replica B
Ambas possuem:
Saldo = R$ 100
Agora ocorre uma partição:
Replica A X Replica B
Um cliente conecta na Replica A:
Cliente A
│
▼
Replica A
│
▼
Saldo = R$ 100
Outro cliente conecta na Replica B:
Cliente B
│
▼
Replica B
│
▼
Saldo = R$ 100
Agora:
Cliente A
│
▼
Replica A
│
▼
- R$ 50
A Replica A agora possui:
Saldo = R$ 50
Enquanto a Replica B ainda possui:
Saldo = R$ 100
A rede continua particionada.
Agora chega outro cliente:
Cliente C
│
▼
Replica B
O que o sistema deve fazer?
A Replica B pode:
Aceitar a operação
Ou:
Recusar a operação
Aqui está o dilema.
8. Escolha CP
CP significa:
Consistency
+
Partition Tolerance
Quando ocorre uma partição, o sistema prefere manter consistência, mesmo que precise rejeitar algumas requisições.
Imagine:
Replica A X Replica B
O sistema não consegue confirmar o estado global.
Então:
Request
│
▼
Não é possível garantir consistência
│
▼
Rejeitar
Resultado:
Consistência → Mantida
Disponibilidade → Reduzida
Em outras palavras:
É melhor retornar um erro do que retornar uma resposta potencialmente incorreta.
9. Exemplo CP
Imagine um sistema bancário.
Temos:
Conta
Saldo = R$ 1.000
O usuário tenta transferir:
R$ 1.000
Agora existe uma partição:
Datacenter A X Datacenter B
Se o sistema não consegue confirmar o estado da conta, ele pode decidir:
TRANSFERÊNCIA
│
▼
Não é possível garantir consistência
│
▼
ERRO
O usuário recebe:
Operação temporariamente indisponível.
Isso é muito melhor do que permitir:
Conta A
Saldo = -R$ 1.000
ou:
Transferência duplicada
Nesse caso, consistência é mais importante do que disponibilidade.
10. Escolha AP
AP significa:
Availability
+
Partition Tolerance
Nesse modelo, durante uma partição, o sistema continua respondendo.
Mesmo que diferentes nós tenham informações diferentes.
Imagine:
Replica A X Replica B
A Replica A recebe:
WRITE
Produto X
Estoque = 10
A Replica B ainda possui:
Estoque = 11
Mesmo assim, ela pode continuar respondendo.
O sistema prioriza:
Disponibilidade
em vez de consistência imediata.
Quando a rede voltar:
Replica A
│
│ sincronização
▼
Replica B
O sistema tenta reconciliar os dados.
Isso é conhecido como:
Eventual Consistency
11. Consistência Forte
Em um sistema de consistência forte:
WRITE
│
▼
Atualização confirmada
│
▼
READ
│
▼
Novo valor
Exemplo:
Valor inicial = 100
Executamos:
WRITE = 50
Depois:
READ
Resultado:
50
O sistema garante que as leituras posteriores observem a atualização confirmada.
12. Consistência Eventual
Na consistência eventual, diferentes réplicas podem temporariamente possuir valores diferentes.
Imagine:
WRITE = 50
│
▼
Primary
│
┌────────┴────────┐
▼ ▼
Replica A Replica B
│ │
50 100
Durante algum tempo:
Replica A = 50
Replica B = 100
Depois:
Replica A = 50
Replica B = 50
O sistema convergiu.
A ideia é:
Se nenhuma nova atualização ocorrer, eventualmente todas as réplicas convergirão para o mesmo estado.
13. Eventual Consistency não significa "dados errados"
Isso é importante.
Eventual consistency não significa que o sistema é inconsistente para sempre.
Significa que:
Agora:
Replica A = X
Replica B = Y
Mas:
Futuro:
Replica A = X
Replica B = X
Existe uma janela de inconsistência.
Essa janela pode durar:
Milissegundos
Segundos
Minutos
Dependendo da arquitetura.
14. Split-Brain
Um problema particularmente perigoso é o chamado:
Split-Brain
Imagine um cluster:
Cluster
│
┌──────┴──────┐
▼ ▼
Node A Node B
Ocorre uma partição:
Node A X Node B
Agora os dois lados podem acreditar que estão autorizados a operar.
Por exemplo:
Node A:
"Eu sou o líder!"
Node B:
"Não, eu sou o líder!"
Temos:
SPLIT-BRAIN
┌─────────┐
│ Node A │
│ LEADER │
└─────────┘
X
┌─────────┐
│ Node B │
│ LEADER │
└─────────┘
Agora ambos aceitam escritas.
Podemos ter:
Node A
User 123 → saldo 100
Node B
User 123 → saldo 50
Quando a rede volta:
????
Qual é o valor correto?
Esse é um dos motivos pelos quais sistemas distribuídos precisam de mecanismos de:
- Eleição de líder
- Quorum
- Consensus
- Fencing
- Detecção de falhas
15. Quorum
Uma técnica comum para evitar problemas de consistência é utilizar quorum.
Imagine:
3 réplicas
Podemos exigir:
2 confirmações
Para uma operação ser considerada válida.
Por exemplo:
Replica A ✓
Replica B ✓
Replica C ✗
Temos:
2 de 3
A operação pode ser confirmada.
Isso reduz o risco de dois lados isolados acreditarem que possuem autoridade suficiente.
16. O verdadeiro trade-off: CP vs AP
Quando ocorre uma partição:
NETWORK PARTITION
│
▼
┌──────┴──────┐
│ │
▼ ▼
CP AP
CP
Prioriza:
Consistência
Se não conseguir garantir consistência:
Retorna erro
Exemplo conceitual:
Request
│
▼
Não posso confirmar
│
▼
ERROR
AP
Prioriza:
Disponibilidade
Mesmo sem comunicação completa:
Request
│
▼
Processa
│
▼
Sincroniza depois
Pode resultar em:
Eventual Consistency
17. MongoDB
O MongoDB é um banco orientado a documentos.
Sua arquitetura moderna utiliza:
Replica Set
Por exemplo:
PRIMARY
│
┌────────┴────────┐
▼ ▼
SECONDARY SECONDARY
Em condições normais, temos:
Write
│
▼
Primary
As operações são replicadas.
Se o Primary falhar:
Primary
X
Os nós restantes podem realizar uma eleição:
Secondary
│
▼
Novo Primary
O MongoDB permite diferentes níveis de preocupação com consistência e durabilidade através de configurações como:
Read Concern
Write Concern
Read Preference
Portanto, classificá-lo simplesmente como:
MongoDB = CP
é uma simplificação.
O comportamento depende da configuração e do padrão de operação.
Em muitos cenários de replicação, o MongoDB prioriza consistência e eleição de um líder, mas oferece opções que permitem diferentes trade-offs.
O ponto importante é:
Sistemas reais não são simplesmente "CAP = X". O comportamento depende da configuração e da operação analisada.
18. Cassandra
O Cassandra foi projetado para alta disponibilidade e escalabilidade horizontal.
Sua arquitetura é distribuída:
Node 1
/ \
/ \
Node 2 ───── Node 3
Ele não depende de um único Primary da mesma forma que arquiteturas tradicionais.
Durante uma partição, o Cassandra pode continuar aceitando operações em diferentes nós.
Isso favorece:
Availability
e:
Partition Tolerance
Portanto, é frequentemente associado ao modelo:
AP
O Cassandra utiliza mecanismos como:
- Replicação
- Quorum
- Consistency Levels
- Eventual Consistency
Por exemplo:
ONE
QUORUM
ALL
O nível escolhido altera o trade-off entre:
Latência
Consistência
Disponibilidade
19. DynamoDB
O Amazon DynamoDB é um banco distribuído projetado para alta disponibilidade e escalabilidade.
Ele utiliza replicação interna e permite diferentes comportamentos de leitura.
Podemos ter:
Eventually Consistent Reads
ou:
Strongly Consistent Reads
Isso significa que o DynamoDB não deve ser reduzido a:
DynamoDB = AP
O comportamento depende do tipo de operação e da configuração.
A arquitetura do DynamoDB prioriza fortemente:
Alta disponibilidade
Escalabilidade
Baixa latência
Enquanto oferece opções de consistência para diferentes necessidades.
20. Spanner
O Google Spanner é um exemplo interessante.
Ele foi projetado para oferecer:
Escalabilidade global
+
Consistência forte
Isso é extremamente difícil em sistemas distribuídos geograficamente.
O Spanner utiliza mecanismos sofisticados de sincronização e consenso.
Uma característica conhecida é o uso do:
TrueTime
que permite ao sistema trabalhar com uma noção de tempo global suficientemente precisa para suportar ordenação de eventos e transações distribuídas.
O Spanner mostra que:
CAP não diz que consistência forte e disponibilidade são impossíveis em sistemas distribuídos.
O que o CAP diz é que, durante uma partição de rede, você não consegue garantir simultaneamente as propriedades de consistência e disponibilidade definidas pelo teorema.
Um sistema pode oferecer:
Consistência forte
+
Escalabilidade global
mas ainda precisar sacrificar disponibilidade durante uma partição.
Portanto, o Spanner é frequentemente associado a:
CP
21. CouchDB
O CouchDB possui uma arquitetura baseada em replicação e sincronização.
Um de seus diferenciais históricos é permitir que diferentes nós trabalhem de maneira relativamente independente e sincronizem posteriormente.
Imagine:
Node A
│
X
│
Node B
Ambos podem continuar operando.
Depois:
Node A
│
│ Sync
▼
Node B
Isso favorece:
Availability
+
Partition Tolerance
E utiliza conceitos relacionados à:
Eventual Consistency
Esse modelo pode ser útil para aplicações que precisam funcionar mesmo quando os dispositivos estão temporariamente offline.
22. Comparando exemplos
Uma visão simplificada:
SistemaTendênciaCaracterísticaMongoDBCP em muitos cenáriosReplica Sets e eleiçãoCassandraAPAlta disponibilidade e distribuiçãoDynamoDBAP/ajustávelLeituras consistentes ou eventuaisSpannerCPConsistência forte em escala globalCouchDBAPReplicação e sincronização
Essa tabela não deve ser interpretada como uma classificação absoluta.
O comportamento real depende de:
- Configuração
- Tipo de operação
- Modelo de replicação
- Nível de consistência
- Quorum
- Topologia
- Tipo de falha
23. CAP não é sobre "qual banco é melhor"
Imagine:
Banco A
CP
Banco B
AP
Isso não significa:
A é melhor
B é pior
Significa apenas:
Problema diferente
│
▼
Trade-off diferente
Se temos:
Sistema financeiro
podemos preferir:
CP
Se temos:
Rede social
podemos aceitar:
AP
Por exemplo:
Número de curtidas
Pode ser eventualmente consistente.
Mas:
Transferência bancária
provavelmente exige garantias muito mais fortes.
24. PACELC
O CAP explica o que acontece:
DURANTE UMA PARTIÇÃO
Mas existe uma pergunta importante:
E quando a rede está funcionando normalmente?
Mesmo sem uma partição, sistemas distribuídos ainda precisam escolher entre:
Latência
e:
Consistência
É aí que entra o:
PACELC
A sigla significa:
P — Partition
A — Availability
C — Consistency
E — Else
L — Latency
C — Consistency
A ideia pode ser resumida assim:
Se houver Partição (P)
│
└── escolher entre A ou C
Caso contrário (E)
│
└── escolher entre L ou C
Ou:
P → A ou C
ELSE → L ou C
25. Por que o PACELC é importante?
Imagine dois sistemas.
Ambos são:
CP
Ou seja, ambos priorizam consistência durante uma partição.
Mas, em condições normais, eles podem ter comportamentos diferentes.
Sistema A:
Mais consistente
Mais lento
Sistema B:
Menos latência
Mais complexo para manter consistência
O CAP não explica completamente essa diferença.
O PACELC explica.
Sistema distribuído
PARTIÇÃO?
│
┌────┴────┐
SIM NÃO
│ │
▼ ▼
A ou C L ou C
26. Exemplo prático do PACELC
Imagine uma aplicação global:
Brasil
│
▼
Servidor Brasil
EUA
│
▼
Servidor EUA
Os dados precisam ser replicados:
Brasil ─────────► EUA
Agora temos uma escolha.
Podemos esperar confirmação de ambos:
Brasil
│
▼
EUA
│
▼
Confirmação
Isso aumenta a consistência.
Porém:
Latência ↑
Ou podemos responder imediatamente:
Brasil
│
▼
Resposta
E replicar depois:
Brasil
│
│ async
▼
EUA
Isso reduz:
Latência ↓
Mas pode resultar em:
Consistência eventual
Esse é exatamente o tipo de trade-off que o PACELC ajuda a visualizar.
27. CAP vs PACELC
CAP:
Durante uma partição:
C
vs
A
PACELC:
Durante uma partição:
C
vs
A
Sem partição:
C
vs
L
Podemos representar:
SISTEMA DISTRIBUÍDO
│
┌────────┴────────┐
│ │
PARTIÇÃO NORMAL
│ │
▼ ▼
C vs A C vs L
Essa é uma visão muito mais completa.
28. O verdadeiro dilema da computação distribuída
O grande insight é que:
Distribuir dados aumenta a capacidade do sistema, mas também aumenta a complexidade.
Quando distribuímos:
Server 1
Server 2
Server 3
ganhamos:
Escalabilidade
Disponibilidade
Tolerância a falhas
Mas precisamos lidar com:
Latência
Consistência
Partições
Sincronização
Conflitos
Eleição de líder
Consensus
Cada benefício vem acompanhado de um custo.
29. Uma forma prática de pensar
Ao projetar um sistema, faça estas perguntas:
1. O que acontece se a rede falhar?
2. Posso continuar respondendo?
3. Posso aceitar dados temporariamente inconsistentes?
4. Preciso de consistência forte?
5. Qual é a latência aceitável?
6. O usuário prefere erro ou dado temporariamente antigo?
7. Posso resolver conflitos posteriormente?
Essas respostas ajudam a definir:
CP
ou:
AP
E depois:
PACELC
30. Exemplos de decisões
Sistema bancário
Consistência > Disponibilidade
Preferência:
CP
Se não podemos garantir o saldo correto:
Retornamos erro
Rede social
Disponibilidade > Consistência imediata
Podemos aceitar:
Eventual Consistency
Uma curtida pode demorar alguns segundos para aparecer em todos os lugares.
Contador de visualizações
Podemos aceitar:
AP
Uma pequena diferença entre réplicas não é crítica.
Sistema de pagamentos
Precisamos de:
Consistência forte
Idempotência
Transações
O sistema não pode processar duas vezes o mesmo pagamento.
Aplicação offline-first
Podemos aceitar:
AP
+
Eventual Consistency
O dispositivo continua funcionando offline.
Quando a conexão volta:
Sincronização
│
▼
Resolução de conflitos
31. Resumo visual
CAP
│
▼
Ocorreu partição?
│
┌──────┴──────┐
SIM NÃO
│ │
▼ ▼
C vs A CAP não
explica tudo
│
▼
PACELC
│
▼
C vs L
32. Resumo final
CAP
O Teorema CAP afirma que:
Durante uma partição de rede, um sistema distribuído não consegue garantir simultaneamente consistência e disponibilidade.
CP
Prioriza:
Consistency
+
Partition Tolerance
Durante uma partição:
Pode rejeitar requisições
Ideal para operações onde dados incorretos são inaceitáveis.
AP
Prioriza:
Availability
+
Partition Tolerance
Durante uma partição:
Continua respondendo
Pode aceitar:
Eventual Consistency
Ideal quando disponibilidade é mais importante que consistência imediata.
Consistência Forte
WRITE
│
▼
READ
│
▼
Novo valor
Consistência Eventual
WRITE
│
├── Replica A → Novo valor
│
└── Replica B → Valor antigo
│
▼
Sync
│
▼
Mesmo valor
PACELC
O CAP fala sobre:
Partição:
C vs A
O PACELC adiciona:
Sem partição:
C vs L
Ou seja:
P → A ou C
ELSE → L ou C
Conclusão
O Teorema CAP não é uma regra que diz:
"Escolha dois entre três."
Essa é apenas uma forma simplificada de introduzir o conceito.
A realidade é mais interessante.
Em um sistema distribuído, partições de rede são inevitáveis. Quando elas acontecem, precisamos decidir se o sistema deve:
CP
ou:
AP
Se escolhermos consistência, podemos precisar sacrificar disponibilidade.
Se escolhermos disponibilidade, podemos aceitar consistência eventual.
Mas o dilema não termina quando a rede está funcionando.
Mesmo em condições normais, sistemas distribuídos precisam decidir entre:
Consistência
e:
Latência
É por isso que o PACELC é uma extensão tão importante do CAP.
No final, o verdadeiro trabalho de um arquiteto de sistemas não é escolher:
"CAP = CP"
ou:
"CAP = AP"
O verdadeiro trabalho é entender o negócio e responder:
"Quais inconsistências meu sistema pode tolerar?"
"Quanto tempo meu sistema pode ficar indisponível?"
"Qual latência é aceitável?"
"O que acontece quando dois usuários modificam o mesmo dado ao mesmo tempo?"
"É melhor retornar um erro ou um dado potencialmente desatualizado?"
Essas perguntas definem a arquitetura.
E esse é o verdadeiro dilema da computação distribuída:
CONSISTÊNCIA
▲
│
│
│
└──────────────► DISPONIBILIDADE
+
LATÊNCIA
Não existe uma solução perfeita para todos os sistemas.
Existem apenas trade-offs.
E entender esses trade-offs é uma das habilidades mais importantes em System Design.