tipos tag remove practices etiquetas create best database git database-performance database-replication document-database

database - remove - git tags best practices



Usando el repositorio de git como base de datos (5)

Como mencionó, el caso de los usuarios múltiples es un poco más complicado de manejar. Una posible solución sería usar archivos de índice Git específicos del usuario que resulten en

  • no hay necesidad de copias de trabajo separadas (el uso del disco está restringido a los archivos modificados)
  • no es necesario realizar trabajos preparatorios que consumen mucho tiempo (por sesión de usuario)

El truco consiste en combinar la variable de entorno GIT_INDEX_FILE de Git con las herramientas para crear confirmaciones de Git manualmente:

A continuación se detalla una solución (hashes SHA1 reales omitidos de los comandos):

# Initialize the index # N.B. Use the commit hash since refs might changed during the session. $ GIT_INDEX_FILE=user_index_file git reset --hard <starting_commit_hash> # # Change data and save it to `changed_file` # # Save changed data to the Git object database. Returns a SHA1 hash to the blob. $ cat changed_file | git hash-object -t blob -w --stdin da39a3ee5e6b4b0d3255bfef95601890afd80709 # Add the changed file (using the object hash) to the user-specific index # N.B. When adding new files, --add is required $ GIT_INDEX_FILE=user_index_file git update-index --cacheinfo 100644 <changed_data_hash> path/to/the/changed_file # Write the index to the object db. Returns a SHA1 hash to the tree object $ GIT_INDEX_FILE=user_index_file git write-tree 8ea32f8432d9d4fa9f9b2b602ec7ee6c90aa2d53 # Create a commit from the tree. Returns a SHA1 hash to the commit object # N.B. Parent commit should the same commit as in the first phase. $ echo "User X updated their data" | git commit-tree <new_tree_hash> -p <starting_commit_hash> 3f8c225835e64314f5da40e6a568ff894886b952 # Create a ref to the new commit git update-ref refs/heads/users/user_x_change_y <new_commit_hash>

Dependiendo de sus datos, podría usar un trabajo cron para fusionar los nuevos refs para master pero la resolución de conflictos es posiblemente la parte más difícil aquí.

Las ideas para hacerlo más fácil son bienvenidas.

Estoy haciendo un proyecto que se ocupa de la base de datos de documentos estructurados. Tengo un árbol de categorías (~ 1000 categorías, hasta ~ 50 categorías en cada nivel), cada categoría contiene varios miles (hasta, digamos, ~ 10000) de documentos estructurados. Cada documento contiene varios kilobytes de datos de alguna forma estructurada (prefiero YAML, pero también puede ser JSON o XML).

Los usuarios de este sistema realizan varios tipos de operaciones:

  • recuperación de estos documentos por ID
  • buscando documentos por algunos de los atributos estructurados dentro de ellos
  • editar documentos (es decir, agregar / eliminar / renombrar / fusionar); cada operación de edición debe registrarse como una transacción con algún comentario
  • ver un historial de cambios registrados para un documento en particular (incluyendo ver quién, cuándo y por qué cambió el documento, obtener una versión anterior, y probablemente volver a este si se solicita)

Por supuesto, la solución tradicional sería utilizar algún tipo de base de datos documental (como CouchDB o Mongo) para este problema; sin embargo, este control de versiones (historia) me tentó a una idea descabellada: ¿por qué no debería usar el repositorio de git como un back-end de base de datos para esta aplicación?

A primera vista, podría resolverse así:

  • categoría = directorio, documento = archivo
  • obtener el documento por ID => cambiar directorios + leer un archivo en una copia de trabajo
  • editar documentos con editar comentarios => hacer commits por varios usuarios + almacenar mensajes de commit
  • history => log git normal y recuperación de transacciones anteriores
  • search => esa es una parte un poco más complicada, supongo que requeriría la exportación periódica de una categoría a la base de datos relacional con indexación de columnas que permitiremos buscar por

¿Hay otras trampas comunes en esta solución? ¿Alguien ha intentado implementar ese backend ya (es decir, para cualquier framework popular - RoR, node.js, Django, CakePHP)? ¿Esta solución tiene posibles implicaciones en el rendimiento o la confiabilidad, es decir, está demostrado que git sería mucho más lento que las soluciones de bases de datos tradicionales o que habría dificultades de escalabilidad / confiabilidad? Supongo que un grupo de tales servidores que empujan / jalan el repositorio de cada uno debe ser bastante robusto y confiable.

Básicamente, dime si esta solución funcionará y por qué funcionará o no.


Implementé una biblioteca de Ruby además de libgit2 que hace que esto sea bastante fácil de implementar y explorar. Hay algunas limitaciones obvias, pero también es un sistema bastante liberador ya que obtienes la cadena de herramientas completa de git.

La documentación incluye algunas ideas sobre el rendimiento, las compensaciones, etc.


Responder mi propia pregunta no es lo mejor que puedo hacer, pero, como al final abandoné la idea, me gustaría compartir el razonamiento que funcionó en mi caso. Me gustaría enfatizar que este razonamiento podría no aplicarse a todos los casos, por lo que depende del arquitecto decidir.

En general, el primer punto principal que mi pregunta echa de menos es que estoy tratando con un sistema multiusuario que funciona en paralelo, al mismo tiempo, usando mi servidor con un thin client (es decir, solo un navegador web). De esta manera, tengo que mantener el estado para todos ellos. Hay varios enfoques para este, pero todos son demasiado duros con los recursos o demasiado complejos para implementarlos (y, por lo tanto, matan el propósito original de descargar todo el material de implementación difícil a git en primer lugar):

  • Enfoque "embotado": 1 usuario = 1 estado = 1 copia de trabajo completa de un repositorio que el servidor mantiene para el usuario. Incluso si hablamos de una base de datos de documentos bastante pequeña (por ejemplo, 100 millones de MiB) con ~ 100K de usuarios, mantener el clon de repositorio completo para todos hace que el uso del disco se agote (es decir, 100K de usuarios por 100MiB ~ 10 TiB) . Lo que es aún peor, la clonación de 100 repositorios MiB cada vez lleva varios segundos, incluso si se realiza en un maneer bastante efectivo (es decir, no se usa por git y se desempaqueta y reempaqueta), lo que no es aceptable, IMO. Y lo que es peor, todas las ediciones que aplicamos a un árbol principal deben ser extraídas al repositorio de cada usuario, que es (1) recurso porcino, (2) puede dar lugar a conflictos de edición no resueltos en el caso general.

    Básicamente, podría ser tan malo como O (número de ediciones × datos × número de usuarios) en términos de uso del disco, y tal uso del disco automáticamente significa un uso de la CPU bastante alto.

  • Enfoque "Solo usuarios activos": mantenga la copia de trabajo solo para usuarios activos. De esta forma, generalmente no almacena un clon de repositorio completo por usuario, sino que:

    • Cuando el usuario inicia sesión, clonas el repositorio. Toma varios segundos y ~ 100 MiB de espacio de disco por usuario activo.
    • A medida que el usuario continúa trabajando en el sitio, trabaja con la copia de trabajo dada.
    • A medida que el usuario finaliza la sesión, su clon del repositorio se copia de nuevo en el repositorio principal como una sucursal, almacenando así solo sus "cambios no aplicados", si los hay, que son bastante eficientes desde el punto de vista del espacio.

    Por lo tanto, el uso del disco en este caso alcanza un máximo de O (número de ediciones × datos × número de usuarios activos), que suele ser ~ 100,1000 veces menor que el número de usuarios totales, pero hace que el inicio / cierre de sesión sea más complicado y más lento , ya que implica la clonación de una sucursal por usuario en cada inicio de sesión y volver a poner estos cambios al cerrar la sesión o al vencimiento de la sesión (lo que debe hacerse de forma transaccional => agrega otra capa de complejidad). En números absolutos, se reducen 10 TiB de uso de disco a 10.100 GiBs en mi caso, eso podría ser aceptable, pero, una vez más, ahora estamos hablando de una base de datos bastante pequeña de 100 MiB.

  • Enfoque de "salida dispersa": hacer una "extracción dispersa" en lugar de una clonación repo completa por usuario activo no ayuda mucho. Podría ahorrar ~ 10x de uso de espacio en disco, pero a expensas de una carga de CPU / disco mucho más alta en operaciones que implican historial, lo que mata el propósito.

  • Enfoque de "grupo de trabajadores": en lugar de hacer clones en toda regla para personas activas, podríamos mantener un grupo de clones de "trabajadores", listos para ser utilizados. De esta forma, cada vez que un usuario inicia sesión, ocupa un "trabajador", retirando su sucursal del repositorio principal y, mientras cierra la sesión, libera al "trabajador", que hace un reinicio completo inteligente para volver a ser solo un clon de repo principal, listo para ser utilizado por otro usuario que inicia sesión. No ayuda mucho con el uso del disco (aún es bastante alto, solo clonación completa por usuario activo), pero al menos hace que el inicio / cierre de sesión sea más rápido, como gasto de aún más complejidad.

Dicho esto, tenga en cuenta que intencionalmente calculé números de bases de datos y usuarios bastante pequeños: 100K usuarios, 1K usuarios activos, 100 MiBs base de datos total + historial de ediciones, 10 MIBs de copia de trabajo. Si observas proyectos de crowd-sourcing más prominentes, hay números mucho más altos allí:

│ │ Users │ Active users │ DB+edits │ DB only │ ├──────────────┼───────┼──────────────┼──────────┼─────────┤ │ MusicBrainz │ 1.2M │ 1K/week │ 30 GiB │ 20 GiB │ │ en.wikipedia │ 21.5M │ 133K/month │ 3 TiB │ 44 GiB │ │ OSM │ 1.7M │ 21K/month │ 726 GiB │ 480 GiB │

Obviamente, para esa cantidad de datos / actividad, este enfoque sería completamente inaceptable.

En general, habría funcionado, si uno pudiera usar el navegador web como un cliente "grueso", es decir, emitir operaciones de git y almacenar prácticamente todo el proceso de pago por parte del cliente, no por parte del servidor.

También hay otros puntos que me he perdido, pero no son tan malos en comparación con el primero:

  • El patrón mismo de tener un estado de edición de usuario "grueso" es controvertido en términos de ORM normales, como ActiveRecord, Hibernate, DataMapper, Tower, etc.
  • Por mucho que he buscado, no hay ninguna base de código libre existente para hacer ese enfoque a git desde frameworks populares.
  • Hay al menos un servicio que de alguna manera logra hacerlo de manera eficiente, obviamente es github , pero, por desgracia, su código base es de código cerrado y sospecho que no usan servidores git normales / técnicas de almacenamiento repo, es decir, básicamente implementaron git alternativo de "big data"

Por lo tanto, en conclusión : es posible, pero para la mayoría de los casos de uso actuales no estará cerca de la solución óptima. Desarrollar su propia implementación document-edit-history-to-SQL o tratar de usar cualquier base de datos de documentos existente sería probablemente una mejor alternativa.


Un enfoque interesante de hecho. Diría que si necesita almacenar datos, use una base de datos, no un repositorio de código fuente, que está diseñado para una tarea muy específica. Si puede usar Git de fábrica, está bien, pero probablemente necesite construir una capa de repositorio de documentos sobre él. Entonces, podrías construirlo sobre una base de datos tradicional, ¿no? Y si está integrado en el control de la versión que le interesa, ¿por qué no utilizar una de las herramientas de repositorio de documentos de código abierto ? Hay mucho de donde escoger.

Bueno, si decides ir a Git backend de todos modos, básicamente funcionaría para tus requerimientos si lo implementaras como se describe. Pero:

1) Usted mencionó "un grupo de servidores que se empujan / jalan entre sí" - He pensado en ello por un tiempo y todavía no estoy seguro. No puede empujar / jalar varios repos como una operación atómica. Me pregunto si podría haber una posibilidad de confusión durante el trabajo simultáneo.

2) Tal vez no lo necesite, pero una funcionalidad obvia de un repositorio de documentos que no ha enumerado es el control de acceso. Posiblemente podría restringir el acceso a algunas rutas (= categorías) a través de submódulos, pero probablemente no podrá otorgar acceso fácilmente a nivel de documento.


mi valor de 2 peniques. Un poco de anhelo, pero ... Tenía un requisito similar en uno de mis proyectos de incubación. Al igual que el tuyo, mis requisitos clave son una base de datos de documentos (xml en mi caso), con versiones de documentos. Fue para un sistema multiusuario con muchos casos de uso de colaboración. Mi preferencia era usar soluciones opensource disponibles que admitieran la mayoría de los requisitos clave.

Para ir al grano, no pude encontrar ningún producto que proporcionara ambos, de una manera que fuera lo suficientemente escalable (cantidad de usuarios, volúmenes de uso, almacenamiento y recursos de cómputo). Estaba predispuesto hacia git por toda la capacidad prometedora, y (probables) soluciones que uno podría elaborar. Mientras jugué más con la opción de git, pasar de una perspectiva de usuario único a una perspectiva de usuario múltiple (milli) se convirtió en un desafío obvio. Lamentablemente, no pude hacer un análisis de rendimiento sustancial como lo hizo. (... perezoso / renuncia temprano ... para la versión 2, mantra) ¡Poder para ti !. De todos modos, mi idea sesgada se ha transformado en la siguiente alternativa (aún parcial): una malla de herramientas que son las mejores en sus esferas separadas, bases de datos y control de versiones.

Mientras todavía trabajo en progreso (... y ligeramente descuidado) la versión modificada es simplemente esto.

  • en la interfaz: (userfacing) utiliza una base de datos para el almacenamiento de primer nivel (interfaz con aplicaciones de usuario)
  • en el backend, use un sistema de control de versiones (VCS) (como git) para realizar versiones de los objetos de datos en la base de datos

Básicamente equivaldría a agregar un complemento de control de versiones a la base de datos, con un poco de pegamento de integración, que tal vez tenga que desarrollar, pero puede ser mucho más fácil.

Cómo se podría (supuestamente) funcionar es que los intercambios primarios de datos de la interfaz multiusuario se realizan a través de la base de datos. El DBMS manejará todos los problemas divertidos y complejos como multiusuario, concurrencia e, operaciones atómicas, etc. En el back-end, el VCS realizará el control de la versión en un solo conjunto de objetos de datos (sin concurrencia, o problemas de multiusuario). Para cada transacción efectiva en la base de datos, el control de versiones solo se realiza en los registros de datos que habrían cambiado efectivamente.

En cuanto al pegamento de interfaz, tendrá la forma de una función de interfuncionamiento simple entre la base de datos y el VCS. En términos de diseño, un enfoque simple sería una interfaz impulsada por eventos, con actualizaciones de datos de la base de datos activando los procedimientos de control de versiones (pista: asumiendo Mysql, uso de triggers y sys_exec () blah blah ...). En términos de implementación complejidad, que va desde lo simple y eficaz (por ejemplo, secuencias de comandos) a lo complejo y maravilloso (alguna interfaz de conector programado). Todo depende de cuán loco quieras ir con él, y cuánto capital sudoroso estás dispuesto a gastar. Creo que las secuencias de comandos simples deberían hacer la magia. Y para acceder al resultado final, las diversas versiones de datos, una alternativa simple es poblar un clon de la base de datos (más un clon de la estructura de la base de datos) con los datos referenciados por la etiqueta de la versión / id / hash en el VCS. de nuevo, este bit será un trabajo simple de consulta / traducción / mapa de una interfaz.

Todavía hay algunos desafíos e incógnitas que deben abordarse, pero supongo que el impacto y la relevancia de la mayoría de ellos dependerán en gran medida de los requisitos de la aplicación y los casos de uso. Algunos pueden terminar siendo no problemas. Algunos de los problemas incluyen la coincidencia de rendimiento entre los 2 módulos clave, la base de datos y el VCS, para una aplicación con actividad de actualización de datos de alta frecuencia, escalado de recursos (almacenamiento y potencia de procesamiento) en el tiempo en el lado git como datos y usuarios crecer: constante, exponencial o eventualmente meseta

Del cóctel de arriba, esto es lo que estoy elaborando actualmente

  • usando Git para el VCS (inicialmente considerado como un buen CVS para el debido al uso de solo conjuntos de cambios o deltas entre 2 versiones)
  • usando mysql (debido a la naturaleza altamente estructurada de mis datos, xml con esquemas xml estrictos)
  • jugando con MongoDB (para probar una base de datos NoSQl, que coincide con la estructura de base de datos nativa utilizada en git)

Algunos datos divertidos: en realidad, GIT aclara las cosas para optimizar el almacenamiento, como la compresión y el almacenamiento de solo deltas entre revisiones de objetos. SÍ, git almacena solo conjuntos de cambios o deltas entre revisiones de objetos de datos, dónde es aplicable (sabe cuando y cómo) . Referencia: packfiles, profundo en las entrañas de los internos de Git - Revisión del almacenamiento de objetos del git (sistema de archivos direccionable por contenido), muestra similitudes de stricking (desde la perspectiva del concepto) sin bases de datos SQL tales como mongoDB. De nuevo, a expensas de capital sudoroso, puede proporcionar posibilidades más interesantes para integrar el 2 y ajustar el rendimiento

Si llegó hasta aquí, permítame si lo anterior puede ser aplicable a su caso, y suponiendo que así sea, cómo se cuadraría con algunos de los aspectos en su último análisis de desempeño integral.