DevGuideTK

Consistência eventual

Como projetar telas e regras de negócio quando a leitura pode vir desatualizada.

O problema: cópias que ainda não concordam

Quase todo sistema real guarda o mesmo dado em mais de um lugar: o banco principal e as réplicas, o banco e o cache, o serviço de pedidos e o de entregas, que recebe os eventos por uma fila. As cópias não mudam todas no mesmo instante. Por um tempo, uma delas tem o valor novo e outra ainda tem o antigo.

Consistência eventual é a garantia mínima nesse cenário: se as gravações pararem, todas as cópias acabam iguais. Ela não diz quando, nem o que cada leitura vê até lá. O tópico do teorema CAP apresenta essa troca e os níveis de consistência; o de replicação mostra a janela de atraso de uma réplica. Este tópico trata de como projetar telas e regras de negócio que convivem com essa janela.

O que o usuário não pode ver

A maior parte do atraso passa despercebida. Alguns efeitos, porém, quebram a confiança no sistema:

EfeitoO que o usuário vêGarantia que evita
Não ver o que acabou de gravarMuda o endereço, a tela mostra o antigoLer o que gravou (read your writes)
O tempo voltarO pedido aparece "enviado" e, ao recarregar, "pago"Leituras monotônicas
Efeito antes da causaVê a resposta a um comentário que ainda não apareceConsistência causal

O segundo efeito foi reproduzido com um PostgreSQL 18, um principal e duas réplicas. A réplica B teve a aplicação do log pausada, e o status do pedido 981 mudou de pago para enviado no principal. Um balanceador que alterna as réplicas a cada pedido devolveu:

Leituras seguidas do mesmo pedidoStatus
Alternando entre as réplicas A e Benviado, pago, enviado, pago, enviado, pago
Sempre na réplica Aenviado, enviado, enviado
Sempre na réplica Bpago, pago, pago
Na réplica B, depois de ela alcançar o principalenviado

Com o balanceador alternando, o pedido "voltou no tempo" a cada recarga, um efeito muito pior que um atraso fixo. Prendendo cada usuário a uma réplica, as leituras dele pararam de oscilar: podem estar atrasadas, mas nunca andam para trás. O "ler o que gravou", com o LSN da última gravação, foi medido no tópico de replicação.

Quando duas cópias mudam ao mesmo tempo

O atraso de leitura é o caso fácil. O difícil é quando duas cópias aceitam gravações ao mesmo tempo, sem se comunicar: dois servidores em regiões diferentes, dois dispositivos sem rede, duas instâncias que mesclam estados depois. Quando as cópias se encontram, alguém precisa decidir o resultado.

A regra mais comum é a última gravação vence (last write wins): cada gravação leva o horário de quem gravou, e a de horário maior fica. É simples e perde dados de dois jeitos:

  • Gravações simultâneas se sobrescrevem: se duas pessoas somam ao mesmo contador, uma soma desaparece.
  • Relógios divergem: o servidor com o relógio adiantado vence mesmo quando gravou antes.

Simulamos um contador em três nós, A, B e C, que somaram 5, 3 e 2 sem se comunicar, e depois trocaram os estados. O relógio do nó C estava adiantado:

FormaValor final nos três nós (o certo é 10)
A última gravação vence2, o valor do nó com o relógio adiantado
Contador que converge (G-Counter)10, 10 e 10

Estruturas que convergem sozinhas

Um CRDT (conflict-free replicated data type) é uma estrutura de dados desenhada para que qualquer cópia possa mudar sozinha, e para que a mesclagem dê sempre o mesmo resultado. A mesclagem tem três propriedades:

PropriedadeSignificadoPor que importa
ComutativaMesclar A com B dá o mesmo que B com AA ordem de chegada não importa
Associativa(A com B) com C dá o mesmo que A com (B com C)Os agrupamentos não importam
IdempotenteMesclar o mesmo estado duas vezes não muda nadaMensagens repetidas não estragam

O contador do teste é o mais simples: cada nó guarda só quanto ele mesmo somou, e a mesclagem fica com o maior valor de cada nó. No teste, o nó A recebeu B e depois C; o nó C recebeu B e depois A; o nó B recebeu o estado de A duas vezes. Os três chegaram a 10, nas cinco linguagens.

Há CRDTs para conjuntos, registros, mapas, listas e textos. São a base de editores colaborativos e de aplicações que funcionam sem rede (Automerge, Yjs), e de bancos que aceitam gravações em várias regiões (Riak, Redis Enterprise com replicação ativa-ativa). O preço é que nem toda regra de negócio cabe num CRDT: "o saldo nunca fica negativo" exige coordenação.

Regras de negócio com dados atrasados

Algumas decisões aceitam um dado de alguns segundos atrás; outras não:

DecisãoPode ler uma cópia atrasada?
Mostrar a contagem de curtidasSim
Listar os pedidos do clienteSim, com "ler o que gravou" para o pedido recém-criado
Exibir o estoque na vitrineSim, como estimativa
Confirmar a compra do último item do estoqueNão: a reserva acontece na fonte da verdade, com gravação condicional
Liberar um saque pelo saldoNão
Conferir se o usuário ainda tem permissãoNão, para ações sensíveis

O padrão é separar mostrar de decidir: a tela pode mostrar a cópia rápida; a decisão que não admite erro acontece onde o dado é a fonte da verdade, com as proteções do tópico de níveis de isolamento.

Por que isso importa quando a IA escreve o código

Um assistente que acrescenta réplicas, cache ou eventos ao sistema costuma tratar todas as cópias como se fossem a fonte da verdade:

  • Leituras distribuídas entre réplicas sem afinidade, com dados que oscilam a cada recarga.
  • A tela recém-alterada lida de uma cópia atrasada.
  • Decisões de saldo, estoque e permissão tomadas com dados de cache ou de réplica.
  • "A última gravação vence" com o relógio de cada servidor, perdendo gravações simultâneas.
  • Contadores replicados incrementados com ler, somar e gravar.
  • Consumidores de eventos que não toleram mensagens repetidas ou fora de ordem.

O próximo nível compara as garantias de leitura, as formas de resolver conflitos e os padrões de tela para dados eventualmente consistentes.

Prompts para aprofundar

Ao abrir no ChatGPT ou no Claude, o prompt é enviado ao serviço escolhido pela URL e também fica copiado na sua área de transferência.

  • Consistência eventual em cinco minutos

    Explique em no máximo cinco minutos de leitura o que é consistência eventual, para alguém que só trabalhou com um banco de dados sem réplicas nem cache.
    
    Cubra só o essencial: por que as cópias de um dado divergem por um tempo, o que o usuário não pode ver (não ver o que gravou, o tempo voltar), o problema de "a última gravação vence" e por que decisões importantes leem a fonte da verdade.
    
    Use o exemplo de uma loja com réplicas de leitura e cache. No final, me faça três perguntas curtas para eu verificar se entendi, e corrija as minhas respostas.
  • As garantias de leitura

    Quero entender as garantias de leitura em sistemas eventualmente consistentes.
    
    Aja como um mentor e, para cada garantia (ler o que gravou, leituras monotônicas, consistência causal), uma de cada vez:
    1. Mostre uma sequência de leituras que a viola, com réplicas em atrasos diferentes.
    2. Peça que eu diga o que o usuário vê.
    3. Explique como implementá-la (afinidade, LSN, versões) e o custo.
    
    Espere a minha resposta em cada etapa.
  • Conflitos de gravação e CRDTs

    Quero entender o que acontece quando duas cópias aceitam gravações ao mesmo tempo.
    
    Explique:
    1. Por que "a última gravação vence" perde dados, com gravações simultâneas e com relógios divergentes.
    2. Detectar conflitos com versão e devolver ao usuário.
    3. O que é um CRDT e as três propriedades da mesclagem.
    4. G-Counter, PN-Counter e OR-Set, com exemplos.
    5. Que regras de negócio não cabem num CRDT.
    
    Depois me descreva três dados de um aplicativo que funciona sem rede e pergunte como eu resolveria os conflitos de cada um.
  • Mostrar e decidir

    Quero aprender a separar o que pode ler uma cópia atrasada do que precisa da fonte da verdade.
    
    Me descreva, uma de cada vez, oito situações de um sistema (vitrine, carrinho, pagamento, saldo, permissões, painel, notificações, relatórios) e pergunte se cada uma pode usar uma cópia atrasada. Depois de cada resposta, explique a resposta certa e o que pode dar errado.