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.

Avançado 35 min 30 pontos Leitura 0%

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?