Estou fazendo um projeto que lida com banco de dados de documentos estruturados. Eu tenho uma árvore de categorias (~ 1000 categorias, até ~ 50 categorias em cada nível), cada categoria contém vários milhares (até, digamos, ~ 10.000) de documentos estruturados. Cada documento tem vários kilobytes de dados em alguma forma estruturada (eu preferiria YAML, mas também pode ser JSON ou XML).
Os usuários deste sistema realizam vários tipos de operações:
- recuperação desses documentos por ID
- pesquisar documentos por alguns dos atributos estruturados dentro deles
- editar documentos (ou seja, adicionar / remover / renomear / mesclar); cada operação de edição deve ser registrada como uma transação com algum comentário
- visualizar um histórico de alterações registradas para um documento específico (incluindo a visualização de quem, quando e por que alterou o documento, obtendo uma versão anterior - e provavelmente revertendo para esta se solicitado)
Claro, a solução tradicional seria usar algum tipo de banco de dados de documentos (como CouchDB ou Mongo) para este problema - no entanto, essa coisa de controle de versão (histórico) me levou a uma ideia selvagem - por que eu não deveria usar o git
repositório como um backend de banco de dados para este aplicativo?
À primeira vista, poderia ser resolvido assim:
- categoria = diretório, documento = arquivo
- obtendo documento por ID => alterando diretórios + lendo um arquivo em uma cópia de trabalho
- editar documentos com comentários de edição => fazer commits por vários usuários + armazenar mensagens de commit
- histórico => log git normal e recuperação de transações mais antigas
- search => essa é uma parte um pouco mais complicada, acho que exigiria a exportação periódica de uma categoria para um banco de dados relacional com indexação de colunas que permitiremos pesquisar por
Existem outras armadilhas comuns nesta solução? Alguém já tentou implementar esse back-end (ou seja, para qualquer framework popular - RoR, node.js, Django, CakePHP)? Esta solução tem alguma implicação possível no desempenho ou confiabilidade - ou seja, está provado que o git seria muito mais lento do que as soluções de banco de dados tradicionais ou haveria alguma armadilha de escalabilidade / confiabilidade? Eu presumo que um cluster de tais servidores que fazem push / pull do repositório uns dos outros deve ser bastante robusto e confiável.
Basicamente, diga-me se essa solução funcionará e por que funcionará ou não?
Respostas:
Responder à minha própria pergunta não é a melhor coisa a fazer, mas, como acabei desistindo da ideia, gostaria de compartilhar o raciocínio que funcionou no meu caso. Gostaria de enfatizar que esse raciocínio pode não se aplicar a todos os casos, então cabe ao arquiteto decidir.
Geralmente, o primeiro ponto principal que minha pergunta deixa passar é que estou lidando com um sistema multiusuário que funciona em paralelo, simultaneamente, usando meu servidor com um cliente fino (ou seja, apenas um navegador da web). Dessa forma, tenho que manter o estado para todos eles. Existem várias abordagens para este, mas todas elas são muito difíceis em recursos ou muito complexas para implementar (e, assim, meio que matar o propósito original de descarregar todo o material de implementação difícil para o git em primeiro lugar):
Abordagem "bruta": 1 usuário = 1 estado = 1 cópia de trabalho completa de um repositório que o servidor mantém para o usuário. Mesmo que estejamos falando de um banco de dados de documentos razoavelmente pequeno (por exemplo, 100s MiBs) com ~ 100K de usuários, manter um clone de repositório completo para todos eles faz com que o uso do disco ultrapasse o limite (ou seja, 100K de usuários vezes 100MiB ~ 10 TiB) . E o que é ainda pior, clonar um repositório de 100 MiB a cada vez leva vários segundos, mesmo se feito de maneira bastante eficaz (ou seja, não usando por git e descompactando-reempacotando coisas), o que não é aceitável, IMO. E ainda pior - cada edição que aplicamos a uma árvore principal deve ser puxada para o repositório de cada usuário, o que é (1) consumo de recursos, (2) pode levar a conflitos de edição não resolvidos em casos gerais.
Basicamente, pode ser tão ruim quanto O (número de edições × dados × número de usuários) em termos de uso do disco, e esse uso do disco significa automaticamente um alto uso da CPU.
Abordagem "Apenas usuários ativos": mantenha uma cópia de trabalho apenas para usuários ativos. Dessa forma, você geralmente não armazena um repo-clone completo por usuário, mas:
Assim, o uso do disco, neste caso, atinge o pico em O (número de edições × dados × número de usuários ativos), que geralmente é ~ 100..1000 vezes menor que o número total de usuários, mas torna o login / logout mais complicado e mais lento , já que envolve a clonagem de um branch por usuário em cada login e puxando essas alterações de volta no logout ou expiração da sessão (o que deve ser feito de forma transacional => adiciona outra camada de complexidade). Em números absolutos, cai 10 TiBs de uso de disco para 10..100 GiBs no meu caso, isso pode ser aceitável, mas, novamente, agora estamos falando de um banco de dados bastante pequeno de 100 MiBs.
Abordagem de "checkout esparso": fazer "checkout esparso" em vez de clone repo completo por usuário ativo não ajuda muito. Isso pode economizar cerca de 10 vezes o uso de espaço em disco, mas às custas de uma carga muito maior de CPU / disco em operações que envolvem histórico, o que acaba com o propósito.
Abordagem de "pool de trabalhadores": em vez de fazer clones completos todas as vezes para a pessoa ativa, podemos manter um pool de clones "trabalhadores", prontos para serem usados. Dessa forma, toda vez que um usuário se loga, ele ocupa um "trabalhador", puxando lá seu branch do repo principal, e, ao se desconectar, ele libera o "trabalhador", que faz git hard reset inteligente para se tornar mais uma vez apenas um clone repo principal, pronto para ser usado por outro usuário que fizer login. Não ajuda muito com o uso do disco (ainda é muito alto - apenas clone completo por usuário ativo), mas pelo menos torna o login / logout mais rápido, à custa de ainda mais complexidade.
Dito isso, observe que calculei intencionalmente números de banco de dados e base de usuários relativamente pequenos: 100 mil usuários, mil usuários ativos, banco de dados total de 100 MiBs + histórico de edições, 10 MiBs de cópia de trabalho. Se você olhar para projetos de crowdsourcing mais proeminentes, há números muito mais altos lá:
Obviamente, para essa quantidade de dados / atividade, essa abordagem seria totalmente inaceitável.
Geralmente, teria funcionado, se alguém pudesse usar o navegador da web como um cliente "grosso", ou seja, emitindo operações git e armazenando praticamente o checkout completo no lado do cliente, não no lado do servidor.
Também há outros pontos que perdi, mas não são tão ruins em comparação com o primeiro:
Portanto, linha de fundo : é possível, mas para a maioria dos casos de uso atuais, não estará nem perto da solução ideal. Acumular sua própria implementação de histórico de edição de documento para SQL ou tentar usar qualquer banco de dados de documento existente seria provavelmente uma alternativa melhor.
fonte
De fato, uma abordagem interessante. Eu diria que se você precisa armazenar dados, use um banco de dados, não um repositório de código-fonte, que é projetado para uma tarefa muito específica. Se você pudesse usar o Git pronto para uso, tudo bem, mas provavelmente você precisará construir uma camada de repositório de documentos sobre ele. Portanto, você também pode construí-lo sobre um banco de dados tradicional, certo? E se você está interessado no controle de versão integrado, por que não usar uma das ferramentas de repositório de documentos de código aberto ? Há muito por onde escolher.
Bem, se você decidir ir para o back-end Git de qualquer maneira, então basicamente ele funcionaria para seus requisitos se você o implementasse conforme descrito. Mas:
1) Você mencionou "cluster de servidores que empurram / puxam uns aos outros" - pensei nisso por um tempo e ainda não tenho certeza. Você não pode empurrar / puxar vários repos como uma operação atômica. Eu me pergunto se poderia haver a possibilidade de alguma confusão de mesclagem durante o trabalho simultâneo.
2) Talvez você não precise, mas uma funcionalidade óbvia de um repositório de documentos que você não listou é o controle de acesso. Você pode restringir o acesso a alguns caminhos (= categorias) por meio de submódulos, mas provavelmente não será capaz de conceder acesso no nível do documento facilmente.
fonte
meu valor de 2 pence. Um pouco de saudade, mas ... tive um requisito semelhante em um de meus projetos de incubação. Semelhante ao seu, meus principais requisitos eram um banco de dados de documentos (xml no meu caso), com versionamento de documentos. Era para um sistema multiusuário com muitos casos de uso de colaboração. Minha preferência era usar soluções de código aberto disponíveis que atendessem à maioria dos principais requisitos.
Para ir direto ao ponto, não consegui encontrar nenhum produto que fornecesse ambos, de uma forma que fosse escalonável o suficiente (número de usuários, volumes de uso, armazenamento e recursos de computação). Eu estava inclinado para o git por toda a capacidade promissora, e soluções (prováveis) que alguém poderia criar a partir dele. Conforme eu brincava mais com a opção git, mudar de uma perspectiva de usuário único para uma perspectiva de vários (milli) usuários tornou-se um desafio óbvio. Infelizmente, não consegui fazer uma análise de desempenho substancial como você fez. (.. preguiçoso / saia cedo .... para a versão 2, mantra) Poder para você !. De qualquer forma, minha ideia tendenciosa se transformou na próxima alternativa (ainda tendenciosa): uma malha de ferramentas que são as melhores em suas esferas separadas, bancos de dados e controle de versão.
Enquanto ainda está em andamento (... e ligeiramente negligenciado), a versão transformada é simplesmente esta.
Em essência, isso equivaleria a adicionar um plugin de controle de versão ao banco de dados, com alguma cola de integração, que você pode ter que desenvolver, mas pode ser muito mais fácil.
Como isso (deveria) funcionar é que as trocas de dados da interface multiusuário primária são feitas por meio do banco de dados. O SGBD tratará de todas as questões divertidas e complexas, como multiusuário, simultaneidade e, operações atômicas, etc. No backend, o VCS executaria o controle de versão em um único conjunto de objetos de dados (sem simultaneidade ou problemas com vários usuários). Para cada transação efetiva no banco de dados, o controle de versão é executado apenas nos registros de dados que teriam sido alterados efetivamente.
Quanto à cola de interface, ela terá a forma de uma função de interoperação simples entre o banco de dados e o VCS. Em termos de design, uma abordagem simples seria uma interface orientada a eventos, com atualizações de dados do banco de dados acionando os procedimentos de controle de versão (dica: assumindo Mysql, uso de triggers e sys_exec () blá blá ...). Em termos de complexidade de implementação, vai desde o simples e eficaz (por exemplo, scripts) até o complexo e maravilhoso (alguma interface de conector programada). Tudo depende de quão louco você quer ir com isso e quanto capital de suor você está disposto a gastar. Acho que um script simples deve fazer a mágica. E para acessar o resultado final, as várias versões de dados, uma alternativa simples é preencher um clone do banco de dados (mais um clone da estrutura do banco de dados) com os dados referenciados pela versão tag / id / hash no VCS. novamente, este bit será um simples trabalho de consulta / tradução / mapa de uma interface.
Ainda há alguns desafios e incógnitas a serem enfrentados, mas suponho que o impacto e a relevância da maioria deles dependerão em grande parte dos requisitos do aplicativo e dos casos de uso. Alguns podem acabar não sendo problemas. Alguns dos problemas incluem a correspondência de desempenho entre os 2 módulos principais, o banco de dados e o VCS, para um aplicativo com atividade de atualização de dados de alta frequência, escalonamento de recursos (armazenamento e poder de processamento) ao longo do tempo no lado git como os dados e usuários crescer: estável, exponencial ou eventualmente platô
Do coquetel acima, aqui está o que estou preparando no momento
Alguns fatos divertidos - git realmente faz coisas claras para otimizar o armazenamento, como compressão e armazenamento de apenas deltas entre a revisão de objetos - SIM, git armazena apenas changesets ou deltas entre revisões de objetos de dados, onde é aplicável (ele sabe quando e como) . Referência: packfiles, no fundo do interior do Git - Revisão do armazenamento de objeto do git (sistema de arquivos endereçável por conteúdo), mostra semelhanças impressionantes (da perspectiva do conceito) com bancos de dados noSQL, como mongoDB. Novamente, às custas de suor de capital, pode fornecer possibilidades mais interessantes para integrar o 2 e ajustes de desempenho
Se você chegou até aqui, deixe-me se o acima pode ser aplicável ao seu caso, e supondo que seria, como se enquadraria em alguns dos aspectos em sua última análise de desempenho abrangente
fonte
Implementei uma biblioteca Ruby em cima da
libgit2
qual torna muito fácil de implementar e explorar. Existem algumas limitações óbvias, mas também é um sistema bastante libertador, pois você obtém o conjunto de ferramentas git completo.A documentação inclui algumas idéias sobre desempenho, compensações, etc.
fonte
Como você mencionou, o caso de vários usuários é um pouco mais complicado de lidar. Uma solução possível seria usar arquivos de índice Git específicos do usuário, resultando em
O truque é combinar a
GIT_INDEX_FILE
variável de ambiente do Git com as ferramentas para criar commits do Git manualmente:Segue um esboço da solução (hashes SHA1 reais omitidos dos comandos):
Dependendo dos seus dados, você pode usar um cron job para mesclar os novos refs,
master
mas a resolução do conflito é sem dúvida a parte mais difícil aqui.Ideias para tornar mais fácil são bem-vindas.
fonte