Alors que mes deux penchants connus -- conspuer les SGBDR et dire du bien de JetBrains -- on longtemps fait l'objet de billets séparés, j'ai aujourd'hui l'occasion de mélanger les plaisirs après la découverte fortuite de [Xodus]
, la base de données embarquable non-relationnelle développée par JetBrains pour ses propres besoins, et dont les sources sont ouvertes depuis un certain temps.
Je n'ai pas eu l'occasion d'utiliser Xodus, mais la lecture de sa documentation allume plein de lumières vertes dans ma tête.
Xodus motorise YouTrack, le gestionnaire d'évolutions ("issue tracker") de JetBrains, vendu comme service en ligne ou logiciel installable sur une infrastructure maison. La base YouTrack utilisée en interne occupe plus de `100 Go`.
Xodus est une bibliothèque Java, codée en Java et Kotlin. Donc l'aspect embarquable c'est réglé.
Xodus supporte des enregistrements binaires de grande taille (BLOB). Y'a qu'à voir la gueule des pièces jointes dans YouTrack.
Xodus est transactionnel. Xodus écrit séquentiellement les modifications dans un journal. Cette séquentialité fournit de fortes garanties d'intégrité. Un ramasse-miette supprime les entrées obsolètes.
Xodus est multi-paradigme. On peut l'utiliser pour faire du clé-valeur, du hiérarchique (genre système de fichiers), du relationnel mou, du sourçage d'événements.
Le relationnel mou (l'appellation vient de moi) est une forme de projection objet-relationnel par-dessus le clé-valeur, avec des attributs et des références à d'autres entités, mais sans schéma. Le relationnel mou supporte des requêtes ensemblistes.
Xodus facilite le sourçage d'événements, du fait d'une option d'écriture ou de lecture séquentielle.
Une base Xodus est partitionnée en magasins ("Stores"). On peut ouvrir les magasins indépendemment. Je présume que pour YouTrack il y a un magasin par projet. Il y a d'autres utilisations : si tu as une application avec des journées d'activité tu crées un magasin par journée, ça fait une base historique qui n'a pas besoin d'être gérée à part, et dont le volume n'influe pas sur les accès à la base en cours de modification.
Il y a plein d'autres petites fonctionnalités vraiment chouettes.
Xodus fournit une fonctionnalité "backup" qui exporte toute la base dans un zip. Ça n'a pas du être compliqué à coder : un mutex pour ne pas se marcher sur les pieds avec le ramasse-miettes et on prend tout depuis la dernière transaction valide. C'est le bénéfice d'une architecture bien conçue.
On a aussi du chiffrement et l'association d'une transaction à un fil d'exécution.
Xodus ne fournit aucune fonctionnalité de réplication ou de distribution sur plusieurs machines. Ça réduit ses capacités à celles du système de fichiers. Le plus gros serveur "Storage" de chez OVH est un hexacœur avec `64 Go` de RAM et `12 «times» 6 To` de disque donc `36 To` si on fait du RAID. Ext4 supporte des volumes d'`1 EB` (exaoctet). Même si ça ne grimpe pas jusqu'au ciel ça laisse de quoi s'amuser un bout de temps.
== Réplication
Ben ouais là si tu veux de la réplication tu répliques le volume en mode bloc, avec par exemple DRBD. Dans ce cas, contrairement aux réplications de type actif-passif (y compris Raft) on n'a pas de vraie reprise à chaud.
Imaginons une application mono-instance embarquant Xodus, avec réplication DRBD dans une grappe Kubernetes. L'application plante, Kubernetes démarre une autre instance qui retrouve les données en bon état (moins les transactions non-achevées bien sûr). On se prend le coût du redémarrage, et l'application cliente doit traiter correctement l'erreur (par exemple en réessayant). Il y a des cas où c'est acceptable. J'ignore complètement si la façon qu'a Xodus d'écrire les fichiers se prête bien à la réplication par DRBD ; il y a semble-t-il des SGBDR qui n'aiment pas ça (peut-être est-ce la motivation de LINBIT pour supporter spécifiquement MariaDB).
Évidemment la solution parfaite c'est une réplication de type Raft avec des serveurs secondaires qui répliquent toujours la dernière version, et prêts à être promus dès que le primaire a l'air malade. Raft est mieux que le primaire-secondaire classique parce qu'il supporte une partition réseau. (DRBD sait juste détecter une partition réseau pour éviter la corruption de données. Pour qu'il résiste à une partition il faut le piloter avec Pacemaker ou Kubernetes.)
Oui je vois arriver l'argument "Mais mon SGBDxxx magique fait déjà de la réplication !" Non non je parle d'un serveur applicatif qui conserve plein de trucs en mémoire vive, notamment les sessions. Si la réplication par DRBD ne broute rien, tu redémarres le SGBDR ailleurs et c'est kif-kif à part le temps de démarrage. Mais pour en arriver là il faut que le serveur applicatif externalise tout son état vers le SGBDxxx (y compris et notamment les sessions). Donc il passera son temps à faire des entrées-sorties et sera très casse-pieds à coder, l'approche relationnelle étant juste un facteur aggravant.
Avec une application basée sur le sourçage d'événements, la réplication est plus simple puisque ce sont juste les messages affectant l'état de l'application qui sont répliqués du primaire vers le secondaire. On n'a pas besoin de faire voyager la description des changements de l'état interne.
Il y a déjà des bibliothèques Java sympathiques pour faire du Raft, mais elles ne donnent pas accès au journal, c'est juste un magasin clé-valeur (des fois [un peu plus]
). L'avantage de connaître l'état Raft c'est qu'une instance passive de l'application peut dire au client "Non l'instance active c'est pas moi c'est machin." Ou décider du bon moment pour la sauvegarde sur `Amazon S3`.
Après il y a plein de gens qui répliquent sans se soucier de la partition réseau. Ou qui abordent le problème autrement. J'ai lu quelque part que chez Facebook, pour une partie de leur infrastructure, ils ont mis du matos qui rend la partition suffisamment improbable. Ça doit permettre des raccourcis au niveau matériel comme répliquer une zone mémoire d'une machine à l'autre, au lieu de faire tous les trucs compliqués de Raft (ou d'un protocole équivalent).
En l'absence de solution miraculeuse, Xodus et DRBD pilotés par Kubernetes, c'est pas dégueu pour le prix.
== Performances
JetBrains publie un "banc d'essai"
. Parmi les solutions pur Java il n'y a que Chronicle qui soit nettement plus rapide dans les scénarios testés. Chronicle va très vite parce qu'il écrit dans des fichiers projetés en mémoire ("memory-mapped files"). Pour des écritures séquentielles, Xodus est même plus rapide que Chronicle.
Après si on veut faire de la réplication DRBD ça ne fonctionne que si les écritures sont balancées ("flushed") sur le disque. Or quand on écrit dans des fichiers projetés en mémoire, c'est le système d'exploitation qui décide de quand a lieu l'accès physique. Donc il faudrait refaire les mesures avec chaque écriture dans une transaction.
== Conclusion
Xodus ne résoud pas toute la misère du monde mais vu les caractéristiques du produit, c'est une option à considérer sérieusement pour une application mono-instance dont les données peuvent tenir sur une seule machine physique. En revanche, si on veut de la réplication en temps réel, ça veut dire DRBD, plus le délai d'un redémarrage à froid en cas de reprise sur panne.
Les autres caractéristiques de Xodus (magasins indépendants, transactionnalité, multi-paradigme, compatibilité avec le sourçage d'événements, performances, utilisation éprouvée, sources ouvertes) en font la base de données embarquable en Java la plus attrayante à ce jour.