Pular para o conteúdo

Arquitetura real · Pleno

Discord: trilhões de mensagens

Partições quentes, pausas de GC e a migração de Cassandra para ScyllaDB com um data service em Rust.

18 min de leitura · Fonte: Discord · Conteúdo revisado em 22 de outubro de 2026

Resposta curta

Partições quentes, pausas de GC e a migração de Cassandra para ScyllaDB com um data service em Rust. O Discord decidiu cedo guardar todo o histórico de chat para sempre, para que o usuário volte a qualquer momento e veja seus dados em qualquer aparelho.

01 · CONTEXTO

Um banco para mensagens que nunca são apagadas

O Discord decidiu cedo guardar todo o histórico de chat para sempre, para que o usuário volte a qualquer momento e veja seus dados em qualquer aparelho. Isso pedia um banco que crescesse só adicionando nós, com bom modelo de dados para ler mensagens por canal.

A escolha foi o Cassandra. A chave de partição era o canal, e as mensagens dentro dela ficavam ordenadas por um ID Snowflake, que carrega o horário. Para que uma partição não crescesse sem limite, o canal foi cortado em buckets de tempo.

  1. O cliente envia a mensagem para a API de mensagens.

    Diagrama da arquitetura: Discord: mensagens no Cassandra
    CLClientes
    CPAPI de mensagens
    DBRéplica A
    DBRéplica B
    DBRéplica C
  2. A API grava na partição formada pelo canal e por um bucket, uma janela de tempo estática. O bucket impede que a partição de um canal antigo cresça sem limite.

    Diagrama da arquitetura: Discord: mensagens no Cassandra
    CLClientes
    CPAPI de mensagensgrava
    DBRéplica A
    DBRéplica B
    DBRéplica C
  3. Cada partição é replicada em três nós, e as operações usam consistência de quórum.

    Diagrama da arquitetura: Discord: mensagens no Cassandra
    CLClientes
    CPAPI de mensagens
    DBRéplica A
    DBRéplica B
    DBRéplica C

Em 2017 o cluster tinha 12 nós, com fator de replicação três. No início de 2022 ele já tinha 177 nós e guardava trilhões de mensagens. O volume não era o problema principal: era a forma como ele se distribuía entre os canais.

02 · O QUE QUEBROU

Partições quentes

Um servidor de amigos envia ordens de grandeza menos mensagens que um servidor com centenas de milhares de pessoas. No Cassandra, leitura custa mais que escrita, pois consulta a memória e possivelmente vários arquivos em disco. Muitas leituras concorrentes no mesmo canal criam uma partição quente.

  1. Um anúncio chega a um servidor enorme e todos os membros abrem o mesmo canal.

    Diagrama da arquitetura: Discord: partição quente
    CLMembros do servidor
    CPAPI de mensagens
    DBRéplica A
    DBRéplica B
    DBRéplica C
  2. As leituras caem todas na mesma partição, nas mesmas três réplicas.

    Diagrama da arquitetura: Discord: partição quente
    CLMembros do servidor
    CPAPI de mensagens
    DBRéplica Amesma partição
    DBRéplica Bmesma partição
    DBRéplica Cmesma partição
  3. As réplicas saturam. Como guardam também outros canais e as operações usam quórum, a latência sobe até para quem não tem relação com o anúncio.

    Diagrama da arquitetura: Discord: partição quente
    CLMembros do servidor
    CPAPI de mensagensafetada
    DBRéplica Asaturada
    DBRéplica Bsaturada
    DBRéplica Csaturada

Adicionar nós não resolve esse problema. Espalhar partições diferentes ajuda no volume total, mas uma partição não se divide entre nós, e o canal popular continua preso às mesmas réplicas. Esse é um antipadrão de desempenho clássico: o gargalo está no padrão de acesso, não na capacidade total.

03 · O CUSTO OPERACIONAL

Compactação atrasada e pausas de GC

O Cassandra grava em arquivos imutáveis em disco, as SSTables, e depende de compactação em segundo plano para manter as leituras rápidas. Com nós sobrecarregados, a equipe ficava atrasada nas compactações, e as leituras ficavam mais caras.

  1. Cada escrita vai à memória e depois a arquivos imutáveis em disco (SSTables); a compactação junta esses arquivos em segundo plano.

    Diagrama da arquitetura: Discord: compactação e pausas de GC
    CPAPI de mensagens
    DBNó ASSTables
    DBNó B
    DBNó C
  2. Com o nó sobrecarregado, a compactação atrasa e cada leitura consulta mais arquivos, ficando mais lenta.

    Diagrama da arquitetura: Discord: compactação e pausas de GC
    CPAPI de mensagens
    DBNó Acompactação atrasada
    DBNó B
    DBNó C
  3. Uma pausa longa de coleta de lixo tira o nó A do ar, e os nós B e C absorvem a carga dele.

    Diagrama da arquitetura: Discord: compactação e pausas de GC
    CPAPI de mensagens
    DBNó Apausa de GC
    DBNó Bsobrecarga
    DBNó Csobrecarga

Por ser escrito em Java, o banco exigia ajuste constante da coleta de lixo, e as pausas geravam picos de latência. Em casos extremos um operador precisava reiniciar e acompanhar o nó manualmente. Esse tipo de custo só aparece para quem tem boa observabilidade.

04 · A DECISÃO

ScyllaDB e um data service na frente do banco

A equipe migrou para o ScyllaDB, compatível com o Cassandra e escrito em C++, portanto sem coletor de lixo. Foi uma decisão de seleção de banco guiada pelo problema real, e ainda assim o ScyllaDB também pode ter partições quentes.

Por isso entraram os data services: serviços intermediários, escritos em Rust, entre a API e o banco, para controlar a concorrência sobre as partições quentes. A função principal deles é o request coalescing, uma forma de cache de curtíssima duração: o primeiro pedido cria uma tarefa e os seguintes se inscrevem nela, de modo que o banco recebe uma consulta só. Na prática, isso reduz de forma significativa os picos de tráfego contra o banco.

  1. Entre a API e o banco entra um data service. A aplicação deixa de falar direto com o armazenamento.

    Diagrama da arquitetura: Discord: data service e ScyllaDB
    CLClientes
    CPAPI de mensagens
    CPData service (Rust)novo
    DBScyllaDB 1
    DBScyllaDB 2
    DBScyllaDB 3
  2. Os pedidos são roteados por canal com hashing consistente, e todos os pedidos de um canal caem na mesma instância do data service.

    Diagrama da arquitetura: Discord: data service e ScyllaDB
    CLClientes
    CPAPI de mensagens
    CPData service (Rust)hash por canal
    DBScyllaDB 1
    DBScyllaDB 2
    DBScyllaDB 3
  3. Pedidos idênticos simultâneos viram uma consulta ao banco (request coalescing).

    Diagrama da arquitetura: Discord: data service e ScyllaDB
    CLClientes
    CPAPI de mensagens
    CPData service (Rust)agrupa pedidos
    DBScyllaDB 1
    DBScyllaDB 2
    DBScyllaDB 3
  4. O armazenamento muda para ScyllaDB: compatível com Cassandra, escrito em C++, sem coletor de lixo.

    Diagrama da arquitetura: Discord: data service e ScyllaDB
    CLClientes
    CPAPI de mensagens
    CPData service (Rust)
    DBScyllaDB 1novo
    DBScyllaDB 2novo
    DBScyllaDB 3novo

Mesmo assim, o Cassandra ainda tinha partições quentes, só que com menos frequência; o data service comprou tempo para preparar o cluster ScyllaDB e executar a migração.

A migração precisava mover trilhões de mensagens sem downtime. Novos dados passaram a ser escritos nos dois bancos ao mesmo tempo, e um migrador reescrito em Rust chegou a mover mensagens a até 3,2 milhões por segundo. A validação comparou uma pequena porcentagem das leituras nos dois bancos, e a troca aconteceu em maio de 2022.

MedidaCassandraScyllaDB
Nós no cluster17772
p99 de leitura de mensagens históricas40 a 125 ms15 ms
p99 de escrita5 a 70 ms5 ms
Disco por nó4 TB em média9 TB
Coletor de lixoSim, na JVMNão, escrito em C++

05 · LIÇÕES PARA A ENTREVISTA

O que levar deste caso

A chave de partição é uma decisão de produto: ela reflete como os usuários acessam os dados, não só como você os grava. Proteger o banco de rajadas de leituras idênticas pode ser mais barato do que somar nós, e isolar a aplicação do armazenamento atrás de um serviço torna a troca de banco viável.

Trocar de banco não eliminou o problema de fundo: partições quentes continuam possíveis. A camada à frente do banco ainda é parte da solução. Na entrevista, comece pelo padrão de acesso e só depois decida entre escalar na horizontal e mudar a chave ou a camada de acesso.

Perguntas frequentes

O que é Discord: trilhões de mensagens?

Partições quentes, pausas de GC e a migração de Cassandra para ScyllaDB com um data service em Rust.

O que o design de Discord: trilhões de mensagens cobre?

Ele está organizado nestas seções: Um banco para mensagens que nunca são apagadas, Partições quentes, Compactação atrasada e pausas de GC, ScyllaDB e um data service na frente do banco e O que levar deste caso.

Quanto tempo leva Discord: trilhões de mensagens e qual é o nível?

Leva cerca de 18 minutos para ler e tem nível Pleno.

Agora projete você mesmo

Rode este caso como uma entrevista na escola de System Design: você toma as decisões, o tutor as questiona e você vê a nota do seu desenho.