DevGuideTK

Consistência eventual

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

As decisões com dados eventualmente consistentes são quatro: quais garantias de leitura oferecer, como resolver conflitos de gravação, como desenhar as telas e onde exigir a fonte da verdade.

Garantias de leitura

GarantiaComo implementarCusto
Ler o que gravouGuardar o LSN, a versão ou o horário da última gravação do usuário, e só ler de uma cópia que já passou dele; ou ler do principal por alguns segundos depois de gravarAlgumas leituras voltam ao principal
Leituras monotônicasAfinidade: o mesmo usuário sempre na mesma réplica; ou guardar a versão mais nova já vista e não aceitar uma mais velhaAfinidade desequilibra a carga se as réplicas forem poucas
Consistência causalLevar junto com cada dado a versão das dependências (um vetor de versões, ou o LSN)Mais metadados em cada mensagem
Ler sempre a fonteLer do principalSem os ganhos das réplicas e do cache

No teste do nível Conceito, sem afinidade, o mesmo pedido alternou entre enviado e pago a cada leitura; com afinidade, cada usuário viu um valor estável. A afinidade por usuário costuma ser o mínimo para qualquer sistema que leia de réplicas.

Como resolver conflitos de gravação

Só existem conflitos quando mais de uma cópia aceita gravações. Com um banco principal, eles não aparecem. Quando aparecem:

EstratégiaComo funcionaPerde dados?
Evitar: um dono por dadoCada registro só é gravado numa região ou num nó, por exemplo pelo clienteNão, mas exige roteamento
A última gravação venceFica a gravação de maior horárioSim: a simultânea e a de relógio atrasado (o teste ficou com 2 em vez de 10)
Versão e conferênciaA gravação leva a versão que leu; se outra gravou antes, recusa e pede para o usuário refazerNão, mas o usuário vê o conflito
Guardar as duas versõesO sistema mantém os valores em conflito e alguém escolhe depois (o carrinho mescla os itens)Não
CRDTA estrutura mescla sozinha, sempre com o mesmo resultadoNão, para as operações que a estrutura cobre

"A última gravação vence" é aceitável para dados em que perder uma atualização não importa (a hora do último acesso). Para todo o resto, prefira evitar o conflito com um dono por dado, ou detectar com versão.

Qual CRDT para cada caso

DadoCRDT
Contador que só cresce (visualizações)G-Counter
Contador que cresce e diminui (curtidas, com descurtir)PN-Counter: dois G-Counters, um para somas e outro para subtrações
Conjunto com adições e remoções (etiquetas, membros)OR-Set
Um valor que pode ser sobrescritoLWW-Register, ciente de que perde gravações simultâneas, ou MV-Register, que guarda todas
Texto editado por várias pessoasCRDTs de sequência, nas bibliotecas Automerge e Yjs

Como desenhar as telas

PadrãoComo funcionaQuando usar
Atualização otimistaA tela mostra a mudança na hora e confirma (ou desfaz) quando o servidor responderCurtidas, marcar como lido, mover cartões
Estado intermediário explícito"Pagamento em processamento" até o evento de confirmação chegarProcessos assíncronos, com filas e serviços externos
Mostrar a idade do dado"Atualizado há 2 minutos"Painéis e relatórios lidos de cópias
Devolver o resultado da gravaçãoA resposta da gravação traz o registro completo, e a tela usa esse registro em vez de buscar de novoFormulários de edição
Recarregar com a versãoA tela pede "a versão 7 ou mais nova" e espera um pouco se a cópia ainda não chegou láTelas que leem projeções alimentadas por eventos

O quarto padrão resolve muita coisa com pouco: se a resposta do PUT já traz o endereço atualizado, a tela não precisa ler de nenhuma réplica logo depois de salvar.

Onde exigir a fonte da verdade

A regra: cópias para mostrar, fonte para decidir. Saldo, estoque disponível para venda, limites, permissões e qualquer regra que não pode ser violada leem o dado na fonte, com as proteções de concorrência do tópico de níveis de isolamento. Telas, listagens, contagens e relatórios podem ler as cópias.

Para processos que atravessam vários serviços, cada um com seus dados, a consistência entre eles também é eventual. O caminho são eventos com entrega garantida e consumidores idempotentes, e sagas com compensação quando um passo falha, como mostram os tópicos de garantias de entrega e de saga e outbox.

Resumo da decisão

  1. Afinidade por usuário em toda leitura de réplicas, para o tempo não voltar.
  2. Ler o que gravou nas telas que mostram o que o usuário acabou de alterar, ou devolver o registro na resposta da gravação.
  3. Um dono por dado para evitar conflitos; versão para detectá-los; CRDTs onde há gravações independentes de verdade.
  4. "A última gravação vence" só onde perder uma atualização não importa.
  5. Telas honestas: atualização otimista, estados intermediários e a idade dos dados.
  6. Decisões na fonte da verdade; cópias só para mostrar.

O próximo nível mostra o contador que converge nas cinco linguagens, comparado com "a última gravação vence".

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.

  • Projetar a consistência do meu sistema

    Meu sistema tem réplicas, cache ou serviços que trocam eventos, e quero projetar a consistência de forma consciente.
    
    Pergunte quais cópias de dados existem, como elas são atualizadas e quais telas e regras de negócio leem de cada uma.
    
    Para cada leitura, diga se ela pode usar uma cópia, qual garantia precisa (ler o que gravou, monotônica) e como implementá-la. Para cada decisão de negócio, diga se ela precisa da fonte da verdade.
  • Telas para dados eventualmente consistentes

    Quero que as telas do meu sistema lidem bem com dados que podem estar atrasados.
    
    Pergunte quais telas existem, quais processos são assíncronos e qual framework de front-end eu uso.
    
    Proponha, tela por tela, o padrão adequado: atualização otimista, estado intermediário explícito, idade do dado, devolver o registro na resposta da gravação ou recarregar com a versão. Mostre o código de um deles no meu framework.
  • Escolher como resolver conflitos

    Meu sistema vai aceitar gravações em mais de um lugar (várias regiões, ou aplicativo que funciona sem rede).
    
    Pergunte quais dados podem ser alterados em paralelo e o que acontece de errado se uma alteração se perder.
    
    Para cada dado, compare: um dono por dado, a última gravação vence, versão com conferência, guardar as duas versões e CRDT. Recomende uma estratégia e as bibliotecas ou recursos do banco que eu poderia usar.