MongoDBからPostgresへ

🚨 このブログ記事は、私の MongoDB to Postgres (2024-03-06)Sequelize vs. Prisma (2023-05-25) の統合です。元の記事は削除され、両方が本質的に同じ内容/情報を含んでいたため、このブログがその代わりとなっています。移行は 2023年3月初旬 に開始され、2023年11月中旬 に切り替えが行われ、古いMongoDBシステムのすべてのインスタンスは 2024年1月初旬 に完全に停止されました。

Introduction

During my time at eBay, I faced what became the most technically challenging problem of my career: migrating the Storage Management System (STMS) from MongoDB to Postgres. This wasn’t just a simple database swap; it was a complete architectural transformation of a critical system that ingests over 1.5 million metrics per minute across eBay’s data centers, with the requirement of zero downtime and maintaining nearly all existing functionality.

What is STMS?

The Storage Management System (STMS) serves as a critical internal tool for eBay’s Service & Storage Infrastructure (SSI) team. It monitors and manages devices across eBay’s data centers, allowing engineers to:

  • Monitor metrics from dozens of arrays, switches, hosts, disk groups, and clusters
  • Handle alerting for switches and arrays
  • Complete advanced tasks like host allocations
  • Access real-time data for other internal eBay services

STMS accounts for over 70 arrays, 60 switches, 1100 hosts, 900 disk groups, and 200 clusters across 3 of eBay’s data centers. Given its vital role in eBay’s infrastructure, any downtime or loss of functionality would directly impact the company’s core services and business operations.

The Challenge

Why the Migration Was Necessary

The decision to migrate from MongoDB to Postgres wasn’t taken lightly. While MongoDB had served STMS well initially, the growing complexity of our data relationships and the need for more sophisticated querying capabilities made Postgres a better long-term solution for our use case.

What Made This Problem Difficult

The complexity of this migration stemmed from several fundamental challenges:

1. Fundamental Database Differences
MongoDB and Postgres are fundamentally different databases. MongoDB is a document-based database (NoSQL), meaning data is stored as JSON in collections, like documents in a filing cabinet. Postgres is a relational database (SQL), meaning data is stored as rows in tables, like in a spreadsheet.

2. Codebase Architecture
STMS’s entire backend was built to process and manage data as JSONs, using packages exclusively compatible with MongoDB for database operations. This meant not just changing the database, but restructuring how our entire application handled data.

3. Zero Downtime Requirement
Due to how vital STMS is as an internal tool, there could be no downtime during the migration. The system had to continue serving 1.5+ million metrics per minute throughout the entire process.

4. Tight Timeline & Limited Experience
The migration had to be completed within a few months, with no clear execution plan initially. Neither I nor my coworkers had experience migrating a large legacy codebase from NoSQL to SQL databases, and I had limited prior experience with Postgres.

5. Scale & Complexity
The migration involved converting 36 MongoDB collections into 74 Postgres tables, requiring careful consideration of relationships, indexing, and query optimization.

Choosing the Right ORM: Sequelize vs Prisma

One of the first major decisions was selecting an ORM (Object-Relational Mapping) tool. Since our codebase was already designed to use Mongoose for MongoDB, using an ORM would provide the smoothest transition path.

Requirements Analysis

After careful analysis of the project’s needs, I established essential criteria for any ORM solution:

  • Must be a JavaScript package (most of our code was written in JavaScript)
  • Must support Postgres and most of its features
  • Performance must be at least on par or better than Mongoose
  • Must be open source and maintained

The Candidates

After extensive research, I narrowed down to two main contenders: Sequelize and Prisma. I created comprehensive testing environments using Docker for Postgres and converted our largest, most complex dataset from document structure to table structure.

Testing Methodology

For each ORM, I measured performance across critical operations:

  • Time to create an entry
  • Time to update an entry
  • Time to update nested entries (relationships and JSON key-values)
  • Time to delete an entry
  • Time to query/get an entry

The Decision: Sequelize

Around May 15, 2023, I decided that Sequelize was the better ORM for our use case. Here’s why:

Sequelize Advantages:

  • Truly open-source and not maintained by a funded startup
  • Supported most of Postgres’s features
  • Better performance, especially compared to Prisma
  • Mature ecosystem with over 10 years of development
  • Flexible model/schema representation using JavaScript classes
  • Support for complex joins and filtering options including Regex

Performance Results:

In my testing, Sequelize significantly outperformed Prisma. For our large dataset entries:

  • Sequelize: ~2.26 seconds per entry
  • Prisma: ~11.21 seconds per entry

Prisma was approximately 5x slower than Sequelize for our use case. Additionally, deleting one entry from our largest dataset took Prisma nearly 4 minutes, which was unacceptable for our requirements.

Sequelize Challenges:

  • More complex and bloated model representations (564 lines vs 262 lines for Mongoose)
  • Confusing syntax in certain cases
  • Database migration complexity
  • Less comprehensive documentation compared to Prisma

Pros & Cons Comparison of Sequelize & Prisma

To give a fuller picture of why I went with Sequelize, I want to share the detailed pros and cons I compiled for both ORMs during my evaluation. I also looked at how they stacked up in terms of schema representation and community support as of May 15, 2023. This deeper dive helped solidify my choice, and I hope it might be useful for anyone else facing a similar decision.

Sequelize Pros:

  • Has a sync() function that automatically creates and handles tables for you, saving a lot of manual effort.
  • Can handle complex joins for nested data, which was critical for STMS’s structure.
  • Supports a wide range of filtering options, including Regex, giving flexibility in queries.
  • Model/schema representation is done in raw JavaScript using classes, which are highly customizable to fit specific needs.
  • Handles database connections seamlessly, including support for multiple read-connections.
  • Supports raw SQL queries for when you need to get under the hood.
  • Community stats as of May 15, 2023: On NPM, last updated 14 days ago with 1,505,835 weekly downloads; on GitHub, last updated yesterday with 4.2k Forks and 27.9k Stars. It’s been open source with an MIT license for over 10 years, so I’m confident it’ll stay that way.

Sequelize Cons:

  • Model/schema representation can get very complex and bloated. For instance, while the Mongoose representation of our large dataset was about 262 lines (including spaces), the same dataset in Sequelize ballooned to 564 lines.
  • The syntax can be confusing and complicated in certain scenarios, which slowed me down at times.
  • Migrating or editing the database is a hassle. Even with sequelize-cli generating migration scripts, it’s still cumbersome, though I’ve noticed this is a common pain point with most ORMs.
  • Documentation isn’t great, though it’s improving. Luckily, tools like ChatGPT have a solid grasp of Sequelize due to its long history, which helped fill the gaps.
  • Not as type-sensitive as Prisma, which could lead to issues in some projects.
  • Limited TypeScript support, though this wasn’t a concern for STMS, it could be a deal breaker for others.

Prisma Pros:

  • Uses its own schema language, making model creation cleaner and more concise. For comparison, while Mongoose took 262 lines for our large dataset, Prisma managed it in just 221 lines.
  • Comes with a CLI tool that simplifies database creation and migration, which is the best I’ve seen from an ORM so far, even if it’s not perfect.
  • Supports raw SQL queries, offering flexibility when needed.
  • The code syntax is clean and simpler to understand compared to Sequelize, making it easier to learn.
  • Automatically generates query builders for Node.js and TypeScript via its client, which is a nice touch.
  • Has excellent, clean documentation. ChatGPT isn’t as up-to-date on Prisma, but the official docs often made up for it.
  • Community stats as of May 15, 2023: On NPM, last updated 6 days ago with 1,344,705 weekly downloads; on GitHub, last updated 3 hours ago with 1.1k Forks and 31.3k Stars.

Prisma Cons:

  • Doesn’t support Regex filtering for Postgres, though it offers alternatives like “contains,” “includes,” and “startsWith.”
  • Performance was a major issue in my tests. Creating entries for our large dataset took Prisma about 11.21 seconds per entry compared to Sequelize’s 2.26 seconds, roughly 5x slower.
  • Deleting a single entry from the large dataset took nearly 4 minutes, which was a deal breaker for our needs.
  • Even with a fair comparison on a complex, three-layer deep relationship dataset, Sequelize was significantly faster at deletions.
  • Prisma is backed by a startup with $56.5 million in funding. While its main ORM code is open source under Apache-2.0, I’m wary of potential licensing changes down the line, similar to what happened with MongoDB.

These detailed comparisons made it clear that Sequelize aligned better with STMS’s needs, especially on performance and long-term reliability. But I figured breaking it down like this might help others wrestling with the same choice for their projects.

The Migration Process

Data Structure Transformation

MongoDB のドキュメント構造から Postgres のリレーショナル構造へ変換するには、綿密な計画が必要でした。私は次のことを行う必要がありました:

  1. Analyze Relationships: MongoDB のドキュメントが互いにどのように関連しているかを特定し、適切な外部キー関係を設計する
  2. Normalize Data: 必要に応じてネストされたドキュメントを別々のテーブルに分割する
  3. Preserve JSON Features: 真に非構造化されたデータで柔軟性を保つ必要があるものには JSONB カラムを使用する
  4. Design Indexes: クエリ性能のために適切なインデックスを作成する

カスタムソリューション

この移行には、いくつかのカスタムソリューションの開発が必要でした:

1. データ移行スクリプト
包括的なスクリプトを作成して、次のことを行いました:

  • MongoDB コレクションからデータを抽出する
  • ドキュメント構造をリレーショナル形式に変換する
  • 適切なリレーションシップを持つ Postgres テーブルにデータをインポートする

2. API 互換レイヤー
ゼロダウンタイムを維持するために、次のことができる互換レイヤーを構築しました:

  • 移行ステータスに応じて、リクエストを MongoDB または Postgres のいずれかにルーティングする
  • 移行期間中のデータ整合性を確保する
  • フォールバックメカニズムを提供する

3. カスタムミドルウェア
MongoDB と Postgres が特定の操作を処理する方法の違いに対応するミドルウェアを開発し、既存の API エンドポイントが変更なしで動作し続けることを保証しました。

技術的課題の克服

複雑なリレーションシップの処理

最大の課題の一つは、MongoDB の埋め込みドキュメントを Postgres のリレーションシップに変換することでした。例えば、単一の MongoDB ドキュメントは次のようなものを含む可能性があります:

  • 基本的なプロパティ
  • 関連エンティティを表すネストされたオブジェクト
  • 埋め込みドキュメントの配列

これらは慎重に次のように分解する必要がありました:

  • 主要エンティティ用のプライマリテーブル
  • 多対多リレーションシップ用のジャンクションテーブル
  • 一対多関連の外部キーリレーションシップ

クエリ最適化

MongoDB のクエリパターンは SQL に直接変換できません。私は次のことを行う必要がありました:

  • 複雑な集計パイプラインを SQL の結合に書き換える
  • 新しいクエリパターンに合わせてインデックスを最適化する
  • クエリ性能が MongoDB の性能と同等またはそれ以上になるように保証する

データ整合性

移行中にデータ整合性を確保するには、次が必要でした:

  • 包括的な検証スクリプト
  • ロールバック手順
  • 移行期間中のリアルタイムデータ同期

結果とインパクト

MongoDB から Postgres への STMS 移行は、ほぼすべての機能と機能性を維持しながらゼロダウンタイムで成功裏に完了しました。結果は期待を上回りました:

パフォーマンス向上:

  • 複雑なリレーショナルクエリのクエリ性能が向上した
  • データの一貫性と整合性が向上した
  • ストレージ利用効率が向上した

運用上の利点:

  • 監視とデバッグ機能が強化された
  • eBay の既存の SQL ベースツールとの統合が向上した
  • バックアップとリカバリ手順が改善された

チームへの影響:

  • リレーショナルデータベースに関するチームの知識が向上した
  • 将来のデータベース移行のためのパターンが確立された
  • 再利用可能なツールとプロセスが作成された

獲得した技術スキル

このプロジェクトは、私の技術的専門知識を大幅に拡大しました:

データベース技術:

  • Postgres の機能と最適化に関する深い理解
  • SQL クエリの最適化とパフォーマンスチューニング
  • データベース設計パターンと正規化
  • プライマリ-スタンバイ データベース構成

開発ツール:

  • Sequelize ORM とクエリ構築
  • データベース移行戦略
  • パフォーマンステスト手法
  • データ検証と整合性チェック

アーキテクチャパターン:

  • ゼロダウンタイム移行戦略
  • API 互換レイヤー
  • データベース抽象化パターン
  • 監視とアラートシステム

個人とプロフェッショナルの成長

この移行プロジェクトは、私のキャリア開発にとって変革的でした。未踏の領域に挑戦させ、次が必要でした:

リーダーシップスキル:

  • 事前経験なしで複雑な技術プロジェクトをリードする
  • プレッシャー下で重要なアーキテクチャ決定を行う
  • 複数のチームやステークホルダーと調整する

問題解決能力:

  • 複雑な問題を管理可能なコンポーネントに分解する
  • 前例のない課題に対する創造的な解決策を開発する
  • 複数の競合する要件と制約のバランスを取る

コミュニケーションとチームワーク:

  • 技術的概念を非技術的ステークホルダーに説明する
  • 将来の参照のためにプロセスと決定を文書化する
  • 新しい技術やパターンについてチームメンバーを指導する

学んだ教訓

技術的教訓

  1. Database Selection Matters: NoSQL と SQL の選択は、特定のユースケースと長期的要件に基づくべきです
  2. Performance Testing is Critical: 理論的な利点が必ずしも実際のパフォーマンス向上に結びつくわけではありません
  3. Migration Planning: 包括的な計画とテストは、複雑な移行に不可欠です
  4. Tooling Investment: 適切なツールを最初に構築することで、時間を大幅に節約し、エラーを減らすことができます

プロジェクト管理の教訓

  1. Stakeholder Communication: 定期的な更新と明確なコミュニケーションが誤解を防ぎます
  2. Risk Management: フォールバックプランとロールバック手順を持つことが不可欠です
  3. Timeline Management: 予期せぬ課題や学習曲線のために余裕時間を確保する
  4. Documentation: 徹底した文書化が知識移転と将来の保守を可能にします

結論

STMS の MongoDB から Postgres への移行は、私のキャリアで解決した中で最も挑戦的でやりがいのある技術的問題です。技術的専門知識だけでなく、リーダーシップ、計画、適応力も必要でした。プロジェクトの成功は、適切な計画、徹底したテスト、卓越性へのコミットがあれば、最も複雑な技術的課題さえも克服できることを示しました。

この経験は、ソフトウェアエンジニアリングへのアプローチを根本的に変え、次の重要性を強調しました:

  • 技術的決定を下す前に全体のコンテキストと要件を理解すること
  • 適切なツールとテストに時間を投資すること
  • 複雑なプロジェクト全体で明確なコミュニケーションを維持すること
  • 必要に応じて新しい技術やアプローチを学ぶ意欲を持つこと

この移行の成功は、STMS の機能を向上させただけでなく、eBay のインフラプロジェクトに引き続き利益をもたらすパターンとプロセスを確立しました。未知の課題に取り組み、それを成功させることが個人とプロフェッショナルの成長の鍵であるという信念を強化しました。

振り返ってみると、このプロジェクトは私のキャリアの転機となり、ソリューションを実装する開発者から、複雑な技術的イニシアチブを設計・リードできるエンジニアへと変貌させました。この経験で得た自信とスキルは、ソフトウェアエンジニアリングにおける新たな課題や機会へのアプローチを今も導いています。