Pular para o conteúdo

Arquitetura real · Sênior

Dropbox Nucleus: motor de sync reescrito em Rust

Arquitetura real em produção. Pontos centrais: testes determinísticos.

17 min de leitura · Fonte: Dropbox · Conteúdo revisado em 23 de outubro de 2026

Resposta curta

Arquitetura real em produção. Pontos centrais: testes determinísticos. Um sync engine roda no computador do usuário e coordena o upload e o download dos arquivos para um sistema de arquivos remoto. Nucleus é o codinome do novo motor, reconstruído do zero para o cliente desktop ao longo de quatro anos.

01 · CONTEXTO

O que um sync engine faz e por que é difícil

Um sync engine roda no computador do usuário e coordena o upload e o download dos arquivos para um sistema de arquivos remoto. Nucleus é o codinome do novo motor, reconstruído do zero para o cliente desktop ao longo de quatro anos.

A escala é grande: o Dropbox tem centenas de bilhões de arquivos, trilhões de revisões e exabytes de dados de clientes. Além disso, clientes podem ficar offline por longos períodos e reconciliar as mudanças na volta, o que torna partições de rede operação normal em vez de exceção.

Um exemplo mostra como a durabilidade vai além de não corromper o servidor. O motor legado representava um move como um par de delete no local antigo e add no novo. Se o delete passasse e o add não, o usuário veria o arquivo sumido no servidor e nos outros dispositivos, embora só o tivesse movido localmente.

02 · POR QUE REESCREVER

Da dívida de modelo de dados ao Nucleus

Reescrever do zero tem má fama: Joel Spolsky chamou isso de maior erro estratégico que uma empresa de software pode cometer. O próprio Dropbox diz que a reescrita foi muito difícil e que só foi uma ótima ideia pelo cuidado com que o processo foi conduzido.

A causa mais importante dos problemas do motor legado era o modelo de dados, que nasceu para um mundo sem compartilhamento, com arquivos sem identificador estável entre moves. Refatorar sozinho não muda o modelo de dados fundamental, e as melhorias de desempenho no uso de memória, como aumentar o número de arquivos gerenciados, continuaram elusivas.

AspectoMotor legado (Classic)Nucleus
Identidade dos nósIndexados pelo caminho completoMove é uma atualização nos atributos do nó
O que se persisteO trabalho pendente de cada arquivo ou pastaObservações do estado
ConcorrênciaLocks globais para coordenar componentesQuase todo o código em uma única thread
Estados do protocoloMetadados de um arquivo podiam chegar antes do diretório paiMesma visão do remoto antes de considerar uma mutação

03 · MODELO DE DADOS

Três árvores em vez de trabalho pendente

O motor legado persistia o trabalho pendente de cada arquivo ou pasta. O Nucleus persiste observações: mantém três árvores, cada uma consistente em si, das quais o comportamento correto de sync pode ser derivado.

  1. Duas árvores descrevem o mundo real: a remota, com o estado mais recente na nuvem, e a local, com o último estado observado em disco.

    Diagrama da arquitetura: Nucleus: as três árvores e o planejador
    DBÁrvore remota
    DBÁrvore local
    DBÁrvore sincronizada
    CPPlanejador
  2. A árvore sincronizada guarda o último estado conhecido como totalmente sincronizado entre as duas e pode ser vista como uma base de merge, como no controle de versão.

    Diagrama da arquitetura: Nucleus: as três árvores e o planejador
    DBÁrvore remota
    DBÁrvore local
    DBÁrvore sincronizada
    CPPlanejador
  3. O planejador recebe as três árvores como entrada e produz operações para convergi-las de forma incremental.

    Diagrama da arquitetura: Nucleus: as três árvores e o planejador
    DBÁrvore remota
    DBÁrvore local
    DBÁrvore sincronizada
    CPPlanejador
  4. Se a árvore local coincide com a base de merge, um arquivo presente só na remota foi adicionado na nuvem. Sem a árvore sincronizada não seria possível distinguir isso de uma exclusão local.

    Diagrama da arquitetura: Nucleus: as três árvores e o planejador
    DBÁrvore remotanovo só aqui
    DBÁrvore local
    DBÁrvore sincronizada
    CPPlanejador
  5. O objetivo do sistema é convergir as três árvores para o mesmo estado, e isso vira uma regra testável: quaisquer que sejam as árvores iniciais, elas devem convergir.

    Diagrama da arquitetura: Nucleus: as três árvores e o planejador
    DBÁrvore remota
    DBÁrvore local
    DBÁrvore sincronizada
    CPPlanejador

Identificar nós por um id único também muda o move. No legado, os nós eram indexados pelo caminho completo; no Nucleus, um move é apenas uma atualização nos atributos do nó movido, e uma invariante testada é que qualquer pasta movida é visível em exatamente um lugar. Outra invariante, de consistência: no banco, nenhum arquivo ou pasta existe, nem transitoriamente, sem um diretório pai. Esse é o princípio de projetar para eliminar estados inválidos.

No caso de duas pastas movidas offline que formariam um ciclo, o motor legado duplicava cada diretório. O Nucleus mantém as pastas originais, e a ordem final depende de qual motor envia o seu move primeiro.

04 · CONCORRÊNCIA E PROTOCOLO

Uma thread de controle e um protocolo estrito

No motor legado, o agendamento ficava a cargo do SO, e isso tornava os testes de integração não reproduzíveis. Os componentes podiam criar threads livremente e a coordenação usava uma série de locks globais.

  1. Quase todo o código do Nucleus roda em uma única thread de controle, o que reduz o que o desenvolvedor precisa considerar ao adicionar uma funcionalidade.

    Diagrama da arquitetura: Nucleus: thread de controle e servidor
    CPThread de controle
    CPThreads auxiliares
    DBBanco local do cliente
    CPServidor Dropbox
    DBBanco de metadados
    DBArmazenamento de conteúdo
  2. A thread de controle é projetada para ser totalmente determinística quando entradas e decisões de agendamento são fixas.

    Diagrama da arquitetura: Nucleus: thread de controle e servidor
    CPThread de controle
    CPThreads auxiliares
    DBBanco local do cliente
    CPServidor Dropbox
    DBBanco de metadados
    DBArmazenamento de conteúdo
  3. O que não fica na thread de controle é delegado: IO de rede para um event loop, hashing para um pool de threads e IO de filesystem para uma thread dedicada.

    Diagrama da arquitetura: Nucleus: thread de controle e servidor
    CPThread de controle
    CPThreads auxiliares
    DBBanco local do cliente
    CPServidor Dropbox
    DBBanco de metadados
    DBArmazenamento de conteúdo
  4. O cliente conversa com o servidor por um protocolo que garante a mesma visão do sistema de arquivos remoto antes de considerar uma mutação.

    Diagrama da arquitetura: Nucleus: thread de controle e servidor
    CPThread de controle
    CPThreads auxiliares
    DBBanco local do cliente
    CPServidor Dropbox
    DBBanco de metadados
    DBArmazenamento de conteúdo
  5. No servidor, o backend inclui um banco de metadados e o armazenamento de conteúdo dos arquivos, entre outros serviços.

    Diagrama da arquitetura: Nucleus: thread de controle e servidor
    CPThread de controle
    CPThreads auxiliares
    DBBanco local do cliente
    CPServidor Dropbox
    DBBanco de metadados
    DBArmazenamento de conteúdo

O protocolo cliente-servidor foi redesenhado com consistência forte: arquivos e pastas aceitam moves atômicos independentes do tamanho da subárvore, e qualquer discrepância entre a visão do cliente e a do servidor é um bug.

O Nucleus foi escrito em Rust, que permite codificar invariantes no sistema de tipos e fazer o compilador checá-las.

05 · TESTES DETERMINÍSTICOS

Testes aleatórios que sempre se reproduzem

A regra é que todos os frameworks de teste aleatório sejam totalmente determinísticos e fáceis de reproduzir. Cada execução gera uma seed, usa um gerador pseudoaleatório com essa seed para todas as decisões aleatórias e, se o teste falha, imprime a seed.

Toda noite rodam dezenas de milhões de execuções aleatórias. Quando uma regressão aparece, o CI cria automaticamente uma tarefa de acompanhamento para cada seed que falha. Para manter a garantia, até o HashMap padrão do Rust foi trocado por um hasher determinístico. O Dropbox também roda milhões de cenários por dia em sua infraestrutura de testes.

AspectoCanopyCheckTrinity
AlvoIdentificar bugs no planejadorDescobrir corridas (races) desse tipo antes do deploy
Como rodaFinge que cada operação teve sucesso, atualizando as árvoresAgita o sistema com falhas, reordenação de respostas e crashes
MinimizaçãoBusca a menor entrada que reproduz a falhaNão consegue minimizar tão facilmente

O CanopyCheck embaralha o conjunto de operações para verificar que a ordem não importa, e impõe invariantes adicionais além da convergência. Uma delas: um arquivo presente só na árvore remota deve existir nas três árvores ao fim do teste. Foi o CanopyCheck que expôs o bug do ciclo de diretórios Archives/Drafts/January.

  1. O Trinity é essencialmente um executor customizado do future que representa o Nucleus, e assim agenda a si mesmo e ao motor na mesma thread.

    Diagrama da arquitetura: Nucleus sob teste: o Trinity
    CPTrinity
    CPNucleus
    CHFilesystem em memória
    CPServidor simulado
    CPRelógio simulado
    NWCI
  2. O filesystem nativo é trocado por um mock em memória, o que permite cerca de dez vezes mais execuções de teste.

    Diagrama da arquitetura: Nucleus sob teste: o Trinity
    CPTrinity
    CPNucleus
    CHFilesystem em memória
    CPServidor simulado
    CPRelógio simulado
    NWCI
  3. O backend do servidor é trocado por um mock em Rust, o que permite reordenar, atrasar e falhar qualquer RPC.

    Diagrama da arquitetura: Nucleus sob teste: o Trinity
    CPTrinity
    CPNucleus
    CHFilesystem em memória
    CPServidor simulado
    CPRelógio simulado
    NWCI
  4. O relógio também é simulado: o Trinity pode interceptar um timeout, avançar o tempo e disparar o timeout quando quiser.

    Diagrama da arquitetura: Nucleus sob teste: o Trinity
    CPTrinity
    CPNucleus
    CHFilesystem em memória
    CPServidor simulado
    CPRelógio simulado
    NWCI
  5. Com tudo sob controle, o Trinity modifica os sistemas de arquivos local e remoto, reordena respostas, injeta falhas de filesystem e de rede e simula crashes.

    Diagrama da arquitetura: Nucleus sob teste: o Trinity
    CPTrinity
    CPNucleussob agitação
    CHFilesystem em memória
    CPServidor simulado
    CPRelógio simulado
    NWCI
  6. Se uma execução falha, a seed é impressa e o CI abre uma tarefa de acompanhamento para ela.

    Diagrama da arquitetura: Nucleus sob teste: o Trinity
    CPTrinity
    CPNucleus
    CHFilesystem em memória
    CPServidor simulado
    CPRelógio simulado
    NWCI

Um exemplo de corrida que o Trinity busca: o motor apaga um arquivo e sobrescreve a mudança recente do usuário. O Trinity ajuda a descobrir corridas desse tipo antes do deploy.

06 · LIMITES E LIÇÕES

O que os testes não cobrem

Simular tanto cobra um preço. Rodar o Trinity contra o filesystem nativo custa cerca de dez vezes em desempenho, e o Trinity não consegue reiniciar a máquina no meio do teste, então não valida o uso de fsync para durabilidade em crash.

Há também risco alto de o comportamento do mock do servidor se afastar da realidade. Para testar o protocolo existe uma suíte separada, o Heirloom, que fala com um servidor Dropbox real e roda cerca de cem vezes mais lento que o Trinity. Além disso, o Trinity não consegue minimizar casos de teste que falham tão facilmente quanto o CanopyCheck.

O alvo da reescrita teve mais de uma década para se estabilizar, e era preciso evitar regredir mais de dez anos de correções. Foram os frameworks determinísticos que permitiram fazer isso com segurança.

Perguntas frequentes

O que é Dropbox Nucleus: motor de sync reescrito em Rust?

Arquitetura real em produção. Pontos centrais: testes determinísticos.

O que o design de Dropbox Nucleus: motor de sync reescrito em Rust cobre?

Ele está organizado nestas seções: O que um sync engine faz e por que é difícil, Da dívida de modelo de dados ao Nucleus, Três árvores em vez de trabalho pendente, Uma thread de controle e um protocolo estrito, Testes aleatórios que sempre se reproduzem e O que os testes não cobrem.

Quanto tempo leva Dropbox Nucleus: motor de sync reescrito em Rust e qual é o nível?

Leva cerca de 17 minutos para ler e tem nível Sênior.

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.