Pular para o conteúdo principal

Por que o Facebook escolheu o Mercurial e não o Git?

Nem todo mundo sabe, mas o Facebook usa o Mercurial como controle de versão desde 2013. Antes, usou o Subversion e depois tentou o Git. Mas como o Git não deu conta do volume de código do seu repositório monolítico, resolveram investir no Mercurial e deu muito certo! Desde então, o Facebook é um grande colaborador do Mercurial, contribuindo com inúmeras melhorias ao projeto. Também são grandes colaboradores o Google e o Mozilla (Firefox).

Essa é a versão resumida da história. Mas o que aconteceu exatamente? Quais foram as limitações do Git que fizeram que fosse preterido em relação ao Mercurial? Vamos analisar esses pontos no artigo.

História

O repositório principal do Facebook é gigantesco. É um repositório monolítico que contém todos os projetos da empresa. São dezenas de milhões de linhas de código, centenas de milhares de arquivos, milhares de commits por semana, milhares de desenvolvedores acessando, com entrega em produção duas vezes por dia 1.

As necessidades crescentes de controle de versão do Facebook já eram preocupantes desde 2012, quando o Git começava a mostrar sinais de lentidão à medida que o repositório crescia. Foi montado um repositório Git de teste com milhões de commits, arquivos etc. e os resultados não foram satisfatórios. O problema foi compartilhado com a comunidade Git e sugestão obtida foi a divisão do repositório monolítico em partes menores 2. Porém, a ideia de que as limitações do controle de versão deveriam determinar a estrutura do código não agradou ao Facebook, que preferiu manter seu repositório monolítico e investir em alguma solução própria no controle de versão.

Conforme relatado em um post de 2014 1, a tentativa inicial foi fazer o Git funcionar na escala gigante. No entanto, após longos estudos, decidiu-se que o Git seria muito difícil de ser estendido e o Mercurial foi escolhido em seu lugar.

Os motivos da escolha do Mercurial foram 1:

  1. O Mercurial é um controle de versão distribuído com funcionalidades equivalentes ao Git
  2. Seu modelo de armazenamento de dados é mais adequado à escalabilidade que o do Git
  3. Por ser escrito em Python, é muito mais fácil de ser estendido.
  4. A comunidade do Mercurial foi muito mais receptiva aos problemas de escalabilidade do Facebook e passou a considerar questões de escalabilidade no design de novas funcionalidades.

A colaboração do Facebook no Mercurial trouxe grandes melhorias de desempenho 1 e novas funcionalidades ao Mercurial através de melhorias em algoritmos, código fonte e novas extensões tais como remotefilelog e smartlog (veja https://bitbucket.org/facebook/hg-experimental).

Mais recentemente, em outubro de 2016, houve relatos de uma mudança cultural no Facebook em relação ao Mercurial 3:

"Facebook relatou melhorias significativas do sentimento dos desenvolvedores em relação ao Mercurial no Facebook. Inicialmente, muitos desenvolvedores estavam cépticos sobre o Mercurial e preferiam o Git. Agora, aparentemente um grande número de desenvolvedores esqueceu como usar o Git. Vários desenvolvedores que trabalham em projetos open source no GitHub (que nada mais são do que mirrors de subdiretórios do repositório monolítico do Facebook) agora preferem trabalhar em clones do Mercurial ao invés do Git."

Diferenças Arquiteturais entre Git e Mercurial no Armazenamento de Dados

Entre os fatores que mais pesam na escalabilidade está o modelo de armazenamento de dados. No Git, uma revisão contém os seguintes objetos:

  • Blob. Registra o conteúdo de um arquivo do projeto.
  • Tree. Corresponde a um diretório Unix. Contém a lista de nomes e permissões dos arquivos do diretório, com apontadores para objetos blob e outros objetos tree em caso de subdiretórios.
  • Commit. Registra informações da revisão tais como autor, ponteiro para um objeto tree com a configuração da revisão (estado do conjunto de arquivos do projeto em um determinado momento) e referências para outros objetos commit anteriores no grafo.

Cada um desses objetos é registrado em um arquivo distinto. Quanto maior o repositório, maior será o número de arquivos de commits, árvores e blobs.

Outro agravante é que o número de objetos desse modelo é particularmente sensível a mudanças. A modificação em um arquivo pode exigir a criação de vários outros objetos tree. Isso acontece porque o número hash que identifica um objeto tree é calculado a partir do seu conteúdo, que depende do hash dos objetos blob e outros objetos tree referentes aos subdiretórios, cujos identificadores hash também dependem do seu conteúdo. Recursivamente. A mudança em um arquivo em um subdiretório exige a criação de novos objetos tree para todos os diretórios acima.

Operações como log, blame e diff precisam iterar por vários objetos commit, tree e blob para obter seus resultados. À medida que o repositório aumenta, o tempo de resposta dessas operações degrada perceptivelmente 4.

Segundo o próprio Linus Torvalds 5:

Git fundamentalmente nunca realmente olha menos que todo o repositório. Mesmo se você limitar as coisas um pouco (isto é, baixar só uma parte ou voltar o histórico só um pouco), git ainda assim acaba se preocupando com a coisa toda e carregando o conhecimento ao redor.

Então, o git escala muito mal se você forçá-lo a olhar tudo como um repositório enorme. Eu não acho que essa parte seja corrigível, mas nós provavelmente possamos melhorá-la.

Mas o que muda no Mercurial?

O Mercurial usa um esquema de armazenamento de dados chamado de Revlog, que permite que todas as operações comuns de controle de versão sejam executadas rapidamente, mantendo a compressão e robustez 6.

Independente do número de revisões, o histórico de um arquivo do projeto é mantido em apenas dois arquivos: um arquivo de índice (com extensão .i) e outro de dados (extensão .d). A cada revisão, um delta ou o conteúdo inteiro do arquivo é acrescentado ao final do arquivo .d dependendo do esforço necessário para reconstituir a revisão. O arquivo .i armazena informações como o offset e o tamanho de cada revisão no arquivo .d correspondente.

Ainda há um par de arquivos de índice e dados para registrar todas as revisões (changeset), e outro par para registrar a lista de todos os arquivos de cada revisão (manifest).

Independente da quantidade de revisões, há sempre um número fixo de arquivos a serem processados: um par de arquivos de changeset, outro par para o manifest e um par para cada arquivo do projeto. Assim, o tempo de resposta não degrada como no Git, mesmo com o aumento do repositório.

Considerações Finais

De modo geral, todo controle de versão distribuído (DVCS) não se comporta bem com repositórios gigantes. Além do problema em manter localmente o histórico completo, há a questão do número de revisões e arquivos do projeto. Por outro lado, repositórios gigantes ainda são raros. Mas os desafios que oferecem vão direcionar a evolução das ferramentas de controle de versão.

O Mercurial saiu na frente e faz alguns anos. Houve uma melhoria significativa no seu desempenho depois do início da colaboração do Facebook, do Google e Mozilla no projeto. O Mercurial agora é muito rápido e já é usado em repositório gigantes com sucesso.

O Mercurial é uma alternativa extremamente viável ao Git.

O Git não está parado, mas só recentemente houve algum avanço na escalabilidade. Em 03/02/2017, a Microsoft (surpresa!) anunciou a criação do GVFS (Git Virtual File System) 7 que virtualiza o sistema de arquivos do Git fazendo parecer que todos os arquivos estão presentes. Foram relatadas melhorias significativas no desempenho para caso da Microsoft. Por enquanto, o GFVS só funciona com o TFS e em repositórios hospedados no Team Server 8.

Interessante notar que tanto a solução do Mercurial com o remotefilelog, quanto essa mais recente da Microsoft e seu GVFS apontam para uma volta à centralização do controle de versão.

Referências

1 Scaling Mercurial at Facebook
2 Git performance results on a large repository
3 Gregory Szorc about Mercurial 4.0 Sprint Notes
4 Mercurial vs Git: Scaling and Architecture
5 http://git.net/ml/git/2009-05/msg00051.html
6 Towards a Better SCM: Revlog and Mercurial
7 Announcing GVFS (Git Virtual File System)
8 More on GVFS

Comentários

Comments powered by Disqus