Skip to main content

Command Palette

Search for a command to run...

Consistência eventual: quando aceitar dados temporariamente inconsistentes é a decisão correta

Updated
•5 min read•View as Markdown
W
Full Stack Engineer building high-scale fintech & fiscal systems. Writing about distributed architecture, AI-augmented development, and security by design.

Muitos desenvolvedores tratam qualquer inconsistência de dados como bug. Mas aceitar alguns milissegundos de divergência entre réplicas, em troca de latência menor e mais disponibilidade, muitas vezes é a decisão certa. A questão é saber onde fazer essa troca.

O que é consistência eventual

É a garantia de que, se não houver novas escritas, todas as réplicas vão convergir para o mesmo valor em algum momento. Não diz quando. No outro extremo está a consistência forte (linearizabilidade): toda leitura vê a escrita mais recente, como se houvesse uma única cópia do dado.

CAP e PACELC

O teorema CAP diz que, durante uma partição de rede, um sistema distribuído precisa escolher entre consistência (recusar requisições para não retornar dados velhos) e disponibilidade (responder mesmo arriscando divergência). O famoso "escolha 2 de 3" engana: partições acontecem, então a escolha real é entre C e A quando elas ocorrem.

O problema é que partições são raras. O PACELC completa o quadro: se houver Partição, escolha A ou C; senão (Else), escolha entre Latência e Consistência. Essa segunda metade afeta seu sistema todos os dias, porque garantir consistência entre réplicas exige esperar confirmação de outros nós.

Sistema Com partição Operação normal
Dynamo / Cassandra Disponibilidade Latência
Google Spanner Consistência Consistência
PostgreSQL + réplicas assíncronas Depende de onde a leitura vai Latência nas réplicas

Replicação: onde a inconsistência nasce

Na prática, você encontra consistência eventual ao adicionar réplicas de leitura. Na replicação síncrona, o primário só confirma a escrita após uma réplica gravar: mais seguro, mais lento. Na assíncrona, o primário confirma na hora e replica em segundo plano: rápido, mas as réplicas ficam atrás por um intervalo chamado replication lag. Normalmente milissegundos; sob carga pesada ou durante uma migração, segundos ou minutos.

Read-after-write: o problema que o usuário vê

Se um cliente vê um preço antigo por 200 ms, ninguém percebe. Mas quando o próprio autor da alteração não a vê, parece bug: o lojista edita um prato, salva, é redirecionado para a listagem e o nome antigo aparece. A escrita foi para o primário; a leitura, para uma réplica atrasada.

A garantia que resolve isso é read-your-writes: cada usuário sempre vê as próprias escritas. Ela é bem mais barata que consistência forte, porque só vale para quem escreveu. Algumas formas de implementar:

1. Ler do primário após escrever. No Laravel, a opção sticky faz isso dentro da mesma requisição:

'pgsql' => [
    'read'  => ['host' => ['replica-1.internal']],
    'write' => ['host' => ['primary.internal']],
    'sticky' => true,
],

O limite: não cobre o padrão "salvar e redirecionar", que gera uma requisição nova.

2. Janela por usuário. Após escrever, marque na sessão ou no Redis que aquele usuário lê do primário pelos próximos N segundos. Simples, mas N é um palpite.

3. Rastrear a posição de replicação. No PostgreSQL, guarde o LSN da escrita e só leia de uma réplica que já chegou nele:

-- no primário, após a escrita
SELECT pg_current_wal_lsn();

-- na réplica, antes de ler
SELECT pg_last_wal_replay_lsn() >= '0/3A1B2C8'::pg_lsn;

4. UI otimista. Com React Query, atualize o cache local com o valor que o usuário enviou e evite refazer a busca na hora.

Quando consistência forte é desnecessária

A pergunta certa é: qual o custo de alguém ver este dado desatualizado por alguns segundos? Quando é quase zero, consistência eventual vence. Exemplos: contadores e métricas de dashboard, cardápios e catálogos (lidos por muitos, editados por poucos), índices de busca, feeds e notificações.

Já estes casos pedem consistência forte no ponto crítico:

  • Dinheiro: cobranças e reembolsos, sempre com idempotência para evitar cobrança dupla em retentativas.

  • Recursos escassos: o último item do estoque, cupons limitados. Use transação no primário ou SELECT ... FOR UPDATE.

  • Unicidade: e-mail, slug da loja. Constraint no banco.

  • Permissões: acesso revogado não pode continuar valendo numa réplica atrasada.

A decisão é por operação, não por sistema. O mesmo app pode servir o cardápio de uma réplica e debitar o estoque com lock no primário.

Conclusão

Consistência eventual não é gambiarra: é como boa parte da internet se mantém rápida e resiliente. O trabalho do engenheiro é proteger os poucos pontos onde a consistência forte é inegociável e deixar o resto fluir, garantindo sempre que o usuário veja o que ele mesmo acabou de fazer.


Referências

  1. VOGELS, W. Eventually Consistent. Communications of the ACM, v. 52, n. 1, 2009.

  2. BREWER, E. Towards Robust Distributed Systems. Keynote, PODC, 2000.

  3. GILBERT, S.; LYNCH, N. Brewer's Conjecture and the Feasibility of Consistent, Available, Partition-Tolerant Web Services. ACM SIGACT News, v. 33, n. 2, 2002.

  4. BREWER, E. CAP Twelve Years Later: How the "Rules" Have Changed. IEEE Computer, v. 45, n. 2, 2012.

  5. ABADI, D. Consistency Tradeoffs in Modern Distributed Database System Design. IEEE Computer, v. 45, n. 2, 2012.

  6. KLEPPMANN, M. Designing Data-Intensive Applications. O'Reilly, 2017. Cap. 5.

  7. TERRY, D. B. et al. Session Guarantees for Weakly Consistent Replicated Data. PDIS, 1994.

  8. Laravel: Read and Write Connections

  9. PostgreSQL: System Administration Functions

  10. Stripe: Idempotent Requests

2 views