MongoDB a Postgres
NOTA: Questo articolo del blog è una fusione del mio MongoDB a Postgres (2024-03-06) e Sequelize vs. Prisma (2023-05-25). I blog originali sono stati rimossi e questo blog ha preso il loro posto poiché entrambi contenevano essenzialmente lo stesso contenuto/informazioni. La migrazione è iniziata all’inizio di marzo 2023, il passaggio è avvenuto a metà novembre 2023 e tutte le istanze del vecchio sistema MongoDB sono state completamente dismesse all’inizio di gennaio 2024.
Introduzione
Durante il mio periodo in eBay, mi sono trovato di fronte a quello che è diventato il problema tecnicamente più impegnativo della mia carriera: migrare il Storage Management System (STMS) da MongoDB a Postgres. Non si trattava solo di un semplice cambio di database; era una trasformazione architetturale completa di un sistema critico che ingerisce oltre 1,5 milioni di metriche al minuto attraverso i data center di eBay, con il requisito di zero downtime e il mantenimento di quasi tutta la funzionalità esistente.
Che cos’è STMS?
Il Storage Management System (STMS) funge da strumento interno critico per il team Service & Storage Infrastructure (SSI) di eBay. Monitora e gestisce dispositivi nei data center di eBay, consentendo agli ingegneri di:
- Monitorare metriche da decine di array, switch, host, gruppi di dischi e cluster
- Gestire gli avvisi per switch e array
- Completare attività avanzate come le assegnazioni di host
- Accedere a dati in tempo reale per altri servizi interni di eBay
STMS rappresenta oltre 70 array, 60 switch, 1100 host, 900 gruppi di dischi e 200 cluster in 3 dei data center di eBay. Dato il suo ruolo vitale nell’infrastruttura di eBay, qualsiasi downtime o perdita di funzionalità avrebbe un impatto diretto sui servizi core e sulle operazioni aziendali dell’azienda.
La Sfida
Perché la Migrazione Era Necessaria
La decisione di migrare da MongoDB a Postgres non è stata presa alla leggera. Sebbene MongoDB avesse servito bene STMS inizialmente, la crescente complessità delle relazioni tra i nostri dati e la necessità di capacità di query più sofisticate rendevano Postgres una soluzione migliore a lungo termine per il nostro caso d’uso.
Cosa Rendeva Questo Problema Difficile
La complessità di questa migrazione derivava da diverse sfide fondamentali:
1. Differenze Fondamentali tra Database MongoDB e Postgres sono database fondamentalmente diversi. MongoDB è un database basato su documenti (NoSQL), il che significa che i dati sono memorizzati come JSON in collezioni, come documenti in un archivio. Postgres è un database relazionale (SQL), il che significa che i dati sono memorizzati come righe in tabelle, come in un foglio di calcolo.
2. Architettura della Codebase L’intero backend di STMS era stato costruito per elaborare e gestire i dati come JSON, utilizzando pacchetti compatibili esclusivamente con MongoDB per le operazioni di database. Questo significava non solo cambiare il database, ma ristrutturare il modo in cui l’intera applicazione gestiva i dati.
3. Requisito di Zero Downtime A causa dell’importanza vitale di STMS come strumento interno, non poteva esserci downtime durante la migrazione. Il sistema doveva continuare a servire oltre 1,5 milioni di metriche al minuto per l’intero processo.
4. Tempistiche Strette ed Esperienza Limitata La migrazione doveva essere completata entro pochi mesi, inizialmente senza un piano di esecuzione chiaro. Né io né i miei colleghi avevamo esperienza nella migrazione di una grande codebase legacy da database NoSQL a SQL, e io avevo un’esperienza precedente limitata con Postgres.
5. Scala e Complessità La migrazione implicava la conversione di 36 collezioni MongoDB in 74 tabelle Postgres, richiedendo un’attenta considerazione delle relazioni, dell’indicizzazione e dell’ottimizzazione delle query.
Scegliere l’ORM Giusto: Sequelize vs Prisma
Una delle prime decisioni importanti è stata la selezione di uno strumento ORM (Object-Relational Mapping). Poiché la nostra codebase era già progettata per usare Mongoose per MongoDB, l’uso di un ORM avrebbe fornito il percorso di transizione più agevole.
Analisi dei Requisiti
Dopo un’attenta analisi delle esigenze del progetto, ho stabilito criteri essenziali per qualsiasi soluzione ORM:
- Deve essere un pacchetto JavaScript (la maggior parte del nostro codice era scritta in JavaScript)
- Deve supportare Postgres e la maggior parte delle sue funzionalità
- Le prestazioni devono essere almeno pari o migliori di Mongoose
- Deve essere open source e mantenuto
I Candidati
Dopo un’ampia ricerca, mi sono ristretto a due principali contendenti: Sequelize e Prisma. Ho creato ambienti di test completi usando Docker per Postgres e ho convertito il nostro dataset più grande e complesso da struttura a documenti a struttura a tabelle.
Metodologia di Test
Per ciascun ORM, ho misurato le prestazioni su operazioni critiche:
- Tempo per creare una voce
- Tempo per aggiornare una voce
- Tempo per aggiornare voci annidate (relazioni e coppie chiave-valore JSON)
- Tempo per eliminare una voce
- Tempo per interrogare/ottenere una voce
La Decisione: Sequelize
Intorno al 15 maggio 2023, ho deciso che Sequelize fosse l’ORM migliore per il nostro caso d’uso. Ecco perché:
Vantaggi di Sequelize:
- Veramente open source e non mantenuto da una startup finanziata
- Supportava la maggior parte delle funzionalità di Postgres
- Prestazioni migliori, soprattutto rispetto a Prisma
- Ecosistema maturo con oltre 10 anni di sviluppo
- Rappresentazione del modello/schema flessibile usando classi JavaScript
- Supporto per join complessi e opzioni di filtro, incluso Regex
Risultati sulle Prestazioni:
Nei miei test, Sequelize superava significativamente Prisma. Per le voci del nostro grande dataset:
- Sequelize: ~2,26 secondi per voce
- Prisma: ~11,21 secondi per voce
Prisma era circa 5 volte più lento di Sequelize per il nostro caso d’uso. Inoltre, eliminare una voce dal nostro dataset più grande richiedeva a Prisma quasi 4 minuti, il che era inaccettabile per i nostri requisiti.
Sfide di Sequelize:
- Rappresentazioni del modello più complesse e ingombranti (564 righe contro 262 righe per Mongoose)
- Sintassi confusa in alcuni casi
- Complessità nella migrazione del database
- Documentazione meno completa rispetto a Prisma
Confronto Pro e Contro di Sequelize e Prisma
Per offrire un quadro più completo del perché abbia scelto Sequelize, voglio condividere i pro e contro dettagliati che ho compilato per entrambi gli ORM durante la mia valutazione. Ho anche esaminato come si confrontavano in termini di rappresentazione dello schema e supporto della community al 15 maggio 2023. Questo approfondimento ha aiutato a consolidare la mia scelta, e spero possa essere utile anche ad altri che affrontano una decisione simile.
Pro di Sequelize:
- Ha una funzione sync() che crea e gestisce automaticamente le tabelle per te, risparmiando molto lavoro manuale.
- Può gestire join complessi per dati annidati, cosa che era fondamentale per la struttura di STMS.
- Supporta un’ampia gamma di opzioni di filtro, incluso Regex, offrendo flessibilità nelle query.
- La rappresentazione del modello/schema è fatta in puro JavaScript usando classi, altamente personalizzabili per adattarsi a esigenze specifiche.
- Gestisce le connessioni al database senza problemi, incluso il supporto per più connessioni di lettura.
- Supporta query SQL grezze quando hai bisogno di andare sotto il cofano.
- Statistiche della community al 15 maggio 2023: su NPM, ultimo aggiornamento 14 giorni fa con 1.505.835 download settimanali; su GitHub, ultimo aggiornamento ieri con 4,2k Fork e 27,9k Star. È stato open source con licenza MIT per oltre 10 anni, quindi sono sicuro che continuerà ad esserlo.
Contro di Sequelize:
- La rappresentazione del modello/schema può diventare molto complessa e ingombrante. Per esempio, mentre la rappresentazione Mongoose del nostro grande dataset era di circa 262 righe (inclusi gli spazi), lo stesso dataset in Sequelize è arrivato a 564 righe.
- La sintassi può essere confusa e complicata in alcuni scenari, rallentandomi a volte.
- Migrare o modificare il database è una seccatura. Anche con sequelize-cli che genera script di migrazione, resta comunque macchinoso, anche se ho notato che è un punto dolente comune della maggior parte degli ORM.
- La documentazione non è eccezionale, anche se sta migliorando. Per fortuna, strumenti come ChatGPT hanno una buona padronanza di Sequelize grazie alla sua lunga storia, il che ha aiutato a colmare le lacune.
- Meno sensibile ai tipi rispetto a Prisma, il che potrebbe causare problemi in alcuni progetti.
- Supporto TypeScript limitato; sebbene questo non fosse un problema per STMS, potrebbe essere un fattore decisivo per altri.
Pro di Prisma:
- Usa un proprio linguaggio di schema, rendendo la creazione dei modelli più pulita e concisa. Per confronto, mentre Mongoose richiedeva 262 righe per il nostro grande dataset, Prisma lo gestiva in sole 221 righe.
- Include uno strumento CLI che semplifica la creazione e la migrazione del database, che è il migliore che abbia visto finora da un ORM, anche se non è perfetto.
- Supporta query SQL grezze, offrendo flessibilità quando necessario.
- La sintassi del codice è pulita e più semplice da comprendere rispetto a Sequelize, rendendolo più facile da imparare.
- Genera automaticamente query builder per Node.js e TypeScript tramite il suo client, il che è un bel tocco.
- Ha una documentazione eccellente e pulita. ChatGPT non è aggiornato su Prisma allo stesso livello, ma la documentazione ufficiale spesso compensava.
- Statistiche della community al 15 maggio 2023: su NPM, ultimo aggiornamento 6 giorni fa con 1.344.705 download settimanali; su GitHub, ultimo aggiornamento 3 ore fa con 1,1k Fork e 31,3k Star.
Contro di Prisma:
- Non supporta il filtro Regex per Postgres, anche se offre alternative come “contains”, “includes” e “startsWith.”
- Le prestazioni erano un problema importante nei miei test. Creare voci per il nostro grande dataset richiedeva a Prisma circa 11,21 secondi per voce contro i 2,26 secondi di Sequelize, circa 5 volte più lento.
- Eliminare una singola voce dal grande dataset richiedeva quasi 4 minuti, il che era un fattore decisivo per le nostre esigenze.
- Anche con un confronto equo su un dataset di relazioni complesse, profondo tre livelli, Sequelize era significativamente più veloce nelle eliminazioni.
- Prisma è sostenuto da una startup con 56,5 milioni di dollari di finanziamenti. Sebbene il codice principale dell’ORM sia open source sotto Apache-2.0, sono cauto riguardo a possibili cambiamenti di licenza lungo il percorso, simili a quanto accaduto con MongoDB.
Questi confronti dettagliati hanno reso chiaro che Sequelize si allineava meglio alle esigenze di STMS, soprattutto per le prestazioni e l’affidabilità a lungo termine. Ma ho pensato che scomporlo in questo modo potesse aiutare altri che stanno affrontando la stessa scelta per i loro progetti.
Il processo di migrazione
Trasformazione della struttura dei dati
La conversione dalla struttura documentale di MongoDB alla struttura relazionale di Postgres ha richiesto un’attenta pianificazione. Ho dovuto:
- Analizzare le relazioni: Identificare come i documenti MongoDB erano collegati tra loro e progettare relazioni di chiave esterna appropriate
- Normalizzare i dati: Suddividere i documenti annidati in tabelle separate dove opportuno
- Conservare le funzionalità JSON: Usare colonne JSONB per i dati veramente non strutturati che dovevano rimanere flessibili
- Progettare gli indici: Creare indici appropriati per le prestazioni delle query
Soluzioni personalizzate
La migrazione ha richiesto lo sviluppo di diverse soluzioni personalizzate:
1. Script di migrazione dei dati Ho ստեղծato script completi per:
- Estrarre i dati dalle raccolte MongoDB
- Trasformare le strutture dei documenti in formato relazionale
- Importare i dati nelle tabelle Postgres con relazioni corrette
2. Strato di compatibilità API Per mantenere zero downtime, ho costruito uno strato di compatibilità che poteva:
- Instradare le richieste a MongoDB o a Postgres a seconda dello stato della migrazione
- Garantire la coerenza dei dati durante il periodo di transizione
- Fornire meccanismi di fallback
3. Middleware personalizzato Ho sviluppato middleware per gestire le differenze nel modo in cui MongoDB e Postgres trattano determinate operazioni, assicurando che gli endpoint API esistenti continuassero a funzionare senza modifiche.
Superare le sfide tecniche
Gestione di relazioni complesse
Una delle sfide più grandi è stata convertire i documenti incorporati di MongoDB in relazioni Postgres. Ad esempio, un singolo documento MongoDB poteva contenere:
- Proprietà di base
- Oggetti annidati che rappresentano entità correlate
- Array di documenti incorporati
Questo ha dovuto essere attentamente scomposto in:
- Tabelle principali per le entità principali
- Tabelle di giunzione per le relazioni molti-a-molti
- Relazioni di chiave esterna per le associazioni uno-a-molti
Ottimizzazione delle query
I modelli di query di MongoDB non si traducono direttamente in SQL. Ho dovuto:
- Riscrivere pipeline di aggregazione complesse come join SQL
- Ottimizzare gli indici per i nuovi modelli di query
- Assicurarmi che le prestazioni delle query raggiungessero o superassero quelle di MongoDB
Integrità dei dati
Garantire l’integrità dei dati durante la migrazione ha richiesto:
- Script di validazione completi
- Procedure di rollback
- Sincronizzazione dei dati in tempo reale durante i periodi di transizione
Risultati e impatto
La migrazione STMS da MongoDB a Postgres è stata completata con successo e senza downtime, mantenendo quasi tutte le funzionalità. I risultati hanno superato le aspettative:
Miglioramenti delle prestazioni:
- Le prestazioni delle query sono migliorate per le query relazionali complesse
- Migliore coerenza e integrità dei dati
- Utilizzo dello spazio di archiviazione più efficiente
Vantaggi operativi:
- Capacità di monitoraggio e debugging migliorate
- Migliore integrazione con gli strumenti SQL esistenti di eBay
- Procedure di backup e ripristino migliorate
Impatto sul team:
- Maggiore conoscenza dei database relazionali nel team
- Modelli stabiliti per future migrazioni di database
- Strumenti e processi riutilizzabili creati
Competenze tecniche acquisite
Questo progetto ha ampliato in modo significativo la mia competenza tecnica:
Tecnologie di database:
- Profonda comprensione delle funzionalità e dell’ottimizzazione di Postgres
- Ottimizzazione delle query SQL e tuning delle prestazioni
- Pattern di progettazione dei database e normalizzazione
- Configurazioni di database primary-standby
Strumenti di sviluppo:
- ORM Sequelize e costruzione di query
- Strategie di migrazione dei database
- Metodologie di testing delle prestazioni
- Validazione dei dati e controllo dell’integrità
Pattern architetturali:
- Strategie di migrazione senza downtime
- Strati di compatibilità API
- Pattern di astrazione del database
- Sistemi di monitoraggio e alerting
Crescita personale e professionale
Questo progetto di migrazione è stato trasformativo per il mio sviluppo di carriera. Mi ha spinto in un territorio inesplorato, richiedendo:
Competenze di leadership:
- Guidare un progetto tecnico complesso senza esperienza pregressa
- Prendere decisioni architetturali critiche sotto pressione
- Coordinarsi con più team e stakeholder
Capacità di problem solving:
- Scomporre problemi complessi in componenti gestibili
- Sviluppare soluzioni creative per sfide senza precedenti
- Bilanciare requisiti e vincoli multipli in competizione
Comunicazione e lavoro di squadra:
- Spiegare concetti tecnici a stakeholder non tecnici
- Documentare processi e decisioni per riferimenti futuri
- Fare mentoring ai membri del team su nuove tecnologie e modelli
Lezioni apprese
Lezioni tecniche
- La selezione del database conta: La scelta tra NoSQL e SQL dovrebbe basarsi su casi d’uso specifici e requisiti a lungo termine
- Il test delle prestazioni è fondamentale: I vantaggi teorici non si traducono sempre in miglioramenti prestazionali nel mondo reale
- Pianificazione della migrazione: Una pianificazione e un testing completi sono essenziali per migrazioni complesse
- Investimento negli strumenti: Costruire strumenti adeguati fin dall’inizio fa risparmiare molto tempo e riduce gli errori
Lezioni di project management
- Comunicazione con gli stakeholder: Aggiornamenti regolari e comunicazione chiara prevengono i malintesi
- Gestione del rischio: Avere piani di fallback e procedure di rollback è essenziale
- Gestione delle tempistiche: Prevedere margine di tempo per sfide impreviste e curve di apprendimento
- Documentazione: Una documentazione accurata consente il trasferimento di conoscenze e la manutenzione futura
Conclusione
La migrazione di STMS da MongoDB a Postgres rappresenta il problema tecnico più impegnativo e gratificante che abbia mai risolto nella mia carriera. Ha richiesto non solo competenza tecnica, ma anche leadership, pianificazione e adattabilità. Il successo del progetto ha dimostrato che, con una pianificazione adeguata, test approfonditi e impegno verso l’eccellenza, anche le sfide tecniche più complesse possono essere superate.
Questa esperienza ha cambiato in modo fondamentale il mio approccio all’ingegneria del software, enfatizzando l’importanza di:
- Comprendere il contesto e i requisiti completi prima di prendere decisioni tecniche
- Investire tempo in strumenti e test adeguati
- Mantenere una comunicazione chiara לאורך tutto il corso di progetti complessi
- Essere disposti a imparare nuove tecnologie e approcci quando necessario
Il successo della migrazione non solo ha migliorato le capacità di STMS, ma ha anche stabilito modelli e processi che continuano a portare benefici ai progetti infrastrutturali di eBay. Ha rafforzato la mia convinzione che abbracciare sfide sconosciute e riuscire a superarle sia la chiave sia per la crescita personale sia per quella professionale.
Guardando indietro, questo progetto rappresenta un punto di svolta nella mia carriera, trasformandomi da sviluppatore che implementa soluzioni a ingegnere capace di progettare e guidare iniziative tecniche complesse. La fiducia e le competenze acquisite grazie a questa esperienza continuano a guidare il mio approccio alle nuove sfide e opportunità nell’ingegneria del software.