o problema
Entre 2011 e 2015 saiu uma safra grande de pesquisa mostrando que dava para ter mais garantia em store geo-replicado sem pagar o preço clássico: consistência causal escalável, consistência forte, transações entre datacenters, tudo com boa performance em workload experimental. A pergunta que os autores fazem no primeiro parágrafo é constrangedora e honesta: nós lemos esses papers, gostamos deles, e mesmo assim não adotamos nenhum. Por quê?
A resposta começa no desenho. Quando um nó ou uma aresta entra no grafo social do Facebook, ele é escrito primeiro numa única instância MySQL. Dali é replicado de forma assíncrona para todos os datacenters, atualizado ou invalidado em várias camadas de cache, e entregue por um pub-sub — o Wormhole — para os outros serviços que mantêm estado. E são centenas de serviços em produção, muitos deles com uma cópia parcial do grafo. O TAO fatia nós por id de 64 bits e arestas pela origem. O News Feed fatia por id de usuário e desnormaliza tudo que a story precisa, para ranquear localmente. O índice secundário materializado fatia por valor. O Unicorn usa document sharding com leaf-aggregator para a busca. Nenhum layout serve a todos os workloads em escala, então cada serviço escolhe o seu.
a ideia
O paper não propõe sistema. Ele propõe um inventário de barreiras, dividido em duas listas: quatro desafios fundamentais e quatro operacionais. A separação importa. Fundamental é o que nenhuma engenharia melhor resolve dentro do modelo atual; operacional é o que dói mas é, em princípio, questão de esforço.
O movimento central do argumento é este: quase toda a pesquisa citada assume um serviço monolítico. O Facebook é visto de fora como um serviço só, mas por dentro é um agregado de serviços heterogêneos que cooperam. E não é a heterogeneidade em si que trava — é a desagregação. Um mecanismo de consistência aqui teria que ser costurado por dentro de cada serviço e por dentro de cada canal de comunicação entre eles.
como funciona
Integrar entre serviços stateful é o primeiro desafio. Mesmo que cada cache fosse linearizável, o resultado composto ainda teria inconsistência. Pior: serviços guardam o resultado de uma consulta a uma camada abaixo — a lista de quem curtiu algo, um range scan, um cache negativo com o que não existia naquele momento. Rastrear dependência por objeto não cobre isso; num banco monolítico o equivalente é predicate lock ou range lock, e não está claro que dê para capturar essas dependências de forma ao mesmo tempo genérica e precisa entre serviços. Agregação piora tudo, porque as dependências de entrada de um resultado mudam muito mais rápido que o resultado.
Amplificação de query é o segundo. Um web server stateless monta a resposta com fork/join de subqueries — fan-out na casa das centenas, caminho crítico com dezenas de saltos, milhares de subqueries por requisição. Daí saem dois efeitos. O slowdown cascade: como cada serviço fatia por chave diferente, existe dependência de ordenação all-to-all entre shards, e a falha de um shard propaga para todos os shards do serviço vizinho. E os outliers de latência: a resposta só volta quando a última subquery volta, então o p99 de um serviço baixo vira efeito de primeira ordem no usuário — internamente eles monitoram até o percentil 99,99.
Linchpin objects são o terceiro. Celebridade, marca grande, ponto turístico: alta cardinalidade, alta taxa de leitura e alta taxa de escrita ao mesmo tempo. Eles quebram a premissa de sistemas que ganham throughput garantindo consistência por objeto ou por shard, e, como caem em boa parte das requisições, combinam com o segundo desafio de forma cruel.
O quarto é o benefício líquido. Consistência forte é mais fácil de programar em cima, mas o ganho para o usuário é proporcional à taxa em que ele observa anomalia — e essa taxa já é baixa. Se uma query em um milhão retorna resultado anômalo, remover essa classe vale menos do que valeria se a anomalia fosse frequente. Do outro lado da balança há latência a mais, e falha, em escala, é rotina.
o que isso custou
O paper admite o buraco: “atualmente nos falta o dado para avaliar” se menos anomalia compensa mais latência. É posição, não medição — HotOS, sete páginas, zero experimento. As barreiras operacionais também são confissões desconfortáveis. Caracterizar throughput de pior caso é difícil porque comportamento emergente entre serviços mal aparece antes de acontecer. O ambiente é poliglota: C++ predomina nos serviços, mas Python, Java, Haskell, D, Ruby e Go passam pelo Thrift, a orquestração é Hack e PHP, e a composição final acontece em JavaScript, Objective-C ou Java no aparelho — cada um com seu modelo de threading, num sistema que precisaria iniciar ou adiar comunicação. E os workarounds já existentes reduzem o ganho de um sistema genérico, ressalva que os próprios autores marcam como específica de um sistema maduro: num sistema novo eles não existiriam.
Há ainda um limite que o texto assume de saída: a discussão é sobre leitura consistente, não sobre escrita transacional. E a nota de rodapé descarta sem explorar a opção de atomicidade de falha em cima de eventual consistency.
onde isso aparece hoje
Este é o contraponto mais citável ao caminho oposto — Spanner e Megastore escolheram pagar a coordenação e vender a garantia; aqui a conclusão é que, com o dado espalhado em centenas de serviços, a conta muda de sinal. A parte de outliers estende para dentro de uma arquitetura desagregada o argumento de The Tail at Scale, que os autores citam como parente direto.
A observação mais afiada está no related work: Orbe e GentleRain melhoram o throughput do COPS rastreando dependência causal com granularidade mais grossa — e é justamente isso que agravaria o slowdown cascade. Melhorar uma dimensão do problema piora outra, e o paper existe para que essa troca fique explícita antes de alguém a descobrir em produção.