MongoDB vers Postgres

🚨 Ce billet de blog est une fusion de mon MongoDB to Postgres (2024-03-06) et Sequelize vs. Prisma (2023-05-25). Les blogs originaux ont été supprimés, et ce blog a pris leur place puisque les deux contenaient essentiellement le même contenu/information. La migration a commencé en début mars 2023, le basculement a eu lieu en mi-novembre 2023, et toutes les instances de l’ancien système MongoDB ont été complètement arrêtées en début janvier 2024.

Introduction

Pendant mon passage chez eBay, j’ai été confronté à ce qui est devenu le problème le plus techniquement difficile de ma carrière : migrer le système de gestion du stockage (STMS) de MongoDB vers Postgres. Ce n’était pas simplement un échange de bases de données ; c’était une transformation architecturale complète d’un système critique qui ingère plus de 1,5 million de métriques par minute à travers les centres de données d’eBay, avec l’exigence de zéro temps d’arrêt et le maintien de presque toutes les fonctionnalités existantes.

Qu’est-ce que STMS ?

Le Storage Management System (STMS) sert d’outil interne critique pour l’équipe Service & Storage Infrastructure (SSI) d’eBay. Il surveille et gère les appareils à travers les centres de données d’eBay, permettant aux ingénieurs de :

  • Surveiller les métriques provenant de dizaines de baies, commutateurs, hôtes, groupes de disques et clusters
  • Gérer les alertes pour les commutateurs et les baies
  • Effectuer des tâches avancées comme les allocations d’hôtes
  • Accéder aux données en temps réel pour d’autres services internes d’eBay

STMS comprend plus de 70 baies, 60 commutateurs, 1100 hôtes, 900 groupes de disques et 200 clusters répartis sur 3 des centres de données d’eBay. Étant donné son rôle vital dans l’infrastructure d’eBay, toute interruption ou perte de fonctionnalité impacterait directement les services principaux de l’entreprise et ses opérations commerciales.

Le Défi

Pourquoi la migration était nécessaire

La décision de migrer de MongoDB vers Postgres n’a pas été prise à la légère. Bien que MongoDB ait bien servi STMS au départ, la complexité croissante de nos relations de données et le besoin de capacités de requêtage plus sophistiquées ont fait de Postgres une meilleure solution à long terme pour notre cas d’utilisation.

Ce qui a rendu ce problème difficile

La complexité de cette migration découlait de plusieurs défis fondamentaux :

1. Différences fondamentales de bases de données
MongoDB et Postgres sont fondamentalement des bases de données différentes. MongoDB est une base de données orientée documents (NoSQL), ce qui signifie que les données sont stockées sous forme de JSON dans des collections, comme des documents dans un classeur. Postgres est une base de données relationnelle (SQL), ce qui signifie que les données sont stockées sous forme de lignes dans des tables, comme dans une feuille de calcul.

2. Architecture du code
L’ensemble du backend de STMS était construit pour traiter et gérer les données sous forme de JSON, en utilisant des packages exclusivement compatibles avec MongoDB pour les opérations de base de données. Cela signifiait non seulement changer la base de données, mais restructurer la façon dont notre application entière gérait les données.

3. Exigence de zéro temps d’arrêt
En raison de l’importance cruciale de STMS en tant qu’outil interne, il ne pouvait y avoir aucun temps d’arrêt pendant la migration. Le système devait continuer à fournir plus de 1,5 million de métriques par minute pendant l’ensemble du processus.

4. Calendrier serré et expérience limitée
La migration devait être terminée en quelques mois, sans plan d’exécution clair au départ. Ni moi ni mes collègues n’avions d’expérience dans la migration d’un grand code hérité d’une base NoSQL vers des bases SQL, et j’avais une expérience limitée avec Postgres.

5. Échelle et complexité
La migration impliquait de convertir 36 collections MongoDB en 74 tables Postgres, nécessitant une prise en compte minutieuse des relations, de l’indexation et de l’optimisation des requêtes.

Choisir le bon ORM : Sequelize vs Prisma

Une des premières décisions majeures a été de choisir un outil ORM (Object-Relational Mapping). Puisque notre code était déjà conçu pour utiliser Mongoose avec MongoDB, l’utilisation d’un ORM offrirait le chemin de transition le plus fluide.

Analyse des exigences

Après une analyse minutieuse des besoins du projet, j’ai établi des critères essentiels pour toute solution ORM :

  • Doit être un package JavaScript (la plupart de notre code était écrit en JavaScript)
  • Doit prendre en charge Postgres et la plupart de ses fonctionnalités
  • Les performances doivent être au moins équivalentes ou supérieures à Mongoose
  • Doit être open source et maintenu

Les candidats

Après de nombreuses recherches, je me suis limité à deux principaux concurrents : Sequelize et Prisma. J’ai créé des environnements de test complets en utilisant Docker pour Postgres et converti notre jeu de données le plus grand et le plus complexe de la structure de documents à la structure de tables.

Méthodologie de test

Pour chaque ORM, j’ai mesuré les performances sur des opérations critiques :

  • Temps pour créer une entrée
  • Temps pour mettre à jour une entrée
  • Temps pour mettre à jour des entrées imbriquées (relations et paires clé-valeur JSON)
  • Temps pour supprimer une entrée
  • Temps pour interroger/obtenir une entrée

La décision : Sequelize

Vers le 15 mai 2023, j’ai décidé que Sequelize était le meilleur ORM pour notre cas d’utilisation. Voici pourquoi :

Avantages de Sequelize :

  • Vraiment open source et non maintenu par une startup financée
  • Prend en charge la plupart des fonctionnalités de Postgres
  • Meilleures performances, surtout comparé à Prisma
  • Écosystème mature avec plus de 10 ans de développement
  • Représentation flexible du modèle/schema utilisant des classes JavaScript
  • Support des jointures complexes et options de filtrage incluant les expressions régulières

Résultats de performance :

Dans mes tests, Sequelize a largement surpassé Prisma. Pour les entrées de notre grand jeu de données :

  • Sequelize : ~2,26 secondes par entrée
  • Prisma : ~11,21 secondes par entrée

Prisma était environ 5 fois plus lent que Sequelize pour notre cas d’utilisation. De plus, la suppression d’une entrée de notre plus grand jeu de données prenait à Prisma près de 4 minutes, ce qui était inacceptable pour nos exigences.

Défis de Sequelize :

  • Représentations de modèle plus complexes et gonflées (564 lignes contre 262 lignes pour Mongoose)
  • Syntaxe confuse dans certains cas
  • Complexité de la migration de base de données
  • Documentation moins complète comparée à Prisma

Comparaison des avantages et inconvénients de Sequelize & Prisma

Pour donner une image plus complète des raisons pour lesquelles j’ai choisi Sequelize, je souhaite partager les avantages et inconvénients détaillés que j’ai compilés pour les deux ORM lors de mon évaluation. J’ai également examiné comment ils se positionnaient en termes de représentation du schéma et de soutien communautaire au 15 mai 2023. Cette analyse approfondie a renforcé mon choix, et j’espère qu’elle pourra être utile à d’autres confrontés à une décision similaire.

Avantages de Sequelize :

  • Dispose d’une fonction sync() qui crée et gère automatiquement les tables pour vous, économisant beaucoup d’effort manuel.
  • Peut gérer des jointures complexes pour les données imbriquées, ce qui était crucial pour la structure de STMS.
  • Prend en charge un large éventail d’options de filtrage, y compris les expressions régulières, offrant une flexibilité dans les requêtes.
  • La représentation du modèle/schema est faite en JavaScript brut utilisant des classes, hautement personnalisables pour répondre à des besoins spécifiques.
  • Gère les connexions à la base de données de manière fluide, y compris le support de multiples connexions de lecture.
  • Prend en charge les requêtes SQL brutes lorsque vous devez aller plus loin.
  • Statistiques communautaires au 15 mai 2023 : sur NPM, dernière mise à jour il y a 14 jours avec 1 505 835 téléchargements hebdomadaires ; sur GitHub, dernière mise à jour hier avec 4,2 k forks et 27,9 k étoiles. Il est open source sous licence MIT depuis plus de 10 ans, je suis donc confiant qu’il le restera.

Inconvénients de Sequelize :

  • La représentation du modèle/schema peut devenir très complexe et gonflée. Par exemple, alors que la représentation Mongoose de notre grand jeu de données faisait environ 262 lignes (espaces inclus), le même jeu de données sous Sequelize a gonflé à 564 lignes.
  • La syntaxe peut être confuse et compliquée dans certains scénarios, ce qui m’a ralenti parfois.
  • Migrer ou éditer la base de données est fastidieux. Même avec sequelize-cli générant des scripts de migration, c’est toujours lourd, bien que j’aie remarqué que c’est un point douloureux commun à la plupart des ORM.
  • La documentation n’est pas excellente, bien qu’elle s’améliore. Heureusement, des outils comme ChatGPT maîtrisent bien Sequelize grâce à son histoire longue, ce qui a aidé à combler les lacunes.
  • Pas aussi sensible aux types que Prisma, ce qui pourrait entraîner des problèmes dans certains projets.
  • Support limité de TypeScript, bien que cela n’ait pas été un problème pour STMS, cela pourrait être un facteur décisif pour d’autres.

Avantages de Prisma :

  • Utilise son propre langage de schéma, rendant la création de modèles plus propre et concise. À titre de comparaison, alors que Mongoose nécessitait 262 lignes pour notre grand jeu de données, Prisma l’a géré en seulement 221 lignes.
  • Vient avec un outil CLI qui simplifie la création et la migration de bases de données, le meilleur que j’ai vu d’un ORM à ce jour, même s’il n’est pas parfait.
  • Prend en charge les requêtes SQL brutes, offrant de la flexibilité quand nécessaire.
  • La syntaxe du code est propre et plus simple à comprendre comparée à Sequelize, ce qui la rend plus facile à apprendre.
  • Génère automatiquement des constructeurs de requêtes pour Node.js et TypeScript via son client, ce qui est un plus.
  • Dispose d’une documentation excellente et claire. ChatGPT n’est pas aussi à jour sur Prisma, mais la documentation officielle compense souvent cela.
  • Statistiques communautaires au 15 mai 2023 : sur NPM, dernière mise à jour il y a 6 jours avec 1 344 705 téléchargements hebdomadaires ; sur GitHub, dernière mise à jour il y a 3 heures avec 1,1 k forks et 31,3 k étoiles.

Inconvénients de Prisma :

  • Ne prend pas en charge le filtrage Regex pour Postgres, bien qu’il offre des alternatives comme “contains”, “includes” et “startsWith”.
  • Les performances étaient un problème majeur dans mes tests. Créer des entrées pour notre grand jeu de données prenait à Prisma environ 11,21 secondes par entrée contre 2,26 secondes pour Sequelize, soit environ 5 fois plus lent.
  • Supprimer une seule entrée du grand jeu de données prenait près de 4 minutes, ce qui était un facteur décisif pour nos besoins.
  • Même avec une comparaison équitable sur un jeu de données complexe à trois niveaux de relations, Sequelize était nettement plus rapide lors des suppressions.
  • Prisma est soutenu par une startup avec 56,5 M$ de financement. Bien que son code principal d’ORM soit open source sous Apache‑2.0, je crains d’éventuels changements de licence à l’avenir, similaire à ce qui s’est produit avec MongoDB.

Ces comparaisons détaillées ont clairement montré que Sequelize correspondait mieux aux besoins de STMS, notamment en termes de performances et de fiabilité à long terme. Mais j’ai pensé que le décortiquer ainsi pourrait aider d’autres personnes confrontées au même choix pour leurs projets.

Le processus de migration

Transformation de la structure des données

Convertir la structure de documents de MongoDB en structure relationnelle de Postgres a nécessité une planification minutieuse. J’ai dû :

  1. Analyser les relations : Identifier comment les documents MongoDB sont liés les uns aux autres et concevoir des relations de clé étrangère appropriées
  2. Normaliser les données : Décomposer les documents imbriqués en tables séparées lorsque cela est approprié
  3. Conserver les fonctionnalités JSON : Utiliser des colonnes JSONB pour les données réellement non structurées qui doivent rester flexibles
  4. Concevoir des index : Créer des index appropriés pour les performances des requêtes

Solutions personnalisées

La migration a nécessité le développement de plusieurs solutions personnalisées :

1. Scripts de migration de données
J’ai créé des scripts complets pour :

  • Extraire les données des collections MongoDB
  • Transformer les structures de documents en format relationnel
  • Importer les données dans les tables Postgres avec les relations appropriées

2. Couche de compatibilité API
Pour maintenir une disponibilité totale, j’ai construit une couche de compatibilité qui pouvait :

  • Acheminer les requêtes vers MongoDB ou Postgres selon l’état de la migration
  • Assurer la cohérence des données pendant la période de transition
  • Fournir des mécanismes de secours

3. Middleware personnalisé
Développé un middleware pour gérer les différences dans la façon dont MongoDB et Postgres traitent certaines opérations, garantissant que les points de terminaison API existants continuent de fonctionner sans modification.

Surmonter les défis techniques

Gestion des relations complexes

L’un des plus grands défis était de convertir les documents imbriqués de MongoDB en relations Postgres. Par exemple, un seul document MongoDB peut contenir :

  • Propriétés de base
  • Objets imbriqués représentant des entités liées
  • Tableaux de documents imbriqués

Cela devait être soigneusement décomposé en :

  • Tables principales pour les entités principales
  • Tables de jonction pour les relations plusieurs-à-plusieurs
  • Relations de clé étrangère pour les associations un-à-plusieurs

Optimisation des requêtes

Les modèles de requêtes de MongoDB ne se traduisent pas directement en SQL. J’ai dû :

  • Réécrire les pipelines d’agrégation complexes en jointures SQL
  • Optimiser les index pour les nouveaux modèles de requêtes
  • Assurer que les performances des requêtes atteignent ou dépassent celles de MongoDB

Intégrité des données

Garantir l’intégrité des données pendant la migration nécessitait :

  • Scripts de validation complets
  • Procédures de retour arrière
  • Synchronisation des données en temps réel pendant les périodes de transition

Résultats et impact

La migration STMS de MongoDB vers Postgres a été réalisée avec succès sans interruption tout en conservant presque toutes les fonctionnalités. Les résultats ont dépassé les attentes :

Améliorations de performance :

  • Les performances des requêtes se sont améliorées pour les requêtes relationnelles complexes
  • Meilleure cohérence et intégrité des données
  • Utilisation du stockage plus efficace

Avantages opérationnels :

  • Capacités de surveillance et de débogage améliorées
  • Meilleure intégration avec les outils SQL existants d’eBay
  • Procédures de sauvegarde et de récupération améliorées

Impact sur l’équipe :

  • Connaissances accrues de l’équipe sur les bases de données relationnelles
  • Établissement de modèles pour les futures migrations de bases de données
  • Création d’outils et de processus réutilisables

Compétences techniques acquises

Ce projet a considérablement élargi mon expertise technique :

Technologies de bases de données :

  • Compréhension approfondie des fonctionnalités et de l’optimisation de Postgres
  • Optimisation des requêtes SQL et réglage des performances
  • Modèles de conception de bases de données et normalisation
  • Configurations de bases de données primaire-standby

Outils de développement :

  • ORM Sequelize et construction de requêtes
  • Stratégies de migration de bases de données
  • Méthodologies de tests de performance
  • Validation des données et vérification de l’intégrité

Modèles d’architecture :

  • Stratégies de migration sans interruption
  • Couches de compatibilité API
  • Modèles d’abstraction de bases de données
  • Systèmes de surveillance et d’alerte

Croissance personnelle et professionnelle

Ce projet de migration a été transformateur pour le développement de ma carrière. Il m’a poussé dans un territoire inexploré, nécessitant :

Compétences en leadership :

  • Diriger un projet technique complexe sans expérience préalable
  • Prendre des décisions architecturales critiques sous pression
  • Coordonner avec plusieurs équipes et parties prenantes

Capacités de résolution de problèmes :

  • Décomposer des problèmes complexes en composants gérables
  • Développer des solutions créatives à des défis sans précédent
  • Équilibrer de multiples exigences et contraintes concurrentes

Communication et travail d’équipe :

  • Expliquer les concepts techniques aux parties prenantes non techniques
  • Documenter les processus et décisions pour référence future
  • Encadrer les membres de l’équipe sur les nouvelles technologies et modèles

Leçons tirées

Leçons techniques

  1. Le choix de la base de données est important : Le choix entre NoSQL et SQL doit être basé sur des cas d’utilisation spécifiques et des exigences à long terme
  2. Les tests de performance sont critiques : Les avantages théoriques ne se traduisent pas toujours par des gains de performance réels
  3. Planification de la migration : Une planification et des tests complets sont essentiels pour les migrations complexes
  4. Investissement dans les outils : Construire des outils appropriés dès le départ permet d’économiser un temps considérable et de réduire les erreurs

Leçons de gestion de projet

  1. Communication avec les parties prenantes : Des mises à jour régulières et une communication claire préviennent les malentendus
  2. Gestion des risques : Disposer de plans de secours et de procédures de retour arrière est essentiel
  3. Gestion du calendrier : Prévoir du temps tampon pour les défis inattendus et les courbes d’apprentissage
  4. Documentation : Une documentation approfondie permet le transfert de connaissances et la maintenance future

Conclusion

La migration STMS de MongoDB vers Postgres représente le problème technique le plus difficile et le plus gratifiant que j’ai résolu dans ma carrière. Elle a nécessité non seulement une expertise technique, mais aussi du leadership, de la planification et de l’adaptabilité. Le succès du projet a démontré qu’avec une planification adéquate, des tests approfondis et un engagement envers l’excellence, même les plus complexes défis techniques peuvent être surmontés.

Cette expérience a fondamentalement changé mon approche du génie logiciel, soulignant l’importance de :

  • Comprendre le contexte complet et les exigences avant de prendre des décisions techniques
  • Investir du temps dans des outils et des tests appropriés
  • Maintenir une communication claire tout au long des projets complexes
  • Être disposé à apprendre de nouvelles technologies et approches lorsque cela est nécessaire

Le succès de la migration a non seulement amélioré les capacités de STMS mais a également établi des modèles et des processus qui continuent de bénéficier aux projets d’infrastructure d’eBay. Cela a renforcé ma conviction que relever des défis inconnus et réussir grâce à eux est essentiel tant pour le développement personnel que professionnel.

En regardant en arrière, ce projet représente un tournant dans ma carrière, me transformant d’un développeur qui implémente des solutions en un ingénieur capable d’architecturer et de diriger des initiatives techniques complexes. La confiance et les compétences acquises grâce à cette expérience continuent de guider mon approche face aux nouveaux défis et opportunités en génie logiciel.