Operações distribuídas e concorrência
Consistência em ambientes distribuídos
Aplique o teorema CAP, modelos de consistência (strong, eventual, causal) e técnicas práticas para gerenciar consistência em sistemas de cache distribuídos.
Nesta aula você vai
- Aplicar o teorema CAP para analisar trade-offs em sistemas de cache
- Diferenciar consistência forte, eventual e causal
- Implementar padrões práticos de consistência em arquiteturas com Redis
Consistência em ambientes distribuídos
Objetivos
Nesta aula você vai:
- Aplicar o teorema CAP como ferramenta de análise arquitetural
- Diferenciar modelos de consistência: forte, eventual e causal
- Implementar padrões práticos para gerenciar consistência com Redis
Conteúdo
O trilema que toda arquitetura enfrenta
Em 2000, Eric Brewer apresentou o teorema CAP — depois formalizado por Gilbert e Lynch — estabelecendo que um sistema distribuído só pode garantir simultaneamente duas de três propriedades:
- Consistency — todos os nós veem os mesmos dados ao mesmo tempo
- Availability — toda requisição recebe resposta (sucesso ou erro explícito)
- Partition tolerance — o sistema funciona mesmo com falha de comunicação entre nós
Como num romance com três personagens principais mas espaço para dois no palco final: em sistemas reais, onde partição de rede (P) é inevitável, a escolha é entre CP e AP.
Consistência (C)
▲
│
CP │ CA
(Redis │ (impossível
sync rep) │ com P real)
│
────────────┼────────────► Disponibilidade (A)
│
AP │
(Memcached, │
eventual) │
▼
Partition (P) — sempre presente
CAP na prática: não é binário
CAP é modelo de pensamento, não switch on/off. Sistemas modernos oferecem espectro de consistência configurável:
| Sistema | Configuração | Comportamento |
|---|---|---|
| Redis (repl. async) | Padrão | AP com consistência eventual |
| Redis + WAIT | WAIT 1 1000 |
CP parcial (quorum de replicas) |
| Redis (min-replicas-to-write) | min-replicas-to-write 1 |
CP (recusa escrita sem replica) |
| Memcached | Sem replicação | AP (dados voláteis) |
| Aerospike | replication-factor 2 sync |
CP (confirma replica antes de ACK) |
Modelos de consistência
Consistência forte (linearizability)
Toda leitura retorna o valor da escrita mais recente. Como se houvesse uma única cópia dos dados:
# Processo A escreve
redis-cli -h master SET saldo:42 100
# Processo B lê imediatamente do master
redis-cli -h master GET saldo:42
# "100" — sempre consistente
# Processo C lê da replica (pode ser stale!)
redis-cli -h replica GET saldo:42
# "90" — valor antigo (replication lag)
Quando exigir: transações financeiras, locks, contadores críticos.
Consistência eventual
Sem garantia de leitura imediata do valor mais recente, mas eventualmente todos os nós convergem:
Master: SET x=1 → Replica (lag 50ms) → Replica: x=1
Janela de inconsistência: 0–50ms
Aceitável para: cache de catálogo, feeds, dashboards
Quando aceitar: cache de leitura, dados de referência, analytics.
Consistência causal
Preserva ordem de operações causalmente relacionadas:
A: SET usuario:42:nome "Ana"
B: SET usuario:42:email "ana@exemplo.com" (depende de A)
C: GET usuario:42:nome → deve ver "Ana" se viu o email atualizado
Redis não garante consistência causal nativamente entre chaves — a aplicação deve coordenar com versionamento ou timestamps.
O espectro PACELC
Extensão do CAP por Daniel Abadi:
Se não há partição (P), escolha entre Latency e Consistency.
| Cenário | Escolha | Exemplo |
|---|---|---|
| Sem partição, prioriza latência | PA/EL | Redis async replication |
| Sem partição, prioriza consistência | PC/EC | Redis com WAIT |
| Com partição, prioriza disponibilidade | AP/EL | Memcached |
| Com partição, prioriza consistência | CP/EC | Aerospike sync replication |
Padrões práticos com Redis
1. Read-your-writes
Garantir que o processo que escreveu veja sua própria escrita:
# Escrever no master
redis-cli -h master SET sessao:abc "dados"
# Ler do master (não da replica) para mesma sessão
redis-cli -h master GET sessao:abc
Em aplicações: após POST, redirecionar GET para master por alguns segundos.
2. Cache-aside com invalidação
1. App escreve no PostgreSQL
2. App invalida cache: DEL cache:produto:1001
3. Próxima leitura: cache miss → busca DB → popula cache
# Invalidação após escrita no banco
redis-cli DEL cache:produto:1001
# Janela de inconsistência: entre escrita no DB e DEL no cache
# Mitigação: invalidar ANTES de confirmar ao cliente, ou usar TTL curto
3. Write-through com TTL
1. App escreve no Redis E no banco (ou fila)
2. Leitura sempre do Redis
3. TTL garante refresh periódico
4. Versionamento otimista
# Cada escrita incrementa versão
redis-cli HSET produto:1001 preco 99.90 _version 5
# Leitura verifica versão
redis-cli HGET produto:1001 preco _version
# Escrita condicional (Lua)
redis-cli EVAL "
local current = tonumber(redis.call('HGET', KEYS[1], '_version') or '0')
if current >= tonumber(ARGV[2]) then
return 0
end
redis.call('HSET', KEYS[1], 'preco', ARGV[1], '_version', ARGV[2])
return 1
" 1 produto:1001 89.90 6
Stale reads: quantificando o risco
# Medir replication lag
redis-cli -h master INFO replication | grep master_repl_offset
redis-cli -h replica INFO replication | grep slave_repl_offset
# Diferença = lag em bytes
# Converter para tempo: lag_bytes / throughput_replication
| Lag típico | Risco de stale read | Ação |
|---|---|---|
| < 1 ms | Negligível | Leitura em replica OK |
| 1–100 ms | Baixo | Aceitável para cache |
| 100 ms–1 s | Moderado | Ler do master para dados críticos |
| > 1 s | Alto | Investigar replica ou rede |
Consistência em Redis Cluster
| Operação | Garantia |
|---|---|
| Escrita em chave única | Atômica no nó primário do slot |
| Leitura de chave única | Do nó que possui o slot |
| MULTI/EXEC (mesmo slot) | Atômica |
| MULTI/EXEC (cross-slot) | Impossível |
| Replicação entre nós | Eventual (async) |
Tabela de decisão: qual consistência?
| Dado | Modelo | Justificativa |
|---|---|---|
| Saldo de conta | Strong (master + WAIT) | Erro financeiro |
| Sessão de login | Read-your-writes | UX — usuário vê própria ação |
| Catálogo de produtos | Eventual (replica OK) | Stale de segundos aceitável |
| Contador de visitas | Eventual | Aproximação suficiente |
| Lock distribuído | Strong (SET NX) | Exclusividade obrigatória |
| Cache de API | Eventual + TTL | Refresh automático |
Resumo
- CAP força escolha entre consistência e disponibilidade quando há partição de rede
- Consistência forte para dados críticos; eventual para cache e leitura tolerante
- Padrões práticos: read-your-writes, cache-aside com invalidação, versionamento otimista
- Meça replication lag — ele define a janela de inconsistência real
Para refletir
"Consistência não é virtude absoluta — é contrato entre sistema e negócio. O catálogo pode estar 2 segundos atrasado; o saldo, não."
Quais dados do seu sistema toleram consistência eventual e quais exigem leitura forte?