- Ao projetar sistemas, é praticamente impossível satisfazer ao mesmo tempo consistência perfeita, alta disponibilidade, baixa latência e alta taxa de transferência
- O importante é encontrar um ponto de equilíbrio adequado à aplicação para chegar a um projeto apropriado
- No design do feed/timeline de seguidores do Bluesky, foi adotado um trade-off que melhora o desempenho de escrita ao sacrificar parte da consistência
- Como resultado, houve uma redução de mais de 96% na latência P99 sem impacto negativo para os usuários
Fan-out da timeline
- Quando um usuário publica uma postagem no Bluesky, ela é indexada pelo sistema, armazenada no banco de dados e disponibilizada nas respostas da API
- Ao mesmo tempo, essa postagem passa por um processo de “fan-out”, sendo inserida na tabela de timeline de cada seguidor
- Isso é feito consultando a lista de seguidores e, em seguida, realizando inserções em ordem reversa na tabela de timeline de cada seguidor
- As tabelas de timeline são particionadas por usuário e armazenadas em um banco de dados distribuído (ScyllaDB), sendo replicadas em vários shards para alta disponibilidade
- Cada usuário pode ser alocado em um shard diferente
- Para economizar espaço de armazenamento, timelines que ultrapassam determinado tamanho removem periodicamente referências de postagens antigas
O problema dos hot shards
- O Bluesky tem cerca de 32 milhões de usuários, e o banco de dados de timelines é dividido em centenas de shards
- Em um sistema usado por milhões de pessoas, podem existir usuários com relações de following extremamente numerosas
- Exemplo: usuários que seguem centenas de milhares de contas
- Em um único shard, várias timelines de usuários ficam armazenadas juntas
- Quando um usuário específico gera muitas escritas, aquele shard entra em estado de sobrecarga (“hot shard”)
- Esses hot shards concentram operações de leitura ou escrita, fazendo com que a latência se propague também para outros usuários no mesmo shard
Acúmulo de latência
- Se um usuário tiver 2.000.000 de seguidores, gravar sequencialmente pode levar mais de 20 minutos
- Para reduzir isso, o fan-out pode ser paralelizado, encurtando a latência média
- Porém, latências P99 (em torno de 15 milissegundos ou mais) podem ocorrer repetidamente e atrasar todo o conjunto de tarefas paralelas
- Quando há seguidores demais, a latência P99 ou P99.9 pode fazer o tempo total de fan-out crescer, no pior caso, de vários minutos para dezenas de minutos
Timeline com perdas
- Para usuários que seguem gente demais, é irrealista mostrar todas as postagens exatamente na ordem correta
- Na prática, também é difícil para uma pessoa consumir todas essas postagens
- Por isso, para timelines de usuários cujo número de follows ultrapassa um certo limite (por exemplo,
reasonable_limit), foi introduzido um método que “descarta” probabilisticamente parte das escritas
- É usada a fórmula
loss_factor = min(reasonable_limit / num_follows, 1)
- Durante o fan-out, é gerado um valor aleatório e, se ele for maior que
loss_factor, a escrita na timeline é ignorada
- Com isso, limita-se a quantidade excessiva de escritas em timelines específicas e evita-se a degradação de desempenho do shard como um todo
Sobre cache
- Como as escritas de timeline passam de um milhão por segundo, consultar diretamente no banco de dados o número de follows do usuário a cada escrita geraria carga demais
- Em vez disso, contas com alta contagem de follows são armazenadas em cache no Redis como um sorted set
- As instâncias do serviço de fan-out carregam essas informações em memória a cada 30 segundos
- Com isso, mesmo durante o processo de fan-out é possível consultar rapidamente informações de usuários com muitos follows
- Como não é necessário que o cache esteja perfeitamente atualizado, o sistema aceita um pequeno grau de imperfeição em troca de mais desempenho e escalabilidade
Resultado
- Após a introdução da timeline com perdas, os hot shards praticamente desapareceram no banco de dados Timelines
- A latência P99 para processar uma página de fan-out caiu mais de 90%
- Considerando o tempo total do trabalho de fan-out, tarefas que levavam de 5 a 10 minutos no P99 passaram a levar menos de 10 segundos
- Isso mostra que, mesmo sacrificando parte da consistência, ainda é possível atender plenamente às expectativas dos usuários e manter escalabilidade em larga escala
- A arquitetura de timeline do Bluesky ainda tem espaço para melhorias, mas essa mudança já aumentou significativamente a taxa de transferência de escrita e a escalabilidade
1 comentários
Comentários do Hacker News
Como entusiasta de sistemas, sou o tipo de pessoa que gosta desse tipo de artigo. É fácil cair na mentalidade de que “precisa ser perfeito”
Fico pensando por que não implementar a timeline de forma híbrida com base na popularidade da conta
Solução interessante para um problema interessante. Obrigado por compartilhar
Tenho curiosidade sobre essa estratégia de sacrificar consistência. Fico pensando se há ideias para algo diferente de fanout completo na leitura ou na escrita
Não é necessário entregar tudo o que foi postado por milhares de usuários que todos seguem em ordem cronológica perfeita, mas faz sentido entregar conteúdo suficiente para que sempre haja conteúdo novo na timeline
Fico pensando como funcionaria limitar a quantidade de seguidores para evitar o problema de hot shard
A AWS tem uma abordagem geral interessante para esse problema
Contas que seguem centenas de milhares de usuários claramente são bots que raspam conteúdo. Eu bloquearia e pronto
Ao ir diretamente ao perfil de um usuário para ver todas as postagens, às vezes há postagens que deveriam estar na timeline e não estão
Fico pensando por que implementaram o fanout de um jeito em que cada “página” bloqueia a busca da próxima página