Este texto faz parte da série Coisas que você já sabe ou deveria saber. A ideia dela não é te ensinar a teoria no detalhe, porque pra isso já existe material melhor que o meu, muito dele de graça. Esses textos saem direto das minhas anotações de estudo pessoal, passadas a limpo e reorganizadas para responder uma pergunta só: o porquê. Eu pego um problema concreto do dia a dia, mostro onde ele dói e só então puxo o conceito que explica aquilo. O aprofundamento técnico eu deixo de propósito pra sua curiosidade resolver, que com um motivo claro na mão ela aprende sozinha e aprende fundo.
Tem uma classe de conhecimento que ninguém te cobra de forma explícita, mas que reaparece toda vez que um sistema te surpreende às três da manhã. CAP, ACID, BASE, PACELC vivem nessa zona. A gente aprende pra entrevista, repete em reunião de arquitetura pra não passar vergonha, e depois deixa enferrujar, até o dia em que um dado some, ou aparece duplicado, ou volta um valor que já tinha sido sobrescrito. É aí que você descobre que nunca entendeu de verdade quais garantias o seu banco estava te dando. Esse texto é sobre isso. Não sobre o que cada letra significa, porque isso está em qualquer lugar, e sim sobre o motivo de saber disso mudar a forma como você projeta, modela e, principalmente, lê sistema que outra pessoa escreveu antes de você chegar.
Começo pelo lugar onde isso aperta, antes de qualquer teoria, porque a teoria só faz sentido depois que você sente onde ela morde. Pega o sistema mais batido que existe: dinheiro entrando e saindo de uma conta. Parece um problema só. São dois, e eles querem coisas opostas. Quem mistura os dois sangra, e a maioria mistura sem perceber.
O caso: o ledger e o extrato
De um lado tem a escrita do movimento. Chega uma transação e duas coisas precisam acontecer grudadas: registrar o movimento no ledger, que é o histórico append-only e imutável, a fonte da verdade de tudo que passou, e atualizar o saldo da conta. Ou os dois acontecem juntos, ou nenhum acontece. Se o registro grava e o saldo não, você acabou de criar uma conta num estado impossível, com um movimento que existe no histórico mas não bateu em lugar nenhum. Junta a isso duas transações caindo na mesma conta ao mesmo tempo: se elas intercalam a leitura e a escrita do saldo, uma sobrescreve a outra e o dinheiro evapora ou se multiplica. Esse lado exige rigor. Tudo ou nada, uma transação cega para o meio do caminho da outra, o dado correto acima de tudo, custe a velocidade que custar.
Do outro lado tem o extrato, e o extrato engana. Ele se parece com um dado, mas é só uma releitura do ledger. A pergunta que ele responde, me mostra os movimentos e o saldo dessa conta nesse período, já está inteira no histórico; o extrato apenas reorganiza num formato confortável de ler. Por ser uma projeção, ele aceita tudo que o outro lado recusa. Pode estar desnormalizado, pode vir de uma réplica de leitura, pode estar um ou dois segundos atrasado sem que ninguém se machuque, pode ser jogado fora e reconstruído do zero a partir do ledger quando der vontade. O que ele quer é responder rápido e estar sempre disponível, não cravar o último centavo no milissegundo exato.
O estrago aparece quando alguém trata os dois como a mesma coisa, e isso acontece de três jeitos. O mais grave é decidir um saque lendo o saldo da projeção. Alguém pega o saldo numa réplica de leitura, que pode estar atrasada, pra autorizar a operação, e aprova um saque contra dinheiro que já não existe. Nasce um saldo negativo que regra nenhuma deveria permitir, porque uma leitura defasada acabou de comandar uma decisão que precisava do número exato. O saldo que autoriza tem que vir sempre do lado rigoroso.
O inverso também é comum. Alguém resolve que o extrato precisa ser perfeito e o gera com uma consulta ao vivo contra o ledger a cada acesso. Agora a leitura do extrato disputa recurso com a escrita das transações, trava linha que o caminho transacional precisa, e a latência de escrita despenca. Você pagou o preço do rigor por uma tela que ninguém precisava que fosse exata ao milissegundo.
O terceiro é o mais silencioso: modelar os dois na mesma tabela. Uma tabela de transações que é, ao mesmo tempo, o ledger imutável e a tela de leitura. Começam os updates em linha que deveriam ser só append, ou a desnormalização do saldo corrente dentro do próprio ledger, e cada insert passa a travar e recalcular saldo, matando a escrita justo onde ela mais importa.
Repare que, pra descrever esse problema, eu já usei meia dúzia de ideias carregadas: tudo ou nada, uma transação sem ver a outra, réplica atrasada, projeção descartável. Cada uma tem nome próprio e, o que importa mais, tem origem. Conhecer esses nomes é o que te permite fazer essa separação de propósito, no quadro branco, antes do incidente, em vez de aprender no susto que juntou dois mundos que não podiam dividir o mesmo modelo. O resto do texto é de onde vêm esses nomes e por que eles existem.
De onde isso tudo vem, e por que precisou mudar
Nada disso caiu do céu, e a linha do tempo importa porque explica por que existem tantas categorias de banco hoje. Não é modismo, é resposta a problema.
Começa em 1970, com Edgar Codd e o modelo relacional. Tabelas, relações, um jeito declarativo de perguntar pelos dados. Aquilo resolveu tão bem o problema da época que virou padrão por décadas. E o problema da época cabia numa máquina: dado estruturado, volume que um servidor aguentava, e quando apertava você comprava um servidor maior. Escala vertical. O ACID nasce confortável nesse mundo, porque coordenar uma transação sai barato quando está tudo no mesmo disco, na mesma máquina, do lado de dentro.
O que quebrou esse conforto foi a web. O volume de dados e de acesso passou do que qualquer máquina única consegue segurar, por maior que ela seja. A única saída era escalar para o lado, espalhar o dado por muitas máquinas. E o pressuposto silencioso do modelo relacional caiu junto: manter consistência forte através de uma rede, entre máquinas que de vez em quando se perdem de vista, é caro e às vezes impossível. Não dava mais para fingir que estava tudo no mesmo disco.
Foi nesse aperto que o CAP ganhou nome. Eric Brewer levantou a ideia por volta de 2000 e Gilbert e Lynch a formalizaram em 2002: num sistema que pode sofrer partição de rede, você não consegue ter consistência e disponibilidade plenas ao mesmo tempo. O CAP não inventou uma limitação, ele deu nome a uma dor que a escala horizontal tornou impossível de evitar. Essa mesma dor fez nascer os bancos NoSQL e a filosofia BASE: em vez de brigar para manter o rigor relacional na marra, relaxar a consistência de propósito, aceitar que o dado fica certo depois e ganhar com isso disponibilidade e escala. As categorias se multiplicaram porque os problemas se multiplicaram. Documento, chave-valor, coluna larga, grafo, cada uma boa num padrão de acesso que o relacional, sob escala, atendia mal e caro.
Só que o CAP tinha um furo, e a prática mostrou rápido. Ele só falava do que acontece durante a partição, que é o caso raro. No dia normal, com a rede inteira de pé, você continua pagando um preço, e o CAP era mudo sobre ele. Daniel Abadi fechou esse furo por volta de 2010 com o PACELC: na partição você escolhe entre disponibilidade e consistência, como no CAP, mas fora dela, no else, você ainda escolhe entre latência e consistência. O PACELC é a evolução natural porque descreve o trade-off dos 99% do tempo, não só o do incidente.
O NewSQL é o capítulo mais recente. É a tentativa de recuperar o ACID que o NoSQL abriu mão, agora em escala horizontal, usando protocolos de consenso como Raft e Paxos para coordenar muitas máquinas sem mentir sobre consistência. Funciona, mas o consenso cobra seu preço em complexidade operacional, e por isso não é solução de graça para todo mundo. A moral da linha do tempo é simples: cada modelo é a resposta de engenharia a um limite real que apareceu quando a escala mudou. Ninguém trocou de paradigma por estética, trocou porque o anterior parou de caber no problema.
As siglas, sem decorar
Não vou destrinchar ACID, BASE, CAP e PACELC letra por letra, porque isso está em qualquer lugar e não é o ponto. Vou só amarrar cada um no que você já viu.
ACID é o modelo transacional do lado rigoroso da conta. Atomicidade é o tudo ou nada do registro mais saldo. Isolamento é uma transação cega para o meio da outra, exatamente o que salvou o saldo das duas concorrentes. Durabilidade é commitou, não some. E consistência, no ACID, é integridade de schema e regra do próprio banco, inteiro não entra em campo de texto, constraint barra o impossível. Guarde só um cuidado dessa lista: o C do ACID tem pouco a ver com a consistência distribuída do CAP, que é sobre cópias do mesmo dado concordarem entre máquinas. Confundir os dois é clássico e custa caro. E quando você abre mão de alguma dessas garantias por escala sem perceber, a consistência vaza para o código de aplicação resolver, e código de aplicação resolve consistência mal.
BASE é o avesso, o lado do extrato: basically available, soft-state, eventually consistent. Relaxa a consistência de propósito para ganhar disponibilidade e escala. A forma honesta de sentir esse trade-off é pela escrita. Consistência forte segura a confirmação até um quórum concordar, e aqui vale travar uma coisa: quando eu falo em nós, são nós de banco de dados, réplicas dentro do cluster, não as instâncias da sua aplicação. Quórum é maioria simples, o N/2+1, cinco nós esperam três, dez esperam seis. É a mesma maioria que sustenta Raft e Paxos, e o motivo de funcionar é geométrico: desde que os nós lidos mais os escritos passem do total de réplicas, o famoso R+W>N, leitura e escrita sempre cruzam em pelo menos um nó atualizado, e por isso uma leitura nunca volta uma versão velha sem perceber.
O efeito disso é a "slow write": você espera a maioria confirmar antes de dar a operação por concluída. (E sim, "slow write" simplifica, porque existe a latência da escrita em si e existe o tempo de confirmação das réplicas, que são coisas distintas; para esta conversa basta que você troca espera por certeza.) Consistência eventual faz o contrário, a "fast write", confirma assim que um nó aceita e propaga depois, e naquele intervalo nós diferentes respondem coisas diferentes. Esse eventualmente é característica comprada de propósito, não defeito a corrigir. Um contador de likes aguenta oscilar dois segundos; o saldo que autoriza saque não.
CAP, depois da história, é fácil de usar se você ignorar a versão de boteco. Tolerância a partição não é escolha, partição vai acontecer em qualquer sistema distribuído de verdade. A pergunta real é uma: quando ela chegar, você sacrifica consistência, parando de responder em vez de mentir, ou disponibilidade, respondendo defasado em vez de calar? Só cuidado com rótulo de produto. Cassandra costuma ser chamado de AP, mas tem consistência configurável por operação, então a garantia que você de fato tem depende de como a sua aplicação ajustou os níveis de leitura e escrita, e não do nome na caixa. Generalizar que o banco X é CP e o Y é AP é onde nasce metade do erro de arquitetura. PACELC fecha o furo do CAP: fora da partição, no else, você ainda escolhe entre latência e consistência, porque manter consistência forte custa coordenar aquela maioria a cada escrita. É por isso que decisões que parecem só otimização, como adicionar réplica de leitura, afrouxar consistência ou cachear, são na verdade movimentos no eixo latência contra consistência. Quem vê isso decide de propósito; quem não vê abre uma janela de inconsistência e descobre meses depois, no relatório que não bate.
Onde isso te salva de verdade: o legado
Tudo isso parece papo de quem projeta do zero, mas você passa muito mais tempo lendo sistema dos outros do que desenhando o seu, e é na leitura de legado que essa teoria deixa de ser acadêmica e vira ferramenta de sobrevivência.
Quando você cai num sistema que não documentou nada, e nenhum documenta isso, a pergunta que destrava tudo é qual garantia os autores acharam que tinham. Quase sempre ninguém escolheu de propósito. Alguém pegou o banco que conhecia, deixou na configuração padrão, e o comportamento de consistência emergiu por acidente. O sistema virou eventualmente consistente porque adicionaram réplica de leitura num relacional e nunca pensaram no lag entre os nós do banco. Ou ficou frágil sob partição porque configuraram um quórum sem entender o que estavam mexendo.
Conhecer CAP, ACID, BASE e PACELC te dá o gabarito para reconstruir essas decisões implícitas. Você lê o schema, vê onde tem transação e onde não tem, onde tem replicação assíncrona, o que o código de aplicação está fazendo para tapar o que o banco não garante, e o sistema deixa de ser caixa-preta. O bug intermitente que ninguém reproduz passa a ter nome, vira a janela de consistência eventual entre a escrita no nó primário e a leitura na réplica. O relatório que às vezes bate vira leitura de um nó que ainda não recebeu a propagação. E o saldo que de vez em quando fica negativo é, quase sempre, autorização feita contra a projeção em vez do ledger, exatamente o primeiro erro lá do começo. O problema não fica mais fácil de resolver, mas para de ser mistério e vira coisa que você consegue nomear, e problema com nome é problema que se ataca.
É por isso que coloco essa teoria na categoria de coisa que você já deveria saber e provavelmente sabe pela metade. Ela não serve para acertar a pergunta da entrevista, serve porque é a gramática com que sistema distribuído quebra, e você vai ler essa gramática de um jeito ou de outro. A única escolha é se vai entendê-la antes ou depois do incidente.
Tem uma ideia velha do Fred Brooks que encaixa certinho aqui. Existe a complexidade acidental, que é a ferramenta, a sintaxe, o boilerplate, e some conforme a tecnologia melhora; e existe a complexidade essencial, que é inerente ao problema e não vai a lugar nenhum. O trade-off entre consistência, disponibilidade e latência é complexidade essencial pura. Nenhum modelo de banco mata esse trade-off, nem o NoSQL, nem o NewSQL, nem o próximo da fila; eles só mudam o lugar onde você paga a conta. Banco de dados não te dá garantia de graça, te vende garantia a um preço, e o preço está sempre escrito nessas siglas. O trabalho nunca foi decorar as letras. É aprender a ler o que vem na conta.
