Date de la publication : 5 juin 2025
Lecture : 9 min
L'optimisation d'une fonction Git vieille de 15 ans a permis d'augmenter la productivité, de renforcer les stratégies de sauvegarde et de réduire les risques.

Les sauvegardes de dépôt sont un élément essentiel de toute stratégie de reprise après sinistre robuste. Cependant, à mesure que les dépôts grossissent, garantir des sauvegardes fiables devient de plus en plus difficile. Notre propre dépôt Rails mettait 48 heures à être sauvegardé, nous obligeant à faire des choix impossibles entre la fréquence des sauvegardes et les performances du système. Nous voulions résoudre ce problème pour nos clients ainsi que pour nos propres équipes internes.
Après investigation, la cause remontait à une fonction Git vieille de 15 ans, dont la complexité algorithmique O(N²) freinait lourdement les opérations. Nous l'avons corrigée en repensant l'algorithme, réduisant ainsi les temps de sauvegarde de manière exponentielle. Résultat : des coûts réduits, un risque diminué, et surtout, des stratégies de sauvegarde qui s'adaptent à la croissance de votre code source.
Il s'est avéré qu'il s'agissait d'un problème d'évolutivité de Git qui affecte tout utilisateur gérant de grands dépôts. Découvrez dans cet article comment nous l'avons identifié et résolu.
À mesure que les entreprises développent leurs dépôts et que les sauvegardes se complexifient, elles sont confrontées aux défis suivants :
Ces défis peuvent conduire à faire des compromis sur la fréquence ou l'exhaustivité des sauvegardes, ce qui est inacceptable en matière de protection des données. L'allongement des fenêtres de sauvegarde peut contraindre certains clients à adopter des solutions de contournement, comme adopter des outils externes ou réduire la fréquence des sauvegardes, ce qui fragilise les stratégies de protection des données au sein des entreprises en les rendant incohérentes.
Nous allons maintenant vous expliquer comment nous avons identifié ce goulot d'étranglement de performance, trouvé une solution et déployé une mesure corrective capable de réduire drastiquement les temps de sauvegarde.
La fonctionnalité de sauvegarde des dépôts de GitLab repose sur la commande git bundle create, qui génère un instantané complet du dépôt, incluant tous les objets et références comme les branches et les tags. Ce paquet sert de point de restauration permettant de recréer le dépôt dans son état exact.
Cependant, l'implémentation de cette commande souffrait d'un problème d'évolutivité lié au nombre de références, créant un véritable goulot d'étranglement en termes de performance. À mesure que les dépôts accumulaient un nombre croissant de références, le temps de traitement des données augmentait de façon exponentielle. Dans nos plus grands dépôts, contenant des millions de références, les opérations de sauvegarde pouvaient dépasser les 48 heures.
Pour identifier la cause profonde de ce ralentissement, nous avons analysé un flame graph de la commande pendant son exécution.

Ce graphique illustre le parcours d'exécution d'une commande à travers sa trace de piles d'appels, où chaque barre correspond à une fonction dans le code, et sa largeur indique le temps que la commande a passé à s'exécuter dans cette fonction spécifique.
Le flame graph de git bundle create exécuté sur un dépôt contenant 10 000 références révèle qu'environ 80 % du temps d'exécution est consommé par la fonction object_array_remove_duplicates(), introduite dans Git par le biais de la validation b2a6d1c686 (paquet : permettre à la même référence d'être spécifiée plusieurs fois, 17/01/2009).
Pour comprendre ce changement, il est important de savoir que git bundle create permet de spécifier les références à inclure dans le paquet et que, pour les paquets de dépôt complets, le flag --all compacte toutes les références.
Cette validation corrigeait un problème lié aux références dupliquées fournies via la ligne de commande, telles que git bundle create main.bundle main main, ce qui créait un paquet sans gérer correctement la duplication de la référence « main ». Lors de la décompression, Git tentait d'écrire la même référence deux fois provoquant une erreur. Le code ajouté pour éviter ces duplications utilise des boucles for imbriquées qui parcourent toutes les références afin de détecter les doublons. Cet algorithme de complexité O(N²) devient un goulot d'étranglement majeur en termes de performance dans les dépôts contenant un grand nombre de références, engendrant un temps de traitement des données très important.
Pour résoudre ce problème, nous avons proposé une correction en amont dans Git, remplaçant les boucles imbriquées par une structure de type map. Chaque référence y est ajoutée une seule fois, ce qui élimine automatiquement les doublons et optimise le traitement.
Ce changement améliore considérablement les performances de git bundle create et permet une bien meilleure évolutivité dans les dépôts contenant un grand nombre de références. Des tests de benchmark effectués sur un dépôt contenant 10 000 références montrent une amélioration des performances par un facteur de 6.
Benchmark 1: bundle (refcount = 100000, revision = master)
Time (mean ± σ): 14.653 s ± 0.203 s [User: 13.940 s, System: 0.762 s]
Range (min … max): 14.237 s … 14.920 s 10 runs
Benchmark 2: bundle (refcount = 100000, revision = HEAD)
Time (mean ± σ): 2.394 s ± 0.023 s [User: 1.684 s, System: 0.798 s]
Range (min … max): 2.364 s … 2.425 s 10 runs
Summary
bundle (refcount = 100000, revision = HEAD) ran
6.12 ± 0.10 times faster than bundle (refcount = 100000, revision = master)
Le correctif a été accepté et fusionné dans Git en amont. Chez GitLab, nous l'avons rétroporté afin que nos clients puissent en bénéficier immédiatement, sans attendre la prochaine version officielle de Git.
Les gains de performance découlant de cette amélioration ont été tout simplement transformateurs :
Pour les clients GitLab, cette amélioration apporte des bénéfices immédiats et concrets en matière de sauvegarde de leurs dépôts et de leur planification de reprise après sinistre :
Les entreprises peuvent à présent mettre en œuvre des stratégies de sauvegarde plus robustes sans compromettre les performances ou l'exhaustivité. Ce qui relevait autrefois d'un arbitrage difficile est devenu une pratique opérationnelle simple.
À partir de la version GitLab 18.0, tous les clients GitLab, quelle que soit leur version de licence, peuvent désormais profiter pleinement de ces améliorations pour leur stratégie de sauvegarde et l'exécution de leurs sauvegardes, sans aucune autre modification de la configuration.
Cette avancée s'inscrit dans notre engagement continu à proposer une infrastructure Git évolutive, adaptée aux exigences des entreprises. Bien que réduire le temps de sauvegarde de 48 heures à 41 minutes représente un jalon majeur, nous poursuivons nos efforts pour identifier et éliminer d'autres goulots d'étranglement dans l'ensemble de notre pile.
Nous sommes particulièrement fiers que cette amélioration ait été intégrée en amont dans le projet Git, bénéficiant ainsi non seulement aux utilisateurs de GitLab, mais aussi à l'ensemble de la communauté Git. Cette approche collaborative du développement garantit que les améliorations sont rigoureusement revues, largement testées et accessibles à tous.
Des travaux d'infrastructure en profondeur comme celui-ci illustrent notre approche de la performance chez GitLab. Participez à l'événement virtuel de lancement de GitLab 18 pour découvrir les autres améliorations fondamentales que nous proposons. Inscrivez-vous dès aujourd'hui !