WriteGuards: suporte de armazenamento distribuído para caches fortemente consistentes
Título original: WriteGuards: Distributed Storage Support for Strongly Consistent Caches
Publicado20th USENIX Symposium on Operating Systems Design and Implementation (OSDI 26)
Saiu em uma revista, congresso ou livro, que normalmente avaliam o trabalho antes de aceitar. Mesmo assim, um único estudo nunca é a palavra final.
Resumo
Este paper não tem resumo disponível.
Análise de pesquisa
Escrita pelo Kurz, não pelos autores do paper. O resumo acima é dos próprios autores.
- Última revisão
- Versão
- 1.0
Visão geral
Muitos serviços de datacenter precisam do estado confirmado mais recente no caminho da requisição, por exemplo as permissões atuais antes de cada chamada. Lê-lo de um banco transacional custa idas e voltas na rede e caudas de vários milissegundos, enquanto os caches são rápidos, mas em geral só eventualmente consistentes. Este artigo do OSDI 2026, da UC Berkeley, da Databricks e do ICSI, pergunta como um cache pode devolver leituras linearizáveis da memória, continuando fracamente acoplado ao armazenamento, escalando para bilhões de chaves e sobrevivendo ao resharding.
O obstáculo é a anomalia das escritas atrasadas. Depois que um auto-sharder move uma faixa de chaves para um novo pod, uma escrita emitida pelo dono anterior ainda pode estar em trânsito, e, se ela for confirmada depois que o novo pod leu e guardou o valor antigo, o cache fica desatualizado. Os autores respondem com os WriteGuards, uma pequena primitiva de fencing no armazenamento, e constroem sobre ela o CLink (um cache em processo) e o CRink-R e o CRink-L (caches remotos).
Três termos se repetem. Um slice é uma faixa de chaves que o auto-sharder atribui a um pod. Propriedade forte (strong ownership) significa que no máximo um pod é dono de uma chave por vez. A Latest State Invariant (LSI) diz que um valor em cache só pode ser servido se for igual ao valor confirmado mais recente no armazenamento.
Vale conhecer antes: linearizabilidade, leases e fencing tokens, controle de concorrência otimista e bancos chave-valor particionados por faixa, como o TiDB (o sistema que os autores modificaram), o Spanner e o FoundationDB.
Contribuição
- A primitiva WriteGuard. Um tablet server mantém, em memória, um mapa de intervalos de faixas de chaves para valores de guarda opacos.
SetGuard(range, value)sobrescreve as guardas de uma faixa, e toda escrita carrega uma guarda que precisa bater com a instalada para a sua chave. Aplicações que omitem guardas combinam com a guarda vazia padrão, então os clientes existentes continuam funcionando. O mapa é estado volátil, apagado na reinicialização. - Por que as soluções existentes ficam aquém. O artigo examina verificar versões a cada leitura, transformar leituras em escritas de metadados, sequencers do Chubby por chave, fencing por timestamp no estilo do TrueTime e timeouts. Cada uma custa latência de leitura, exige estado por chave, depende de hardware de relógio especial ou apenas reduz a chance de uma escrita obsoleta.
- CLink. Um cache write-through ligado aos pods da aplicação. Em uma nova atribuição, o pod instala uma guarda nova sobre cada pedaço do slice do tamanho de um tablet e só então passa a guardar valores. Duas regras (as instalações precisam estar contidas em um período de propriedade e não se sobrepor) dão às guardas uma ordem total que coincide com a ordem no armazenamento, e uma terceira (toda instalação usa um valor novo) faz com que uma escrita atrasada de um dono anterior nunca possa coincidir.
- Regras de serviço. Uma escrita remove a chave do cache, e o resultado de uma leitura só é guardado se passar na checagem da LSI: havia uma guarda instalada, nenhuma escrita se sobrepôs a ela e a propriedade foi contínua. Todo acerto antes verifica se a propriedade não foi perdida.
- Chaves quentes. O auto-sharder isola uma chave quente e pode replicá-la. Um primário recebe todas as escritas e executa uma invalidação em duas fases entre as réplicas, e qualquer mudança na composição do grupo descarta as entradas em cache delas.
- CRink-R e CRink-L. O CRink-R executa o mesmo desenho como um serviço remoto compartilhado. O CRink-L guarda apenas versões em um pequeno serviço consistente e os valores em um cache lookaside comum, como o Redis: uma leitura busca os dois em paralelo e recorre ao armazenamento se as versões diferirem.
Metodologia
O cache tem cerca de 6.000 linhas de C++, e os autores escreveram um auto-sharder com propriedade forte (cerca de 12.000 linhas de Scala) inspirado no Centrifuge. Para o armazenamento, estenderam o TiDB 7.5.1 com o SetGuard e a checagem de escrita, alterando 717 linhas de Rust no TiKV, 229 linhas de Go na camada SQL e 57 linhas de Go no Placement Driver, o que estimam em cerca de um mês-pessoa.
Os experimentos rodaram em clusters de produção com 9 pods de TiKV, 6 de TiDB, 3 de PD e 6 pods de aplicação de 16 vCPUs. A latência é medida no servidor da aplicação. As cargas são os traces da Meta e do Twitter (chave-valor simples) e um trace interno do Unity Catalog, que tem 99,4% de leituras, valores de cerca de 23KB e de 6 a 8 buscas no armazenamento por requisição. As bases de comparação são o armazenamento direto, uma verificação de versão a cada leitura e o Chrono (Dropbox), configurado com o timestamp de tentativa de 5 segundos que ele recomenda. Além da comparação entre os três traces, os autores usam apenas o Unity Catalog e cargas sintéticas.
Resultados
Estes são os resultados relatados pelos autores.
- Throughput por núcleo. As leituras do CLink chegam a cerca de um milhão de operações por segundo por núcleo e a 22,8 milhões de QPS com 24 núcleos, escalando de forma quase linear. Os autores dizem que o protótipo não é otimizado e não pretende representar o estado da arte em desempenho de cache.
- Overhead dos WriteGuards. Com uma carga só de escritas e 75% de CPU no armazenamento, nem o throughput nem a latência são prejudicados. O SetGuard roda a cada mudança de atribuição, não a cada escrita.
- Latência de leitura (P90). O CLink chega a 0,5 µs a 4,2 µs, contra 4,8 ms a 10,3 ms do armazenamento e 3,3 ms a 9,0 ms das verificações de versão. O CRink-R e o CRink-L chegam a 0,65 ms a 3,2 ms, contra 2,3 ms a 5,5 ms do Chrono, o que os autores resumem como 3,6x a 5,2x menos que o armazenamento e 2,2x a 2,4x menos que o Chrono. O P99 fica de fora porque reflete sobretudo falhas de cache.
- Taxa de acerto do Chrono. Seus timestamps de 5 segundos deixam uma chave escrita sem poder ser cacheada por esse tempo, e os autores calculam que mais de uma escrita a cada 50 segundos em uma chave derruba a taxa de acerto abaixo de 90%.
- Tráfego de escrita. No trace do Unity Catalog com leituras convertidas em escritas, a latência do CLink se mantém estável conforme as escritas crescem. Em uma única chave quente, a latência sobe para 2 ms a 2,8 ms quando a taxa de acerto cai abaixo do percentil: o P99 com 80 QPS de escrita, o P90 e o P50 com 200 e 400.
- Resharding. Com 0,5% de churn de tráfego, o CLink mantém 100% de disponibilidade e baixa latência, porque as atribuições mudam em menos de 20 ms. Três serviços de produção moveram tipicamente menos de 0,2% da carga por minuto.
- Replicação. O auto-sharder adiciona réplicas conforme a carga de leitura sobe, e a latência de escrita cresce apenas de forma modesta. Os autores reduziram a capacidade dos pods para disparar 5 réplicas, o que normalmente exigiria mais de 80 milhões de QPS em uma chave.
- Reinicializações. Em 86 serviços particionados, reinicializações não planejadas acontecem cerca de uma vez a cada 750 a 2.000 reinicializações. Para um serviço de 100 pods, 1% das chaves fora do ar por 30 segundos ainda deixa a disponibilidade diária acima de 99,999%.
Análise crítica
O artigo é franco sobre os custos de seu desenho. Ele admite três.
- A propriedade forte custa disponibilidade. Depois de uma queda não planejada, o lease antigo precisa expirar, o que deixa a faixa indisponível por dezenas de segundos. Os autores argumentam que isso é raro, usando dados de reinicializações da frota.
- Chaves quentes com escrita perdem o cache. O CLink não guarda uma chave enquanto uma escrita se sobrepõe a ela, então uma chave muito escrita é servida do armazenamento, e as réplicas acrescentam latência de invalidação às escritas.
- Limites de escopo e do protótipo. O artigo cobre um único datacenter, descarta os metadados de guarda na migração de tablets, deixa sem exploração o limite de guardas retidas e diz que seus números de throughput não refletem um cache otimizado.
Os pontos a seguir são uma leitura das evidências, não afirmações do artigo.
- A correção é argumentada em prosa. A seção 6.4 argumenta que os valores satisfazem a LSI na entrada e na hora de servir. Não encontrei prova mecanizada, verificação de modelo nem teste de linearizabilidade relatados, e o argumento pressupõe a consistência de atribuição e a checagem barata de propriedade do auto-sharder dos próprios autores.
- As quedas são estimadas, não medidas. O experimento de resharding cobre movimentos planejados. Nenhum experimento que li derruba um pod e mede a janela de expiração do lease, então a afirmação de disponibilidade se apoia em estatísticas da frota.
- Um único sistema de armazenamento. O "fácil de adicionar" se apoia em cerca de 1.000 linhas alteradas no TiDB, cujos tablets já são faixas de chaves. O overhead dos WriteGuards vem de uma única rodada só de escritas, e o Unity Catalog é um trace interno que outros não podem reexecutar.
- Uma única base de comparação para caches remotos. O Chrono é comparado com seu padrão de 5 segundos, e a diferença mudaria com outro limite, o que não é explorado. O resumo também junta 2,2x a 2,4x em uma única afirmação contra o armazenamento e contra caches remotos, enquanto o corpo do texto dá 3,6x a 5,2x contra o armazenamento.
- O destaque é um P90 em traces com muita leitura. As "três ordens de grandeza" comparam memória no processo da aplicação com uma leitura pela rede. Nenhuma base de comparação é um cache eventualmente consistente, então o preço da consistência sobre um cache em processo comum não fica isolado.
- Alguns limites são afirmados. As taxas de escrita em uma chave que quebram o CLink "excedem de longe" as taxas de produção, mas nenhuma taxa é mostrada.
Pesquisas futuras
- Cache entre datacenters sobre bancos globais, que o artigo menciona e deixa fora do escopo.
- Limitar as guardas mantidas depois de splits e merges, e mover o estado das guardas junto com tablets em migração.
- As otimizações que o artigo lista: agrupamento de requisições, versões no cache, guardas por chave depois de uma escrita que falha e marcação das leituras com sua guarda.
- Um segundo sistema de armazenamento, com injeção de quedas e um verificador de linearizabilidade, e uma comparação com caminhos baseados em log para leituras linearizáveis em memória, como o Tango, cujas views verificam o fim do log antes de cada leitura.
Texto completo
Discussão · 0
Entre para participar da discussãoNenhum post ainda. Seja o primeiro a iniciar a discussão.