Neon Serverless Postgres: 便利な機能と導入パターン
この記事は、株式会社エス・エム・エス Advent Calendar 2024 シリーズ2の12/02(今年は土日はスキップする方針となったので実質的には1日目)の記事です。
最近 Serverless PostgreSQL の Neon を触っていて、色々と活用できそうだと感じているので、そのあたりをつらつら書いてみようと思います。
まだトライアルの段階なので「使った結果どうだったか」までは踏み込めてません。もし運用知見持っている方がいたら、シェアしてもらえるとありがたいです。
そもそも Neon とは?
公式ドキュメントの Why Neon? の記事には、以下のように書かれています。
- Neon is Postgres
- Neon is serverless
- Neon is fully managed
- Neon is open source
- Neon doesn't lock you in
ざっくりとしたイメージとしては、Amazon Aurora Serverless に近い位置づけのサービスです。ただ、OSS なのでセルフホストで動かすこともできます。
Neon を試そうと思った背景
自分が開発に携わっているサービスでは Amazon Aurora PostgreSQL を採用しています。AWS 使うならば Aurora 使っておいて間違いない(もちろんワークロードによる)ものの、Production 以外の環境すべてで Aurora(や非Aurora のRDS)を使うことについては、コストや利便性の面であまり適していないように感じていました。
対して Neon は、開発体験やコスト効率の向上にもフォーカスしていることを謳っているため、以前から興味を持っていました。
注目している Neon の機能その1: ブランチ
ブランチ機能は Neon の開発者エクスペリエンスについてのドキュメントでも第1に挙げられている機能です。
ドキュメントにある通り、Neon のブランチ機能では copy-on-write 技術によって軽量かつ低コストで分岐したDB環境を扱うことができます。
ブランチ機能を提供している他の DB サービスとしては PlanetScale が有名です。ただ、Neon と PlanetScale のブランチ機能には本質的な違いがあります。
PlanetScale のブランチ機能は、「DB スキーマ」を分岐させて分離した環境を提供する機能で、作成したブランチに対して加えたスキーマの変更をデプロイリクエストを使用して分岐元に安全に反映することにも使われます。この「通常のブランチ機能」(ドキュメント上の表現は "regular branching feature")における分岐の対象は「DB スキーマ」であり、DB に含まれるデータ自体は対象外となっています。
分岐元からデータも含めて分岐したい場合は、ベータで提供されているデータブランチング機能を使うことになります。ドキュメントによると、データブランチングは最新のバックアップからデータを準備するという方式になっているようで、つまりは dump & restore 方式に近い形で実現されているようです。
対して Neon のブランチ機能は、データを含む DB 全体を分岐して分離した環境を構築します。分岐は copy-on-write によってデータのコピーをせずに実現できるため、DB のサイズに関わらず数秒でブランチを作成することができるのが大きな特徴です。ただし、PlanetScale におけるデプロイリクエストなど DB スキーマの変化をサポートするような機能の提供は限定的(例えばブランチ間のスキーマ差分を見る機能などはあります)で、 軽量かつシンプルな機能となっている印象です[1]。
注目している Neon の機能その2: タイムトラベル
Neon では、履歴保持期間であれば任意の時点の DB に接続してクエリを実行することができるタイムトラベル機能が提供されています。
ドキュメント上では、ポイントインタイムリカバリならぬポイントインタイム接続("point-in-time connections")とも表現されています。
タイムトラベルの機能は一時的に特定の時点でのブランチを作成して接続を可能にする仕組みで提供されており、ブランチ機能の提供方式の1つとも捉えることができます。ただ、Neon コンソール上の SQL Editor上からも手軽に実行できるため、利便性が高いものとなっている印象です。
その他 Neon について注目していること: Postgres コアへの変更は最小限
機能ではないのですが、Neon は PostgreSQL への変更を最小限に抑えるポリシーで開発が進められています。
Neon Co-Founder による説明(CMU DB セミナー動画)によると、
- No changes to planner or executor
- Support all extensions, tools
- Support all PostgreSQL index types
- PostgreSQL handles MVCC
Replace low-level storage, close to where read() & write() happens
- Goal is to get all changes into upstream PostgreSQL
とのことです。
("Support all extensions, tools" はさすがに厳しい気もしますが...現状サポートされている extension はドキュメントにまとめられています)
GitHub リポジトリには Postgres コアの変更についての詳細がまとめられており、upstream への取り込みについても検討がされている様子が伺えます。
先日 PostgreSQL 17 がリリースされましたが、同日に Neon 上での 17 利用も可能になっていることが印象的でした。変更を最小限に抑えることで高い追従性を実現しているようです。
Neon をどう使っていくか
ここでは「こんな感じで使えるかも」と考えていることを書いていきます。
今後実際使ってみて意見が変わる可能性は大いにあります。
DB マイグレーションのチェック
DB マイグレーションの成否は含むデータによっても変わってきます。そのため、ローカル環境ではうまく実施できたマイグレーションが特定の環境では失敗する、といったことも起こりがちです。
そこで、CI/CD のパイプラインに DB のブランチを作成&マイグレーション実施検証のステップを置くことで、ぶっつけ本番の適用を回避したいと考えています。
また、マイグレーション方式として up/down のスクリプトを採用している場合は、up だけでなく down の適用検証も実施できそうです。
開発中の機能の動作確認環境や自動テスト環境の構築
オンデマンドな環境構築をするためには色々と乗り越えないといけない壁がありますが、DB 構築は壁を補強している要因の1つです。
DB ブランチを使うと DB の管理の煩雑さを低減できそうです。例えば Pull Request ごとにベースとなる DB からブランチを作成することで、手動の動作確認や自動テストを実行するなどができるかなと考えています。
不具合調査/再現
不具合発生時点での DB のブランチを作成することでデータを確認できることに加え、更新系の処理も繰り返し実行できるため調査や再現もしやすくなりそうです。
ローカル開発環境に最適
というのは、Neon のブログ記事に書かれているのですが、正直初めはピンと来ていませんでした。
Due to its unique architecture that natively decouples storage and compute, Neon is more agile, more efficient, and more developer-friendly than any other hosted Postgres – which makes it perfect for local dev and testing environments.
というのもこれまで、ローカル開発は DB もローカルで動かしてオフラインで開発が完結するのが当たり前、という認識を持っていたからです。
ただ考えてみると、GitHub や Slack が不調な日は「本日の業務はもう終わり」となるわけで、そこまでオフライン完結にこだわる必要もありません。
そこで、本番 DB に準ずるデータが入っている DB を開発メンバーでシェアしつつ、DB ブランチ上で開発を進めるといったことも試してみて、ローカル開発にマッチするかを判断したいと思っています。
特にペアプロ、モブプロを取り入れている場合はオフィスワーク・リモートワーク問わず、開発体験向上するかも、という期待を持ち始めています。
どう導入していくか
既に Aurora を使っている状況で Neon を導入する方法をいくつか挙げてみます。
Neon Twin 方式
「Neon Twin」はブログ記事に載っていた表現ですが、GitHub Action で pg_dump & pg_restore を実行して構築する Neon 上の RDS の同期コピーのことを指します。
既に動いている Aurora がある場合に最も楽に進められる方式である反面、同期のタイミングが GitHub Action 実行時(日次夜間など)になるため、発生した不具合を即時で調査するなどの用途には適していないです。
ロジカルレプリケーション方式
コピー元から準リアルタイムでデータを同期したい場合、ロジカルレプリケーションを使うこともできます。ドキュメントにも現状ベータながら Aurora からのロジカルレプリケーションの設定方法が紹介されています。
ただし、ロジカルレプリケーションは DB スキーマや DDL コマンドなどはレプリケートされないなどの制限があるため、DB スキーマの反映の仕組みを別途用意する必要があります。
完全移行
リアルタイムの同期をしたいがロジカルレプリケーションの管理は面倒、という場合はもう完全に Neon に移行してしまった方が良い気がします。
どの方式を採用すべきか
Production では Aurora を利用する前提を置く場合、Production に対しては Neon Twin を作り、その他の環境は Neon のみを利用する、というのが1つの手だと思います。
ただ、Aurora を Production だけで使う場合、Aurora の運用知見を得るチャンスが減り、Aurora のみで起こる問題の発覚タイミングが遅れるなどのデメリットもあるため、仮に Neon の使用感が良かったとしても常にそうすべきとは言い切れません...。
結局どう導入するのか
ということで、まずは開発環境の DB の Neon Twin を導入維持しつつ開発プロセスへの組み込みトライアルを進めていこうと考えています。
(知見を得たらまた記事に書きたい)
最後に
2024年12月現在 マネージド Neon が利用可能な AWS のリージョンは限定的で、例えば東京は含まれていません。また Azure は Virginia (ベータ提供)のみ、GCP は利用可能リージョンなし、という状況です。
以下のページからプロバイダー/リージョンの追加をリクエストできますので、興味がある方はリクエストしましょう。
(自分は AWS 東京リージョンをリクエスト済)
補足: Neon のブランチ機能の内部
Neon を特徴づけている DB ブランチ機能ですが、どう実現されているかの資料も公開されているので、 補足としてそれらの資料をいくつか挙げます。
Neon ブログ: Architecture decisions in Neon
Aurora のような「ストレージとコンピューティングの分離」をしたアーキテクチャを採用し、ストレージレイヤーをどう構築しているかを紹介した記事です。
Neon ではストレージレイヤーを更に WAL サービスとページサーバーに分離しています。WAL サービスはその名の通りWALを永続化し、ページサーバーは WAL サービスから WAL を受け取って Replay して ページ化しつつコンピューティングノードからのページ取得リクエストに応答します。
ページサーバーはページをローカルストレージの不変ファイルに書き出し、さらに適宜 S3 などのコールドストレージに移すことで、ストレージコスト効率と堅牢性を高めています。
Neon ブログ: Deep dive into Neon storage engine
ページサーバーの内部を解説した記事です。
ページを LSM-tree に似たデータ構造で管理しつつ、PostgreSQL のコンピューティングノードからのページ取得リクエストを処理します。ページ取得リクエストにはログシーケンス番号(LSN)という WAL に振られた番号が含まれており、ページサーバーは LSN に対応する履歴のページを返します。
運悪くページサーバーの LSM-tree ライクなデータ構造上に指定された LSN のページの履歴がない場合でも、近い履歴のページを特定し必要な WAL を適用して目的のページ履歴を返すことができます。
ブランチ作成時の copy-on-write やタイムトラベルの機能も、この LSN 指定でページ履歴を取得できる仕組みによって実現されています。
PostgreSQL Conference Japan 2023: What’s Neon?
NTT OSSセンタの方の PostgreSQL Conference Japan 2023 での講演スライドです。
Neon のアーキテクチャや内部の仕組みなどについて、分かりやすくまとめられています。また、Neon 公式で用意してくれている docker compose での動かし方も紹介されています。
K8s デプロイ用の非公式 Helm Chart にも触れられていますが、現在ベータながら公式のものも提供されているので、自前デプロイする場合はこちらを使う方が良さそうです。
補足: MySQL 版の Neon 的位置づけのものはあるのか
注: ざっと調べただけで書いてます。他にもあったら教えて下さい。[2][3]
まず記事で触れた PlanetScaleは MySQL とワイヤプロトコル互換でスキーマブランチ機能がありますが、シャーディングソリューションを提供するVitessベースであるため Neon とは異なる立ち位置だと認識しています[4]。
他に強いて挙げるとすると MySQL ではなく MariaDB ベースですが SkySQL あたりかなと思います。ただ、月額$100〜(Freeトライアルはあり)でブランチ機能は無いため、Serverless を謳っている DBaaS であるということくらいしか共通点はありません。
と思っていたんですが、ちょうどこの記事を書いている時に WeSQLというプロジェクトを知りました[5]。
- MySQL のサーバーレイヤーをほぼ変更せず利用
- ストレージレイヤーを InnoDB から LSM-tree ベースの独自のストレージエンジンに置き換え
- 永続化先として S3 も活用
- MySQL へのパッチは最小限に留め、追従性を高く保つ
という Neon と似たアーキテクチャ & ポリシーで開発が進められているようです。
そして、気になるブランチ機能もあります。ただ、2024年12月現在時点のブランチ機能のドキュメントを見る限り、Neon のような copy-on-write ではなく、copy によって別 DB を作る方式のようです。
- Prepare: Initializes the branch workflow by copying the database schema from the source to the target database.
- Start: Begins data streaming from the source to the target database.
- Stop: Halts the data streaming process.
- SchemaDiff: Displays differences between the source and target database schemas.
- PrepareMergeBack: Prepares to merge schema changes from the target back to the source database.
- StartMergeBack: Executes the merge of schema changes.
- Cleanup: Cleans up the branch workflow
スキーマのマージも用意されており PlanetScale の機能に近い印象です。
加えて、開発ロードマップには "Instant Branch" という機能も記載されており、説明を見ると こちらはまさに Neon のブランチ機能と言えそうです。
A new branch is cloned instantly from shared S3 data, with subsequent writes diverging via Copy-On-Write.
まだマネージドサービスは無く今後どう展開していくのかは読めませんが、MySQL での代替ソリューションとして興味深い存在です。
-
シンプルと言っても、ブランチ機能を活用するとスキーマの変化をサポートするプロセスも組みやすそうです。Neon Twin: How to deploy a change tested in Neon to prod in RDSというブログ記事では、Neon を活用して Production 環境の RDS にスキーマ差分を適用するワークフローが紹介されています。またDatabase Branching Workflowsというブランチ活用ガイド記事もあります。 ↩︎
-
あえて書いていないサービスとして Doltがあります。こちらはGit for Dataを謳っており、そのためにかなり独特なアーキテクチャとなっているため、ここでは除外しました。 ↩︎
-
MySQLワイヤプロトコル互換のDBについてはNavigating the MySQL Wire-Compatible Database Landscape — Overviewの記事も参考になります。 ↩︎
-
Hobby プランがあった時期はワークロード問わず使われていたようですが、2024年4月に廃止され、違いはより明確になっています。 ↩︎
-
ちなみに WeSQL の Founder は Wei Cao さんという方で、命名についても MySQL の伝統(参考1: MySQL、参考2: MariaDB)を受け継いでいるのかもしれません(?)。 ↩︎
Discussion