この記事では、言語、プーリング モード、シングル テナント モデルまたはマルチテナント モデル、純粋なプーリングを超えた機能という 3 つのプーラーのアーキテクチャを比較します。 Tembo と PkgPulse はこれら 3 つのツールの数値比較を公開していますが、私たち自身ではそれらの測定値を再現していません。 ベンチマーク方法論で詳述されているベンチマークに関する当社の編集上の立場は、当社自身が検証していない数値を決して再公開しないことです。代わりにここでは、各ツールの実際のアーキテクチャと、Aurabase が実際に Postgres トラフィックをルーティングする方法を、ソースコードのセクションごとに検証して説明します。
- PgBouncer (C) は依然として最も実績のあるプーラーであり、Kubernetes との統合に最適です。CloudNativePG は
Poolerリソースを PgBouncer (C) に直接依存しています。 - Supavisor (Elixir、Supabase プロジェクト) は、データベースごとに 1 つのプーラーではなく、同じサービスから数千のデータベースを提供するという別の問題をターゲットにしています。
- PgCat (Rust) は、アプリケーションのシャーディング、レプリカ間の負荷分散、および RAW プーリングへの自動フェイルオーバーを追加します。
- Aurabase リポジトリには、共有フリートの共有デプロイメントと、専用テナントごとに CloudNativePG によって管理される
Poolerリソースの 2 つのレベルで使用される PgBouncer が表示されます。どちらもトランザクション モードで動作します。 - PostgREST と
aura-db管理プールは、プーラーを経由せずに、自主的に Postgres に直接接続されたままになります。トランザクション プーリングにより、スキーマの再ロードとセッション ロックが解除されます。
3 つのプーラー、3 つの哲学
PgBouncer は最小化、Supervisor はマルチテナント規模でプールし、PgCat は RAW プーリングにネットワーク機能を追加します。 3 つはいずれも他の 2 つの直接の代替品ではありませんが、同じページで用語ごとに比較されることがよくあります。
| 言語 | C | エリクサー(ビーム) | さび |
|---|---|---|---|
| プーリングモード | セッション、トランザクション、ステートメント | セッション、トランザクション | セッション、トランザクション、ステートメント |
| テナントモデル | インスタンスごとに 1 つのターゲット クラスター、シングル テナントになるように設計 | ネイティブ マルチテナント: 多くのデータベース用のサービス | ターゲットクラスター、パーティションキーによるシャーディング |
| プーリングを超えて | 追加機能はなく、意図的に最小限に抑えられています | 管理HTTP API、動的テナント登録 | レプリカ間のシャーディング、負荷分散、フェイルオーバー |
| ネイティブ Kubernetes の統合 | はい: CloudNativePG リソース プーラー | 現在までネイティブに文書化されていない | 現在までネイティブに文書化されていない |
| 起源 | Postgres プーリングの歴史的な標準 | Supabase が独自のマルチテナント クラウド用に構築 | Instacart で誕生し、現在は PostgresML によって維持されています |
列は順に、PgBouncer、Supervisor、PgCat です。各プロジェクトの公式ファイリングによるアーキテクチャの特徴は、展開しているバージョンで確認する必要があり、この点でエコシステムは急速に進化しています。
軽量で Kubernetes に統合された歴史的な標準
PgBouncer は Postgres 接続をプールするという 1 つのことだけを行い、追加の機能はありません。この意図的に狭い範囲が、その寿命の長さと、本番環境のほとんどの Postgres スタックの基本構成要素としての採用の主な説明になります。
Three pooling modes are available. Session mode opens one server connection per client connection, the most permissive. Transaction mode reuses a server connection between several clients, released at each end of a transaction. Statement mode goes even further, and is rarely used in production. It is the transaction mode that brings the real pooling gain, but it imposes strict rules. Any session state (SETvariables, advisory locks, LISTEN/NOTIFY) does not survive beyond a transaction. We detail these rules and their pitfalls in our dedicated article on the transaction pooling mode of PgBouncer.
歴史的に単一プロセスである PgBouncer インスタンスは、デフォルトで単一の CPU コアを使用します。同じポートの背後で複数のインスタンスを実行する ( SO_REUSEPORT経由) は、プロジェクトの最近の進化であり、初期の設計機能ではありません。認証側では、PgBouncer は構成可能な auth_queryをサポートしています。これは、ロールのパスワードを動的に解決するために各接続で実行される SQL 関数です。このメカニズムにより、各ユーザーを事前にリストした静的ファイルに依存することがなくなります。 Aurabase がプロジェクトごとの役割に使用するのは、まさにこのメカニズムです (セクション 05)。
PgBouncer は、CloudNativePG が Poolerリソースの背後にネイティブにデプロイするプーラーです。 CloudNativePG オペレーターによって管理される Postgres クラスターでは、管理対象プーラーをアクティブ化すると、実際には、手動で構成せずに PgBouncer をアクティブ化することになります。
Supabase のクラウドネイティブ マルチテナント プーラー
Supervisor は、PgBouncer がこの規模で解決するように設計されていなかった問題に対処します。これには、データベースごとに 1 つのプーラー インスタンスではなく、単一のサービスから非常に多数の異なるテナント データベースを提供することが含まれます。 Elixir で書かれ、Erlang 仮想マシン (BEAM) 上で実行されるこのプロジェクトは、Supabase によってオープン ソースで独自の GitHub リポジトリ上で開発および保守されています。
ネイティブ マルチテナント モデルが実際の構造的な違いです。従来の PgBouncer フリートではターゲット ベースごとに 1 つのプロセス (または一連の専用接続) が必要ですが、Supervisor の動作は異なります。 HTTP 管理インターフェイスを介してテナントを動的に登録し、サービスを再起動せずに各受信接続を正しいデータベースにルーティングします。 Supabase は、まさにこの理由から、独自のクラウド プロジェクトを PgBouncer から Supavisor に移行しました。従来のプーリング クラスター (データベースごとに 1 つ) は、数十万のプロジェクトをホストするマルチテナント クラウドには拡張できません。
このアーキテクチャ上の選択には、文書化された欠点があります。高度なケースにおける PgBouncer との機能同等性は、プロジェクトの開始後に安定するまでに時間がかかりました。 2 つの例: LISTEN/NOTIFYの特定の動作、およびトランザクション モードでのプリペアド ステートメントの詳細な管理。アプリケーションがこれらの特定の動作に依存している場合は、移行前にバージョンを確認してください。
Rust の部外者: ネイティブ シャーディングとロード バランシング
PgCat は、Rust で書かれた PgBouncer の代替として明示的に位置付けられています。 PgBouncer も Supervisor もネイティブに埋め込まれていないネットワーク機能をクラシック プーリングに追加します。特に、パーティション キーによるアプリケーション シャーディング、リード レプリカ間の負荷分散、障害が発生したレプリカからの自動フェイルオーバーの 3 つです。このプロジェクトは Instacart で誕生し、現在では PostgresML によって引き継がれ、維持されています。
具体的には、PgCat は、複数の Postgres インスタンス間のルーティングのための接続プーラーとアプリケーション プロキシという 2 つの異なる層が通常占める役割を果たします。すでに手動でデータをシャーディングしていたチームは、PgCat を使用してコードを簡素化できます。内部で開発されたレプリカ間で読み取りを分散するロジックについても同様です。専用のネットワーク層がそれを直接置き換えます。
逆の妥協案も存在します。PgCat は若いプロジェクトであり、ドキュメントと制作フィードバックのエコシステムが PgBouncer よりもはるかに小さいです。シャーディング機能とフェイルオーバー機能を採用するということは、プーリング容量だけでなく、この特定のコンポーネントの成熟度に依存することに同意することも意味します。
Aurabase コードが示すもの: トランザクション プーリングがすべてを壊す場合を除いて、どこでも PgBouncer
Aurabase リポジトリは、PgBouncer を 2 つの別々の層に、両方ともトランザクション モードでデプロイします。共有フリートの場合、Helm チャートは共有データ プレーンの前に専用の PgBouncer デプロイメントを定義します (deploy/helm/aurabase/templates/infra/pgbouncer.yaml、イメージ edoburu/pgbouncer)。専用インスタンス上のテナントの場合、プロビジョナーは CloudNativePG によってネイティブに管理されるリソース Pooler (deploy/cnpg/tenant-pooler.yaml、 k8s_tenant.rsによってレンダリングされます) を生成します。どちらも Supervisor または PgCat を使用しません。このコードには、この選択に先立つ明示的な比較が記載されていません。一方で、PgBouncer がネイティブ プーリング ブリックであるという事実と一致して、CloudNativePG エコシステムとの緊密な既に運用可能な統合が示されています。
ただし、すべてがプーラーを通過するわけではなく、これはコード自体に文書化された意図的な選択です。 PostgREST は PgBouncer 経由ではなく、Postgres にライブ接続されたままになります。 Helm チャートのコメントでは、その理由が明示的に示されています。トランザクション プーリングにより、pgrstチャネルの LISTEN に依存するスキーマのリロードが中断されるからです。このメカニズムは、クライアント間のリサイクルされたサーバー接続と互換性がありません。aura-db 管理プール (スキーマ、DDL、セッション勧告ロック) も、同じ基本的な理由で、直接接続されたままになります。非トランザクション スコープの SET search_path およびセッション ロックは、トランザクション モード プーラーでは存続しません。
認証は、セクション 02: AUTH_QUERY: SELECT * FROM pgbouncer.user_lookup($1)で説明されている auth_query パターンに従い、静的な userlist.txt ファイルは使用されません。これにより、プロジェクトごとに動的に作成されたロール (project_<uuid>_authenticator) が、新しいプロジェクトごとにプーラーを再デプロイすることなく PgBouncer 経由で認証できるようになります。
ローカル Kubernetes マニフェスト内の PgBouncer healthcheck コメントには、すでに修正されている実際のバグが記載されています。 PgBouncer に対して実行される pg_isready はプロキシ ハンドシェイクのみを検証し、中継する Postgres バックエンドへの実際の接続は検証しません。 PgBouncer は、バックエンドが停止している場合でも「接続の受け入れ」に応答し、リクエストをキューに入れます。破壊的テスト中に観察された結果: Postgres に到達できない間、サービスは 5 サイクル連続して healthy のままでした。この修正により、チェックが、プーラーを介してバックエンドに至る真のエンドツーエンドの psql リクエストに置き換えられます。同じテストを再実行した修正後の結果: unhealthy が 7 サイクル、約 35 秒で検出されました。
最後に細かいことですが、明らかになります。Helm チャートはデフォルトで edoburu/pgbouncer:v1.24.1-p1 を固定しますが、ローカル k3d ベンチは v1.25.2-p0を使用します。これはアーキテクチャ上の選択ではなく、2 つの環境間でのバージョン同期がわずかに欠如しているだけです。このような詳細は、コード レビューの方がブログ投稿よりも早く把握できます。飾り立てるのではなく、ありのままを記録します。このプーラーが提供するスキーマ分割の詳細については、マルチテナント RLS 分離に関する記事を参照してください。
3つの中から選ぶ方法
次の場合は PgBouncer を選択してください…
- 一般的に CloudNativePG または Kubernetes によって管理される Postgres クラスター
- 最も実績があり、十分に文書化されたプーラーが必要です
- プーラー インスタンスごとのターゲット ベースは最適です
次の場合はスーパーバイザーを選択してください…
- 同じサービスの背後にある数百または数千の基地
- 再デプロイせずに API 経由でテナントを動的に登録する必要がある
- すでに Supabase エコシステムに参加している、またはそれに依存する意思がある
次の場合は PgCat を選択してください…
- アプリケーション共有はすでに導入されているか、プーラー レベルで計画されています
- 別個のアプリケーション層を必要としない負荷分散およびフェイルオーバーレプリカ
- PgBouncer ほどドキュメントが少なく、若いプロジェクトに慣れている
どのプーラーを選択しても、Postgres 自体のサイジングが置き換えられるわけではありません。プール サイズとサーバー max_connections は、順番に考えるのではなく、一緒に考える必要があります。低すぎる max_connections の前に十分なプールがあると、彩度が 1 つのレベルから別のレベルに移動するだけです。 の max_connections の調整に関するガイド では、プールのサイズを設定する前に適用するサイジング公式について詳しく説明しています。
最もよく聞かれること
万能のプーラーはなく、テナントに適したプーラーのみが存在します
PgBouncer、Supavisor、および PgCat は、同じツールの 3 つのバージョンではなく、同じ問題の 3 つのバリエーションを解決します。プラットフォームがすでに Kubernetes と CloudNativePG に依存している場合、または単に最も文書化されたプーラーが必要な場合は、PgBouncer が最も安全な選択肢となります。スーパーバイザーは、同じサービスからサービスを提供する一定数のベースを超えて関連するようになります。ネットワーク レベルでのシャーディングとレプリカ フェイルオーバーを見逃している場合は、若いプロジェクトの成熟度を受け入れるのであれば、PgCat は回り道をする価値があります。
Aurabase コードは、中立ではなく一貫した選択肢を示しています。トランザクション モードの PgBouncer は、共有フリートと専用テナントごとの CNPG プーラーの 2 つのレベルです。 PostgREST とスキーマ管理の 2 つの例外が文書化されています。このプロジェクトのサイロ化を紙の上ではなく実際に確認したい場合は、パフォーマンス ページに関連する測定方法が記載されています。