世界を動かす技術を、日本語で。

Show HN: PgDog – アプリを変更せずにPostgresをスケールする

概要

PgDog はPostgreSQL向けの プロキシ であり、 接続プーリング・ロードバランシング・シャーディング を一括提供 Rust製 のため、高速・安全・大量接続処理が可能 Kubernetes・AWS・Docker など多様な環境に対応 クロスシャードクエリ・2フェーズコミット・再シャーディング など高度な機能を搭載 アプリ側の修正やDB移行不要、 既存システムに容易導入 できる点が特徴

PgDogとは何か

  • PostgreSQL用のネットワークプロキシ であり、 接続プーリング・ロードバランサ・データベースシャーディング を一体化
  • Rust で開発されており、 高速性・堅牢性・セキュリティ を両立
  • 数千接続 を一般的なサーバで安定処理
  • アプリケーション側のコード変更やDBマイグレーション不要
  • OSSとして公開、 ドキュメント・Discordコミュニティ も充実

導入とクイックスタート

  • Kubernetes:
    • Helm Chart で簡単デプロイ
    • コマンド例:
      • helm repo add pgdogdev https://helm.pgdog.dev
      • helm install pgdog pgdogdev/pgdog
  • AWS:
    • EKSや自己管理Kubernetes でHelm利用
    • Terraformモジュール でECSデプロイ対応
  • Docker:
    • Docker Compose でローカル試用可能
    • docker-compose up で起動
    • psql等のPostgreSQLクライアントで接続
  • デモ環境:
    • 3シャード・2シャードテーブルを標準搭載
    • INSERT/SELECT例付き

設定と構成ファイル

  • 設定ファイル2種:
    • pgdog.toml(ホスト・シャーディング等の設定)
    • users.toml(ユーザー名・パスワード管理)
  • 最小構成例:
    • pgdog.tomlでDB・ホスト・ポート指定
    • users.tomlでユーザー・DB・パスワード指定
  • DBとユーザーの紐付け必須
  • ローカル試用時:
    • CREATE DATABASE/USERコマンド例あり

接続プーリング

  • トランザクション・セッションプーリング に対応
  • PgBouncer互換 でありつつ、 SET文や起動オプションも正しく管理
  • 自動トランザクションロールバック・接続再同期 など高度な回復機能
  • アプリクラッシュ時の接続ストーム対策 として有効

ロードバランサ

  • アプリ層(OSI L7)ロードバランサ
  • Postgresプロトコルを理解 し、プライマリ・レプリカ間でトランザクションを均等分散
  • 戦略3種:
    • ラウンドロビン
    • ランダム
    • 最小アクティブ接続
  • 複数ホスト設定時に自動有効化

ヘルスチェック

  • リアルタイムでDBホストの健全性監視
  • 障害発生時は自動でルーティング変更
  • ネットワーク障害・一時的なHW障害・設定ミスにも対応
  • DBの可用性最大化

シングルエンドポイント

  • pg_query(PostgreSQLネイティブパーサ)搭載 でクエリを解析
  • 書き込みはプライマリ、読み込みはレプリカ へ自動振り分け
  • アプリは同じPgDogエンドポイントに接続するだけでOK

トランザクションとロードバランス

  • 複数文を含むトランザクションはプライマリにルーティング
  • クライアントが明示的にREAD ONLY指定可能
    • BEGIN READ ONLY; でレプリカに送信

フェイルオーバー

  • Postgresレプリケーション状態を監視し、自動で書き込み先切替
  • Patroni等のフェイルオーバーオーケストレーションツールと併用可能
  • lsn_check_delay等の設定で有効化

シャーディング

  • 複数シャードDBの管理が可能
  • PostgreSQLパーサを用いシャーディングキー抽出・最適ルーティング
  • クロスシャードクエリ時は結果をメモリ上で統合・変換
  • シャーディング設定例:
    • shard属性でホストごとにシャード番号指定
  • 最低1つのシャードテーブルが必要

シャーディングアルゴリズム

  • PostgreSQLパーティション関数(HASH, LIST, RANGE)またはスキーマ利用
  • パーティションベースシャーディング:
    • Postgresソースコード準拠
    • postgres_fdw等とも連携可能
  • スキーマベースシャーディング:
    • スキーマ単位でシャード割当
    • search_pathでルーティング制御可能

直接シャードクエリ・クロスシャードクエリ

  • シャードキー指定時は単一DBにルーティング
  • クロスシャード時は全DBに送信し、結果を統合
  • サポート状況:
    • 集約関数(count, min, max, avg, stddev, variance)部分対応
    • ORDER BY, GROUP BY, マルチタプルINSERT, シャードキーUPDATE対応
    • サブクエリ・CTEは全シャードで同一実行
  • COPYコマンドもシャード分割対応

一意ID生成・シャードキー更新

  • UUID(v4/v7)や独自の一意BIGINT生成関数(pgdog.unique_id())を提供
  • シーケンス不要で数百万ID/秒生成可能
  • シャードキー更新時は自動でSELECT→INSERT→DELETEを実行
  • マルチタプルINSERTも自動シャード分割

再シャーディング・データ同期

  • PostgreSQL論理レプリケーションプロトコルを利用
  • バックグラウンドでデータ分割・再配置を無停止で実施可能
  • 既存DBのシャーディングやシャード追加も容易

運用・特徴

  • クロスシャード書き込みも2フェーズコミットで原子性確保
  • 全シャード共通の複製テーブル(オムニシャーディング)もサポート
  • マルチタプルINSERT・シャードキー変更・一意ID生成もORM互換
  • フェイルオーバー時の自動書き込み先切替や、接続プール自動復旧
  • 全機能は設定でON/OFF可能・段階的導入支援
  • OSSとして開発が進行中・フィードバック歓迎

他製品との比較・差別化

  • Citus等のPostgres拡張と比較し、アプリ・DBへの侵襲性が極小
  • HAProxy等のロードバランサ不要、PgDog単体で一元管理可能
  • ActiveRecordやPrisma等主要ORMとシームレス連携
  • マルチスレッドアプリでの接続プール統合によるリソース削減
  • パフォーマンス・導入容易性・柔軟性を両立

まとめ・導入検討ポイント

  • 高トラフィックアプリケーションのDBボトルネック解消
  • 既存システムへの非侵襲的導入
  • スケーラビリティ・可用性・運用効率化を実現
  • 公式ドキュメント・OSSコミュニティも充実
  • 柔軟な設定で段階的な導入・検証が可能

Hackerたちの意見

pgdogのユーザーとして、これを使ってる人にはおすすめできるよ。コネクションプーラーとしての視点からね。今、テストもやっててシャーディングにも期待してるけど、まだ本番環境では使ってないから、100%は保証できないかな。でも、そういう方向に進んでるよ。@Lev、2pcの進捗はどう?前に見たときは結構新しかったけど、それ以来あんまり見てないんだ。今は結構安定してる?

今は良くなってきたけど、まだクラッシュ保護を追加する必要があるね。PgDog自体がクラッシュした場合に、進行中の2pcトランザクションの記録を耐久性のあるメディアから復元しないといけないから。これをすぐに追加する予定だよ。

トラフィックの多いアプリを作ると、最初に壊れるのはデータベースだって知ってるよね。ちょっと興味があるんだけど、PgDogを使いたいと思っている高トラフィックのアプリってどんなのが多いの?ホームページにCoinbaseやRampのロゴがあるけど、フィンテックが合ってる感じ?

いろんな種類があるよ、特定のセクターに限らないんだ。それがPostgresを使う魅力の一つだね!みんな何らかの形で使ってるから。一般的なアドバイスとしては、データベースの接続が100を超えたら、コネクションプーラーを追加することを考えた方がいいよ。もし主要な負荷が30%(CPU使用率)を超えるなら、リードレプリカを追加することも検討してみて。これって、データベース間でのワークロードの分離をしたい場合にも当てはまるよ。例えば、遅い/高コストの分析クエリをレプリカに押し込むとかね。プライマリを縦にスケールするのも良い選択だけど、その縦の限界は意識しておいて。クラウドプロバイダーの最大のマシンから数インスタンスタイプ離れたら、シャーディングを考え始めて。

ちょっと馬鹿な質問だけど、これってデータベースもシャーディングするの?それとも手動でシャーディングしてから設定する感じ?

データベースもシャーディングするよ。スキーマの同期、テーブルデータの移動(並行して)、論理レプリケーションの設定、アプリケーショントラフィックの切り替えを全部やるから。ゼロダウンタイムのリシャーディングは現在進行中で、今まさにPRを進めてるところだよ: https://github.com/pgdogdev/pgdog/pull/784。

すごい進展だね、みんな!いろんな機能が増えてるのが印象的だよ。もっと多様なタイプ、集約関数、ノード間DML、リシャーディング、信頼性重視のコネクションプーリングなど、すごくクールだね!これらは本当に難しい問題で、Citusで作るのに何年もかかったから、出荷のスピードには拍手を送りたい。

一部のHTTPプロキシはリトライができるよね。もし一つのバックエンドへの接続が失敗したら、別のバックエンドでリトライする。PgDog(またはPgBouncer、他のツールでも)も似たようなことができるのかな?「データベースサーバーがシャットダウン中」エラーや接続リセットがあった場合、別のバックエンドでリトライできる?

現在はまだだけど、追加できるよ。一つ気をつけなきゃいけないのは、トランザクション内で実行中のリクエストを再試行しないこと。それ以外は、これは素晴らしい機能になると思う。

「レプリケーション」って言葉が何度も出てくるね。これってpgdogが管理してるの?他の論理レプリケーションの設定をpgdogに置き換えて、高可用性クラスターを作ることはできる?やり方についての説明はある?

君のユースケースについてもう少し情報が必要だね。私たちは論理レプリケーションを使ってシャード間でデータを移動させて、新しいシャードを作るつもりなんだ。これもPgDogが管理してるよ。ここではたくさんのツールを作っていて、その多くは設定可能で別々に使えるんだ。例えば、データベースのシャード状態に関係なく、データベース間でレプリケーションストリームを設定するためのCLIや管理データベースコマンドがあるから、テーブルやデータベース全体を新しいハードウェアに移動させるためにも使えるよ。ストリームを動かしておけば、最新の論理レプリカを維持できる。今のところ、論理レプリケーションされたデータベースのDDLレプリケーション(CREATE/ALTER/DROP)は管理してないけど、これは知られている制限で、すぐに対処する予定だよ。結局、リシャーディング中にスキーマの移行を一時停止させたくないからね。この部分が整えば、ほぼどんな目的でも長期間の論理レプリカを運用できるようになると思うよ、高可用性も含めて。

これ、すごくいいね!いくつか質問があるんだけど、1) プレーンなPostgresから始めて、将来的にシャーディングが必要になったときにpgdogを追加することは、予定されたダウンタイムなしで可能なの?2) 物理的なマルチテナンシーを使っているとき、スキーマの更新はどう処理されるの?pgdogは知っているすべてのデータベースをループして、各データベースにスキーマ更新コマンドを発行するの?

  1. うん、オンラインリシャーディングをサポートしてるから、必要になるまでこれをデプロイする必要はないよ。2. その通り、DDLを設定されているすべてのシャードにブロードキャストするよ。もし二相コミットが有効になっていれば、この操作がアトミックであることが強く保証される。ブロードキャストは並行して行われるから、速いよ。

進展おめでとう!PgDocが現在適切に処理できないクエリを受け取った場合、どんな動作をするの?自分のクエリがうまくいくか評価するためのリンターや静的解析ツールはある?

現在の挙動は、残念ながらそのまま通して間違った結果を返すことなんだよね。ここでさらにチェックを追加してて、アプリを本番環境にリリースする前に、早期採用者にしっかりしたテストスイートを持ってもらうことに頼ってるんだ。とはいえ、これがあるよ:[general] expanded_explain = true これを使うと、EXPLAINクエリの出力がPgDogによって行われたルーティング決定を返すように変更されるんだ。もしクエリが「direct-to-shard」、つまり一つのシャードにだけ行く場合は、期待通りに動くって確信できるよ。このクエリは一つのデータベースとだけやり取りするから、結果を操作したり、複数のシャードから結果を組み立てたりする必要がないんだ。クロスシャードクエリについては、今のところ自分たちで統合テストを用意する必要があるね。ここにもすぐにチェックを追加する予定だよ。CIスイートも結構整ってるけど、全てをカバーしてるわけじゃないんだ。コードのその部分を見るたびに、最近のLIMIT x OFFSET yのサポートみたいに、どんどん機能を追加しちゃうんだよね(PgDogがLIMIT x + yに書き換えて、オフセット計算をメモリ内で適用する)。いつかはうまくいくさ。

Postgresをシャーディング可能にするための課題について、もう少し詳しく教えてもらえる?Postgresにネイティブでシャーディングを追加するのは大変だったのを覚えてる。いくつかの会社が独自のソリューションを持っていたよね。君たちが達成したことは、まさに奇跡だよ。

たくさんあるから、どこから始めようかな。1. 人々はシャーディングされることを考えてスキーマを設計しないけど、多くは共通のキー(例:user_idやcountry_id、tenant_id、customer_idなど)に引き寄せられる。それが起こると、シャーディングが簡単になる。2. Postgresは、シャーディングされたときに維持するのが難しい多くの保証を提供している:アトミックな変更、参照整合性、チェック制約、一意のインデックス(および制約)など。これらはシャーディングレイヤー(PgDogのような)によって別々に構築される必要があり、通常はパフォーマンスに関するトレードオフがある。グローバルに強制される制約をチェックするのは、ローカルのものよりもずっと高くつく(ネットワークのホップは無料じゃないからね)。3. 非シャーディングからシャーディングへのオンライン移行は難しいことがある:DBが書き込みを続ける中で、テラバイトのデータを再分配しなきゃいけない。1行も失うことはできない - Postgresは記録のストアとして使われていて、これはビジネスに影響を与える深刻な問題になる可能性がある。私たちはこの課題に対してますます大きな一歩を踏み出している。基本的なクエリルーティングから始めて、今はクエリの書き換えも行っている。以前はデータの移動を扱っていなかったけど、今はほぼ完全に自動化されたリシャーディングができるようになった。時間と労力が必要で、何よりも意欲的で勇気のある初期の利用者たちには大きな感謝の気持ちを持っているよ。

いくつか思いつく選択肢があるよ:1. シャードを一つの大きなデータベースにレプリケートして使う。レプリケーションは個別のステートメントより安上がりだから、しばらくはこれでいけるかも。シンクはPostgresやClickhouseみたいな別のデータベースでもいいよ。Instacartでは、社内のCDCパイプラインを使ってSnowflakeを利用してた。うまくいったけど、Snowflakeはオフライン分析、つまりBIやバッチMLにしか使えなくて、かなり高かったんだ。最終的にはこれに対するサポートも追加する予定だよ;論理レプリケーション、DDL変更を管理するのがかなり上手くなってきたからね。2. シャード自体を使って、その上にちゃんとしたクエリエンジンを構築する。これがCitusのやり方で、可能だってわかってる。いくつかのクエリは高コストになるかもしれないけど、それは予想されることで、計算リソースを増やすことで解決できる。私たちのアーキテクチャでは、メンテナンスのためにシャードがダウンするのはインシデントレベルのイベントだから、常に稼働していることを期待してるし、問題があればスタンバイにフェイルオーバーする。最近は、ほとんどのメンテナンスタスクはオンラインでそのまま行えるか、ブルー/グリーンでできるから、これもサポートする予定だよ。ゼロダウンタイムが重要なんだ。