Cómo GitLab redujo el tiempo de respaldo de repositorios de 48 horas a 41 minutos
(about.gitlab.com)- GitLab detectó un problema de lentitud al respaldar repositorios de gran tamaño y realizó mejoras para resolverlo
- La causa principal era la complejidad O(N²) de una función de Git con 15 años de antigüedad, y mediante una optimización del algoritmo logró mejorar drásticamente el rendimiento
- Como resultado de la optimización, el tiempo de respaldo del repositorio más grande se redujo de 48 horas a 41 minutos
- El método mejorado ofrece eficiencia de recursos y confiabilidad, y al mismo tiempo tiene un impacto positivo en otras herramientas basadas en Git y en la comunidad
- A partir de GitLab 18.0, todos los usuarios podrán obtener estos beneficios sin configuración adicional
La importancia y los desafíos del respaldo de repositorios
- El respaldo de repositorios es un elemento clave de una estrategia de recuperación ante desastres
- A medida que crece el tamaño del repositorio, aumenta la complejidad de contar con un proceso de respaldo confiable
- El propio repositorio de Rails de GitLab tardaba 48 horas en respaldarse, lo que obligaba a elegir entre la frecuencia de los respaldos y el rendimiento del sistema
- En los repositorios a gran escala existen diversos problemas relacionados con tiempo, recursos, riesgo de fallos y condiciones de carrera
- Esto llevaba a estrategias poco consistentes entre organizaciones, como dificultad para programar respaldos, dependencia de herramientas externas o reducción en la cantidad de respaldos
Análisis del cuello de botella de rendimiento e identificación de la causa
- La función de respaldo de GitLab genera instantáneas del repositorio usando el comando
git bundle create - En la implementación de este comando surgía un cuello de botella de rendimiento a medida que aumentaba la cantidad de references (referencias)
- En algunos casos, respaldar repositorios grandes con millones de referencias tomaba más de 48 horas
Análisis de la causa
- Se identificó la zona del cuello de botella mediante análisis con Flame Graph durante la ejecución del comando
- Con 10,000 referencias, alrededor del 80% del tiempo de ejecución se consumía en la función
object_array_remove_duplicates() - Esta función fue introducida en 2009 en este commit para procesar referencias duplicadas
- Evitaba problemas cuando se incluían referencias duplicadas al usar
git bundle create - Sin embargo, la detección de duplicados estaba implementada con bucles
foranidados, lo que generaba una complejidad O(N²) - Cuantas más referencias había, más severo se volvía el cuello de botella
- Evitaba problemas cuando se incluían referencias duplicadas al usar
De O(N²) a un mapeo eficiente
- GitLab contribuyó a Git con un parche que usa una estructura de datos tipo mapa en lugar de bucles anidados
- Cada referencia se agrega al mapa, eliminando duplicados automáticamente y procesándolos de forma eficiente
- Con este cambio, el rendimiento de
git bundle createmejoró de forma drástica y ahora puede escalar incluso en entornos con grandes volúmenes de referencias - Según los benchmarks, con 100,000 referencias se confirmó una mejora de rendimiento de más de 6 veces
Reducción drástica del tiempo de respaldo y sus efectos
- El tiempo máximo de respaldo del repositorio se redujo de 48 horas a 41 minutos (hasta un 1.4% del tiempo original)
- Ahora es posible ofrecer un rendimiento consistente sin importar el tamaño del repositorio
- También se obtuvieron beneficios adicionales, como menor carga del servidor y mejor rendimiento general en comandos de Git relacionados con respaldos
- El parche correspondiente se aplicó en upstream Git, y GitLab lo adoptó de inmediato para que los clientes pudieran experimentar la mejora cuanto antes
Cambios reales para los clientes de GitLab
- Los clientes empresariales ahora pueden ejecutar respaldos nocturnos consecutivos sin conflictos con el flujo de trabajo de desarrollo
- Gracias a la reducción del objetivo de punto de recuperación (RPO), el riesgo de pérdida de datos en situaciones de desastre se redujo de forma importante
- También disminuyeron el consumo de recursos, el tiempo de mantenimiento y los costos operativos, incluidos los costos en la nube
- Incluso si el tamaño del repositorio sigue creciendo, ya no es necesario preocuparse por elegir entre la frecuencia de respaldo y el rendimiento del sistema
- Ahora todos los usuarios de GitLab pueden disfrutar de estos beneficios sin cambios de configuración
Próximos pasos y significado
- Esta mejora forma parte del esfuerzo continuo de GitLab por construir una infraestructura Git empresarial altamente escalable
- Los cambios aportados por GitLab también se reflejaron en el proyecto Git upstream, generando un efecto positivo en toda la industria y la comunidad de código abierto
- Este tipo de mejoras de infraestructura se está convirtiendo en un modelo para otros trabajos clave de optimización de rendimiento
Una historia más profunda sobre el enfoque de rendimiento de GitLab puede verse en el evento de lanzamiento virtual de GitLab 18
1 comentarios
Comentarios en Hacker News
Se comparte la información de que el código de mejora de rendimiento que GitLab aportó a Git está previsto para lanzarse en la v2.50.0 enlace al commit relacionado
A partir de experiencia directa, se enfatiza que eliminar operaciones n^2 en código escrito por uno mismo siempre resultó ser la decisión correcta. También sorprende lo fácil que estos problemas salen a la luz incluso con valores de n muy pequeños, sin necesidad de escribir algoritmos especiales
Se cita la primera ley de la computación de Bruce Dawson: O(n^2) es el punto donde surgen los peores problemas de escalabilidad. En producción parece lo bastante rápido, pero una vez desplegado provoca una degradación de rendimiento letal enlace al texto relacionado
Se comparte la experiencia personal de haber visto muchas veces casos donde la complejidad temporal O(N^2) parecía rápida en pruebas, pero explotaba gravemente en producción
Según su experiencia, en la mayoría de los problemas (80~90%), si hace falta un algoritmo complejo, eso es señal de que el propio modelo de datos no es el correcto. Considera que solo en algunos casos especiales, como compiladores, internals de bases de datos o planificación de rutas, los algoritmos complejos son intrínsecamente necesarios
Se menciona como única excepción los casos limitados a hardware pequeño con n menor que 10 (como interfaces CAN). Si n puede crecer sin cambiar el hardware, hay que evitar sí o sí las operaciones n^2, y se recomienda imponer límites de diseño o detectar el problema de antemano para reconocer la necesidad de rediseñar
Expresa la frustración de estar lidiando con una operación n^3 que ya genera un cuello de botella con apenas 10,000 elementos, sin haber encontrado todavía una solución
Se evalúa como un hallazgo interesante, aunque también se comenta con algo de pesar que el artículo habría comunicado igual de bien si hubiera tenido solo una décima parte de su longitud. Aun así, se menciona como punto positivo que, al no ser contenido en video, fue fácil de hojear
Como tip para quienes aún no leyeron el artículo, se sugiere leer solo hasta después del flame graph y detenerse antes de la mención del backport, ya que esa es la forma más eficiente de captar lo esencial
Se comenta que el estilo completo del artículo se sintió como si lo hubiera generado un LLM (modelo de lenguaje grande), y que eso fue lo más memorable
Se dice que no habría molestado aunque el artículo fuera todavía más largo, y que sigue quedando la duda de por qué el bundle de respaldo se hizo con dos refs
Se percibe como mucho tiempo tanto las 48 horas para comprimir la carpeta de git (varios GB) como incluso los 41 minutos. Se cuestiona si hay alguna razón especial para usar
git bundleen vez de simplemente hacer un snapshot/archivo del repo completo, y qué ventaja tendríagit bundlefrente a respaldos frecuentes con ZFSEn el FAQ oficial de git se advierte que este método es riesgoso porque omite los procedimientos normales de verificación de integridad de Git. En estos casos se recomienda
git fsckpara validar la integridad de la colección. A nivel personal, con Syncthing y snapshots de Btrfs ya suele bastar por velocidad y estabilidad. documentación relacionadaSe menciona que hacer respaldos offsite de snapshots de ZFS hacia destinos no basados en ZFS, como S3, tiene limitaciones. Como función menos conocida de git bundle, se puede especificar una ubicación en
git clone --bundle-uri, y si el servidor informa al cliente dónde está el bundle, el cliente puede descargarlo, descomprimirlo rápidamente y luego recibir del servidor solo las actualizaciones delta, reduciendo mucho la carga al clonar repos grandesEn resumen, parece que simplemente se añadió caché donde hacía falta. Dado que los repos de git son sistemas distribuidos, surge la duda de si no bastaría con espejarlos en otro repositorio y administrarlos con herramientas de snapshot/respaldo del sistema de archivos. Se enfatiza que la información crítica de control de versiones debe estar distribuida
No tiene mucha experiencia haciendo respaldos de git, pero le resulta curioso por qué surgiría una race condition al crear el respaldo directamente desde un repo local
Si esto se vuelve tan complicado por culpa del protocolo de datos de capas superiores, se cuestiona por qué no usar snapshots a nivel de bloque. El hecho de que Git no tenga algo como WAL (Write Ahead Logging) es un obstáculo, pero con SQLite se aprovecha en entornos reales una estrategia de snapshots de bloque de forma bastante simple solo agregando modo WAL. Se piensa que, si Git adoptara una arquitectura así, sería posible una estrategia de respaldo mucho más confiable
Se coincide en que la falta de un mecanismo similar a WAL en Git causa este tipo de problemas, y se comparte una experiencia crítica donde confiar solo en snapshots llevó a que el repositorio quedara roto al restaurarlo. Se añade que es recuperable, pero extremadamente engorroso
Se comparte como dato que recientemente apareció una mejor solución para SQLite: sqlite3_rsync
Se explica la perspectiva de GitLab: no es solo un servicio administrado simple, sino un producto que puede instalarse por cuenta propia y usarse en entornos muy diversos, por lo que cada usuario tiene condiciones distintas de sistema de archivos, soporte de snapshots y sistema operativo. Es decir, GitLab quiere un sistema de respaldo independiente que funcione de forma general en todos esos entornos
Al ver la expresión "redujo exponencialmente el tiempo de respaldo con un cambio de algoritmo", surge la duda de si eso significaría haber pasado de O(n^2) a O(n^2/2^n). Se sospecha que en realidad no puede ser eso
Se explica que la complejidad algorítmica de la función modificada en realidad se volvió 6 veces más rápida, y que en su contexto de uso el tiempo operativo total cayó al 1%, así que en este caso "mejora exponencial" podría interpretarse como una forma de marketing razonable. La clasificación exacta de complejidad no tendría tanta importancia aquí
Se aclara que en la conversación cotidiana "exponential" no se usa con un sentido matemático estricto, sino más bien como una forma figurada de decir "mejoró muchísimo"
Según su propia interpretación, podría ser que n se redujera hasta log(n). Se menciona una situación parecida a cuando suele decirse que la transformada cuántica de Fourier es exponencialmente más rápida que la DFT tradicional, como analogía para un cambio de complejidad de n^2 a nlogn
Si se reemplaza un algoritmo n^2 por uno con búsquedas log(n), sí podría decirse que la mejora es exponencial, pero en la práctica muchas veces se llega incluso a O(1), como con búsquedas en hashmap, así que sería aún más rápido
Se opina que toda esta discusión es una objeción improductiva
Se comenta que este es un buen ejemplo de que escribir en C no garantiza un gran rendimiento por sí solo, y que las estructuras de datos y los algoritmos importan más
for. En este caso también hay un string set en Git, pero como no es algo estándar, se analiza que el autor original quizá ni supiera que existíaSe menciona como lección la necesidad de encontrar un equilibrio entre la optimización prematura y la optimización anticipatoria. En general se advierte contra optimizar demasiado pronto, pero se propone como regla aplicar por adelantado optimizaciones obvias y fáciles en funciones que se llaman con muchísima frecuencia
Se comparte directamente el enlace al commit relacionado (mejora del algoritmo) enlace al commit relacionado