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 tiene varios kilobytes de datos en alguna forma estructurada (prefiero YAML, pero también puede ser JSON o XML).
Los usuarios de estos sistemas realizan varios tipos de operaciones:
- recuperación de estos documentos por identificación
- buscar 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 (que incluye ver quién, cuándo y por qué cambió el documento, obtener una versión anterior y probablemente volver a esta si se solicita)
Por supuesto, la solución tradicional sería usar algún tipo de base de datos de documentos (como CouchDB o Mongo) para este problema; sin embargo, este control de versiones (historial) me tentó a una idea loca: ¿por qué no debería usar el git
repositorio como un backend de base de datos para esta aplicación?
A primera vista, podría resolverse así:
- categoría = directorio, documento = archivo
- obtener documento por ID => cambiar directorios + leer un archivo en una copia de trabajo
- editar documentos con comentarios de edición => realizar confirmaciones de varios usuarios + almacenar mensajes de confirmación
- history => registro de git normal y recuperación de transacciones más antiguas
- búsqueda => esa es una parte un poco más complicada, supongo que requeriría la exportación periódica de una categoría a una base de datos relacional con indexación de columnas que permitiremos buscar
¿Existen otros errores comunes en esta solución? ¿Alguien ha intentado implementar dicho backend ya (es decir, para marcos populares: RoR, node.js, Django, CakePHP)? ¿Tiene esta solución alguna posible implicación en el rendimiento o la confiabilidad, es decir, se ha comprobado que git sería mucho más lento que las soluciones de bases de datos tradicionales o habría problemas de escalabilidad / confiabilidad? Supongo que un grupo de servidores de este tipo que empujan / tiran del repositorio de los demás debería ser bastante robusto y confiable.
Básicamente, dime si esta solución funcionará y por qué funcionará o no.
Respuestas:
Responder mi propia pregunta no es lo mejor que se puede hacer, pero, dado que finalmente abandoné la idea, me gustaría compartir el fundamento que funcionó en mi caso. Me gustaría enfatizar que esta justificación podría no aplicarse a todos los casos, por lo que depende del arquitecto decidir.
Generalmente, el primer punto principal que mi pregunta pasa por alto es que estoy tratando con un sistema multiusuario que funciona en paralelo, simultáneamente, usando mi servidor con un cliente ligero (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 de implementar (y, por lo tanto, eliminan el propósito original de descargar todas las cosas de implementación difíciles para git en primer lugar):
Enfoque "contundente": 1 usuario = 1 estado = 1 copia de trabajo completa de un repositorio que el servidor mantiene para el usuario. Incluso si estamos hablando de una base de datos de documentos bastante pequeña (por ejemplo, cientos de MiB) con ~ 100K de usuarios, mantener un clon de repositorio completo para todos ellos hace que el uso del disco se corra por las nubes (es decir, 100K de usuarios por 100MiB ~ 10 TiB) . Lo que es aún peor, la clonación del repositorio de 100 MiB cada vez lleva varios segundos de tiempo, incluso si se hace de manera bastante efectiva (es decir, sin usar git y desempaquetar y reempacar), lo cual no es aceptable, en mi opinión. Y lo que es aún peor: cada edición que aplicamos a un árbol principal debe llevarse al repositorio de cada usuario, que es (1) acaparamiento de recursos, (2) podría conducir 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 dicho uso del disco automáticamente significa un uso bastante alto de la CPU.
Enfoque "Solo usuarios activos": mantenga la copia de trabajo solo para los usuarios activos. De esta manera, generalmente no almacena un clon de repositorio completo por usuario, sino:
Por lo tanto, el uso del disco en este caso alcanza su punto máximo en O (número de ediciones × datos × número de usuarios activos), que suele ser ~ 100 ... 1000 veces menor que el número total de usuarios, pero hace que iniciar y cerrar sesión sea más complicado y lento. , ya que implica la clonación de una rama por usuario en cada inicio de sesión y retirar estos cambios al cerrar la sesión o al vencimiento de la sesión (lo que debe hacerse transaccionalmente => agrega otra capa de complejidad). En números absolutos, reduce el uso de disco de 10 TiB a 10..100 GiB 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 "pago disperso": hacer un "pago disperso" en lugar de un clon de repositorio completo por usuario activo no ayuda mucho. Puede ahorrar ~ 10 veces el uso de espacio en disco, pero a expensas de una carga de CPU / disco mucho mayor en las operaciones que involucran el historial, lo que acaba con el propósito.
Enfoque de "grupo de trabajadores": en lugar de hacer clones completos cada vez para una persona activa, podríamos mantener un grupo de clones de "trabajadores", listos para ser utilizados. De esta manera, cada vez que un usuario inicia sesión, ocupa un "trabajador", tirando allí su rama desde el repositorio principal y, cuando cierra la sesión, libera al "trabajador", que hace un reinicio completo inteligente de git para volver a ser solo un clon de repositorio principal, listo para ser utilizado por otro usuario que inicie sesión. No ayuda mucho con el uso del disco (todavía es bastante alto, solo clon completo 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 calculé intencionalmente números de base de datos y base de usuarios bastante pequeños: 100K usuarios, 1K usuarios activos, base de datos total de 100 MiB + historial de ediciones, 10 MiB de copia de trabajo. Si observa proyectos de crowdsourcing más destacados, hay números mucho más altos allí:
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, emitiendo operaciones git y almacenando prácticamente el pago completo en el lado del cliente, no en el lado del servidor.
También hay otros puntos que me he perdido, pero no son tan malos en comparación con el primero:
Así, línea de fondo : que es posible, pero para la mayoría de casos de uso actuales no será ni de lejos la solución óptima. Probablemente una mejor alternativa sería acumular su propia implementación de historial de edición de documentos en SQL o intentar utilizar cualquier base de datos de documentos existente.
fuente
Ciertamente un enfoque interesante. Yo 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 listo para usar, entonces está bien, pero probablemente necesite construir una capa de repositorio de documentos sobre él. Entonces, también podría construirlo sobre una base de datos tradicional, ¿verdad? Y si lo que le interesa es el control de versiones integrado, ¿por qué no utilizar una de las herramientas de repositorio de documentos de código abierto ? Hay mucho de donde escoger.
Bueno, si decide optar por el backend de Git de todos modos, básicamente funcionaría para sus requisitos si lo implementara como se describe. Pero:
1) Mencionaste "grupo de servidores que se empujan / jalan entre sí". Lo he pensado durante un tiempo y todavía no estoy seguro. No puede presionar / extraer varios repositorios como una operación atómica. Me pregunto si podría haber alguna posibilidad de que se produzca un desorden de fusión durante el trabajo simultáneo.
2) Quizás no lo necesite, pero una funcionalidad obvia de un repositorio de documentos que no enumeró 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 a nivel de documento fácilmente.
fuente
mi valor de 2 centavos. Un poco de nostalgia pero ... Tenía un requisito similar en uno de mis proyectos de incubación. Al igual que el suyo, mis requisitos clave eran 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 fue utilizar las soluciones de código abierto disponibles que admitan 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 (número de usuarios, volúmenes de uso, recursos de almacenamiento y computación). Estaba predispuesto a git por toda la capacidad prometedora, y (probables) soluciones que uno podría elaborar a partir de él. A medida que jugaba más con la opción git, pasar de la perspectiva de un solo usuario a una perspectiva de múltiples (mili) usuarios se convirtió en un desafío obvio. Desafortunadamente, no pude hacer un análisis de rendimiento sustancial como lo hizo usted. (.. perezoso / salir temprano .... para la versión 2, mantra) ¡Poder para ti !. De todos modos, mi idea sesgada se ha transformado desde entonces en la siguiente alternativa (aún sesgada): una combinación de herramientas que son las mejores en sus esferas separadas, bases de datos y control de versiones.
Aunque todavía se está trabajando (... y un poco descuidado), la versión transformada es simplemente esto.
En esencia, equivaldría a agregar un complemento de control de versiones a la base de datos, con algo de pegamento de integración, que puede que tenga que desarrollar, pero que puede ser mucho más fácil.
Cómo funcionaría (se supone que funciona) es que los intercambios de datos de la interfaz multiusuario principal se realizan a través de la base de datos. El DBMS manejará todos los temas divertidos y complejos como multiusuario, concurrencia e, operaciones atómicas, etc. En el backend, el VCS realizaría el control de versiones 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 efectivamente habrían cambiado.
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 que activan los procedimientos de control de versiones (pista: asumiendo Mysql, uso de disparadores y sys_exec () bla bla ...) En términos de complejidad de implementación, variará desde lo simple y efectivo (por ejemplo, scripting) hasta lo complejo y maravilloso (alguna interfaz de conector programada). Todo depende de cuán loco quieras ir con él y de cuánto capital de sudor 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 versión / id / hash en el VCS. de nuevo, este bit será un simple trabajo de consulta / traducción / mapa de una interfaz.
Aún quedan algunos desafíos e incógnitas que abordar, pero supongo que el impacto y la relevancia de la mayoría de ellos dependerán en gran medida de los requisitos de su aplicación y los casos de uso. Algunos pueden terminar siendo un problema. 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 (capacidad de almacenamiento y procesamiento) a lo largo del tiempo en el lado de git como los datos y los usuarios crecer: estable, exponencial o eventualmente meseta
Del cóctel de arriba, esto es lo que estoy preparando actualmente.
Algunos datos divertidos: git en realidad hace cosas claras para optimizar el almacenamiento, como la compresión y el almacenamiento de solo deltas entre revisiones de objetos: SÍ, git solo almacena conjuntos de cambios o deltas entre revisiones de objetos de datos, dónde es aplicable (lo sabe cuando y cómo) . Referencia: packfiles, en lo más profundo de las entrañas de Git : revisión del almacenamiento de objetos de git (sistema de archivos direccionable por contenido), muestra similitudes sorprendentes (desde la perspectiva del concepto) con bases de datos noSQL como mongoDB. Nuevamente, a expensas del capital de sudor, 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 lo sea, cómo encajaría con algunos de los aspectos de su último análisis integral de desempeño.
fuente
Implementé una biblioteca Ruby además de la
libgit2
cual 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 rendimiento, compensaciones, etc.
fuente
Como mencionaste, el caso multiusuario es un poco más complicado de manejar. Una posible solución sería utilizar archivos de índice de Git específicos del usuario que resulten en
El truco consiste en combinar la
GIT_INDEX_FILE
variable de entorno de Git con las herramientas para crear confirmaciones de Git manualmente:A continuación se muestra un esquema de la solución (los hash SHA1 reales se omiten en los comandos):
Dependiendo de sus datos, podría usar un trabajo cron para fusionar las nuevas referencias,
master
pero la resolución del conflicto es posiblemente la parte más difícil aquí.Las ideas para hacerlo más fácil son bienvenidas.
fuente