Pourquoi avons-nous migré ?
Début avec MongoDB
Lorsque nous avons initialement construit Infisical, nous avons choisi MongoDB + Mongoose ORM, car cette combinaison présentait le moins de surcharge et nous permettait de livrer rapidement des fonctionnalités de qualité. À l’époque, nous étions davantage concentrés sur Infisical Cloud, notre offre SaaS gérée. Nous n’avions pas anticipé autant d’utilisateurs auto-hébergés du produit, et il n’avait donc pas été conçu dans cette optique.
Limitations de MongoDB
Bien que MongoDB ait bien servi Infisical au début, il a montré ses limites lorsque notre produit a évolué au-delà du service géré. De plus en plus d’organisations, notamment celles opérant à l’intersection de la conformité et de la sécurité, ont préféré l’auto-hébergement d’Infisical plutôt que d’utiliser Infisical Cloud. D’autres avaient des exigences sur site à respecter. La demande croissante pour l’auto-hébergement nous a amenés à quitter MongoDB au profit de PostgreSQL.
Problèmes avec MongoDB
Voici quelques-uns des problèmes que nous avons rencontrés avec MongoDB :
- Transactions difficiles à configurer : la mise en place de transactions avec MongoDB n’était pas triviale, car elle nécessitait l’exécution de MongoDB en mode cluster avec diverses configurations. Cela rendait difficile la réalisation d’un simple POC d’Infisical, car cela exigeait une configuration de production de MongoDB ;
- Manque de support pour les transactions : MongoDB ne prenait pas en charge les transactions de manière native, ce qui posait des problèmes pour les opérations critiques ;
- Structure de base de données sans schéma : bien que la flexibilité de MongoDB soit un avantage, elle a également entraîné des problèmes de conception de schéma. La conception sans schéma a rendu difficile la gestion des versions et la maintenance.
Pourquoi PostgreSQL ?
Lors de notre recherche d'une nouvelle base de données, nous avons commencé par dresser la liste des aspects qui nous importaient le plus : facilité de gestion (c'est-à-dire configuration, déploiement et mise à l'échelle inclus), support intégré des transactions et capacités relationnelles. Dans le cadre de nos délibérations, nous nous sommes également demandé si nous devions ou non créer notre propre système de stockage intégré ou opter pour une solution de stockage externe.
Voici ce que cela signifie pour chaque option :
- stockage intégré : Nous pourrions intégrer un système de base de données comme SQLite directement dans Infisical et poursuivre une stratégie de réplication horizontale pour réduire la latence en évitant les sauts de réseau supplémentaires. Dans ce modèle, la mise à l'échelle du système impliquerait de déployer plusieurs instances d'Infisical et de les faire communiquer entre elles par le biais d'un algorithme de consensus comme Raft. Bien que cela semble être une excellente solution puisque les clients n'auraient pas besoin de connecter des dépendances pour faire fonctionner Infisical, l'écosystème d'outils pour exécuter cette vision semblait immature et l'effort d'ingénierie requis pour cela semblait rien de moins que démesuré ;
- stockage externe : Nous pouvions simplement remplacer MongoDB par une ou plusieurs autres bases de données telles que PostgreSQL ou MySQL et utiliser leurs capacités d'extension intégrées. Bien que cette solution n'ait pas totalement éliminé les frictions associées au besoin de dépendances externes pour utiliser Infisical, nous avons estimé qu'elle offrait déjà des avantages significatifs du fait qu'il ne s'agissait pas de MongoDB. Lorsqu'il s'est agi de prendre en charge une ou plusieurs bases de données, nous avons estimé que le fait de prendre en charge plusieurs bases de données reviendrait à se priver des avantages uniques de chaque solution ; cela ajouterait également à nos frais généraux d'ingénierie.
Après mûre réflexion, nous avons choisi PostgreSQL. En plus d'une communauté dynamique, d'une documentation complète et d'une myriade de solutions et d'extensions disponibles, nous avons surtout apprécié sa nature open source et le fait que la grande majorité des fournisseurs de cloud proposent des services gérés de PostgreSQL.
Cela signifiait surtout que les utilisateurs d'Infisical pouvaient plus facilement auto-héberger notre plateforme sur n'importe quel fournisseur de cloud et la coupler avec le service géré PostgreSQL correspondant. De plus, compte tenu de la large adoption de cette base de données, nous étions convaincus que les utilisateurs auraient moins de difficultés à l'utiliser dans le cadre d'Infisical.
Qu'en est-il de l'ORM ?
Après avoir choisi PostgreSQL, nous devions déterminer comment notre application interagirait avec la base de données. D'emblée, nous voulions quelque chose de comparable à notre expérience avec MongoDB où nous avons utilisé l'ORM Mongoose. Nous avons donc commencé à évaluer les candidats sur la base de la maturité, de la visualisation et du support de migration, et du niveau d'abstraction approprié ; nous avons principalement considéré Drizzle ORM, Prisma ORM, TypeORM, et Knex.js, un constructeur de requêtes.
Finalement, nous avons décidé d'utiliser Knex.js, un générateur de requêtes, au lieu d'un ORM pour garder un meilleur contrôle sur la base de données. Même s'il est vrai que l'utilisation de SQL brut serait plus polyvalente avec le moins d'abstraction possible, nous avons estimé que cette approche serait beaucoup trop sujette aux erreurs et franchement encombrante à maintenir, surtout sans un support TypeScript approprié. De plus, en plus d'être proche de SQL brut, Knex.js était livré avec sa propre boîte à outils pour l'ensemencement et la migration, avait un écosystème mature avec une excellente documentation et des réponses pour presque toutes les requêtes possibles. Avec le travail d'intégration de Zod, nous avons réussi à atteindre un niveau satisfaisant pour le support de TypeScript.
Après avoir choisi la base de données et l'ORM, nous avons lancé un processus qui allait finalement aboutir à la réécriture de dizaines de structures de données et de centaines de requêtes dans l'ensemble de l'application.
Comment a été planifié la migration ?
Vers la fin de la réécriture du code, nous avons commencé à réfléchir à la manière dont nous allions mener l'opération de migration pour mapper nos données MongoDB vers PostgreSQL avec un minimum d'interruption de la plateforme Infisical Cloud.
Étant donné le rôle critique d'Infisical dans l'infrastructure des clients, nous avons immédiatement exclu la possibilité d'un temps d'arrêt absolu. Nous avons dû faire un compromis en interdisant les opérations d'écriture pendant la brève fenêtre de migration (c'est-à-dire que les clients ne seraient pas en mesure de créer ou de mettre à jour la configuration de l'application) en échange d'une garantie plus élevée de l'intégrité des données. Ce compromis semblait acceptable étant donné que les clients récupéraient principalement des secrets d'Infisical et, dans une bien moindre mesure, mettaient à jour la configuration de leur application seconde par seconde.
Ensuite, en ce qui concerne l'opération de migration proprement dite, nous avons dû extraire les données de MongoDB, les transformer soigneusement et les réinsérer dans PostgreSQL. Lors de l'audit de la séquence de migration, nous avons dû relever des défis tels que s'assurer que les différentes structures arborescentes de NoSQL étaient correctement transformées en leurs équivalents relationnels ; cela était particulièrement délicat pour les structures de données telles que les dossiers qui comportaient des considérations récursives. Nous avons également constaté que nous avions besoin d'un moyen persistant de stocker et de faire correspondre les identifiants de MongoDB à ceux de PostgreSQL ; le faire en mémoire n'aurait pas fonctionné compte tenu de la quantité de données que nous avions à traiter. Finalement, nous avons décidé d'utiliser le magasin clef-valeur LevelDB pour faciliter le stockage des identifiants et les opérations de recherche. Grâce à lui, nous avons transféré les données table par table dans PostgreSQL.
Le processus de migration
La migration elle-même s’est déroulée dans une fenêtre de six heures, pendant laquelle seules les opérations de lecture étaient autorisées sur la plateforme. Nous avons exécuté le script de migration pour déplacer les données de MongoDB vers PostgreSQL, vérifié qu’aucune donnée n’était perdue et, si tout s’est bien passé, nous avons basculé le DNS vers la nouvelle instance. Des plans de secours étaient bien sûr en place en cas de problème.
En fin de compte, la migration vers PostgreSQL a été un succès et a permis d’améliorer notre plateforme. Nous espérons que cette expérience pourra être utile à d’autres personnes envisageant une migration similaire.
Source : Infisical
Et vous ?
Quelles sont les principales différences entre MongoDB et PostgreSQL en termes de modèle de données ? De façon plus générale, quelles sont les avantages et inconvénients de chaque approche (document store vs. relationnel) ?
Quels sont les cas d’utilisation spécifiques où MongoDB pourrait être plus approprié que PostgreSQL, et vice versa ? Considérer les besoins de vos applications, la scalabilité, la flexibilité et les performances.
Comment gérer la migration des données existantes ? Quels sont les défis liés à la migration des données d’une base de données à une autre, en particulier lorsque les schémas diffèrent ?
Quelles sont les meilleures pratiques pour optimiser les performances dans PostgreSQL ? Explorez les index, les requêtes optimisées et les bonnes pratiques de conception de schéma.
Quels sont les avantages et inconvénients de l’utilisation d’ORM (Object-Relational Mapping) lors de la migration ? Penchez-vous sur l’impact de l’utilisation d’ORM sur la migration et la maintenance ?
Voir aussi :
« Au revoir MongoDB » : le témoignage d'un développeur qui a changé MongoDB pour PostgreSQL. Il révèle aussi les inconvénients et les limites du SGBD NoSQL