Pour un projet perso top secret, je me suis retrouvé dans une situation où je souhaitais être capable de :
- stocker un grand nombre de petits dépôts Git
- les avoir en haute-disponibilité (99.9% d’uptime)
- encaisser une forte charge en lecture sur ces dépôts
- être capable de monter en charge verticalement mais aussi horizontalement rapidement si besoin est
- toujours avoir un contenu apparent cohérent : Quand on fait un push sur une des machines du cluster et qu’on enchaîne sur un pull depuis une autre, il faut que le contenu du push soit systématiquement apparent
- accéder et manipuler ces dépôts depuis un programme en Python 3.
Autrement dit, il me fallait essentiellement un moyen de répliquer des dépôts Git sur plusieurs machines efficacement.
Je n’ai rien trouvé qui semble répondre à ce cahier des charges. Donc j’ai regardé si je ne pouvais pas assembler des briques logiciels déjà existantes pour obtenir ce résultat. Les briques suivantes ont attirées mon attention:
- MariaDB + Galera : En gros, Galera permet de répliquer automagiquement une base de données MariaDB sur plusieurs machines, de façon synchrone
- Libgit2 + Pygit2 : Libgit2 a la capacité d’utiliser d’autres backends que le FS. C’est juste que il n’y a que le backend FS qui existe actuellement. Et Pygit2, c’est le Bien(tm), parce-que Python 3.
- Libgit2-backends : Exemples d’implémentation de backends divers pour Libgit2
- Des instructions claires sur ce qu’il faut implémenter pour avoir un backend Libgit2 complet
À partir de là, après presque 1 mois et demi de travail, ça a donné ça:
- Libgit2 avec support de Mysql/MariaDB comme backend ODB, RefDB et comme transport (pour les push/pull/clone/etc)
- Pygit2 avec support pour le backend Mysq/MariaDB + un script supplémentaire pour manipuler ces dépôts + tests fonctionnels
Et ça fonctionne ! :D
Malheureusement je l’ai fait un peu vite, et du coup ce n’est pas intégrable upstream en l’état :
- L’historique Git est dégueu (je pensais d’abord tout faire dans Pygit2)
- J’ai fait des modifications dans Libgit2 pour réutiliser une grosse partie du code de src/transport/local.c. Pour l’instant, ça ne peut pas être fait en dehors de la librairie Libgit2.
- Les dev Libgit2 préfèrent que le code pour d’autres backend que le FS reste en dehors de la libgit2 officielle.
Concernant les performances, sans surprise, je constate une dégradation assez importante : À la louche, c’est généralement 2 à 3 fois plus lent que le FS en brut pour des dépôts de petite taille. C’est surtout beaucoup plus lent pour les gros dépôts Git. Je suis entrain de tester avec le dépôt de Linux. Je pense qu’un peu de tuning MariaDB et de mes changements permettront d’améliorer (.. sans faire de miracle non plus). Il est aussi intéressant de noter que, avec mon implémentation actuelle, les dépôts Git prennent beaucoup plus d’espace disque dans MariaDB que dans un dépôt Git classique. La raison est simple : Git “pack” les commits ensemble en utilisant un algorithme de compression delta. Sans surprise, cet algorithme est très efficace avec les fichiers textes qui changent progressivement. Mais il n’est pas supporté par mon implémentation (ça compliquerait … tout). Pour le dépôt Git du noyau Linux, on passe ainsi d’un dépôt de 1.1Go à .. >= 30Go (sans parler du temps d’insertion dans la BDD qui est en conséquence).
Plus qu’à faire quelques statistiques pour estimer les performances de cette solution.