PostgREST 互換性 に関する記事では、サーバーが機能的にカバーする内容 (フィルター、埋め込み、RPC、RLS) とユーザーに委ねられる内容について詳しく説明しています。これは別のところから来たものです。 Aurabase コードと公式 PostgREST ドキュメントをソースとして、PostgREST が実際に大規模な場合にどこで、そしてなぜ頭打ちになるのかを文書化します。ここでは、私たち自身が運営していないロードバンクを再現しているわけではありません。私たちの ベンチマーク手法 は、公開されたプロトコルのない孤立した図が信頼できないように見える理由を説明しています。
必需品
- PostgREST 自体は軽量です。専用の Aurabase インスタンス上のレプリカあたり、CPU が 50 ~ 250 ミリコア、RAM が 64 ~ 128 MB です。生の HTTP スループットが運用環境の制限要因になることはほとんどありません。
- 実際の上限は、Postgres 接続予算:
PGRST_DB_POOL× レプリカです。 Aurabase コードで検証: プロジェクトごとに専用レベル (10×2) で 20 接続、共有レベル (2×2) で 4 接続。これは、同じmax_connectionsにより多くのテナントを適合させるための意図的な選択です。 Prefer: count=exactは、大きなテーブルに対して高価な MVCC スキャンを強制します。 PostgREST では、2 つのより安価な代替手段、count=plannedとcount=estimatedが文書化されており、おおよその合計コストがかかります。db-max-rowsの上限 (Aurabase ではデフォルトで 1000 行) は、応答をContent-Rangeで報告せずに切り捨てます (実際の条件で測定。詳細は以下を参照)。- DDL 移行後、PostgREST スキーマ キャッシュは非同期に再ロードされます。 Aurabase ゲートウェイは、諦めるまでに最大 8 回 (最悪の場合は累積約 3.5 秒) 再試行します。この動作はコードに直接文書化されています。
PostgREST ベンチマークが何を測定し、何を測定しないのか
PostgREST での HTTP スループット テストは主に Postgres を測定しますが、PostgREST はほとんど測定しません。サーバーは、ベースの前にある薄い翻訳層です。実際の負荷の大部分では、応答時間は SQL クエリを生成したプロセスではなく、実行された SQL クエリによって支配されます。
PostgREST プロジェクトは、GitHub 上にこのトピックの専用リポジトリ PostgREST/postgrest-benchmark を維持しており、個別のマーケティング数値を公開するのではなく、リリースごとのスループットの変動を追跡します。ここでは実行したり再公開したりしません。その結果は、ハードウェア、回路図のサイズ、およびテストされたシナリオに依存します。正確には、図を引用する前に、当社独自の ベンチマーク プロトコル が文書化する必要がある変数です。
PostgREST の下では、実際に重要なレイヤーである同時負荷時の SQL トランザクション時間を測定する pgbench です。これは、公式の PostgreSQL ベンチマーク ツール (postgresql.org/docs/current/pgbench.html、2026 年 8 月 24 日にアクセス) です。この記事では、このプロトコルをここで再現するのではなく、本番環境における PostgREST の 4 つの具体的なアーキテクチャ上の制限について文書化します。それぞれは、Aurabase ソースコードまたは公式プロジェクトドキュメントで検証されています。
Aurabase での PostgREST インスタンスの実際のフットプリント
各 Aurabase Postgres エンジンプロジェクトは、クラスターと同じ場所に配置された 2 つの専用 PostgREST レプリカを受け取ります。これらをデプロイする Kubernetes マニフェストは、控えめなリソースを設定します。
これらのレプリカが実際に消費するのは CPU ではなく、Postgres プライマリへの接続です。各 PostgREST インスタンスは、テナントにデプロイされた PgBouncer プーラーを経由せずに、プライマリ (-rw) に直接接続します。この選択については、PostgREST の互換性に関する記事ですでに詳しく説明されています。: LISTEN/NOTIFY スキーマのリロード メカニズムには永続的な接続が必要であり、トランザクション モードのプーラーとは互換性がありません。この記事で追加される内容: 接続に関して、実際のコストはいくらか、そしてそのピークはどこでしょうか。
レプリカごとのこのプールのサイズ (PGRST_DB_POOL) は、プロジェクト レベルに応じて意図的に異なります。これは、各プロジェクトの PostgREST マニフェストを構築する関数 k8s_tenant.rsで検証されています。
| ベアリング | PGRST_DB_POOL / レプリカ | レプリカ | つながり・目覚めたプロジェクト |
|---|---|---|---|
| 専用(プレミアム、A1) | 10 (PostgREST のデフォルト) | 2 | 20 |
| 共有 (フリート、無料/プロ/チーム) | 2 (Aurabase のデフォルト、低め) | 2 | 4 |
専用レベルでは制約が緩和されます。プロジェクトには独自の CNPG クラスターがあり、したがって独自の max_connectionsがあり、隣接するクラスターはありません。共有レベルでは、同じ組織の複数のプロジェクトが 1 つのクラスターを共有します。このコンテキストによって、接続の予算が決定的に決まります。これについては、次のセクションで説明します。
接続バジェットにより、同時に実行するテナントの数が決まります
共有 Postgres クラスターでは、同時にアクティブなプロジェクトの数を制限するのは HTTP スループットではありません。これは、使用可能な max_connectionsと比較した、PostgREST インスタンスがプライマリ上で開いた状態を保持する接続の数です。
Aurabase derives this budget directly from the actual cluster limits, checked in fleet.rs: (max_connections − réserve superuser/CNPG − connexions réservées au pooler) ÷ connexions par projet éveillé, floor at 1. The fixed reserve is 10 connections (superuser, CNPG instance manager, metrics exporter, provisioner admin margin). On the defects delivered (pool of 2 per replica, 2 replicas, or 4 connections per awakened project), the calculation gives three different budgets depending on the sizing level of the cluster.
ソース: fleet.rs::derive_wake_budget および wake_budget_for_org_plan、Aurabase コードから派生、2026 年 8 月 24 日に再読。
This budget is not a quota of owned projects: an team organization can hold 50 projects, most of which are dormant. This is a cap of concurrency: the number of projects that can hold open connections at the same time on the primary. A wake-up over budget does not fail, it is deferred until a sibling project goes back to sleep, checked in the same file. The topic of sizing max_connections itself is expanded upon in our article on tuning of max_connections, and the dedicated/mutualized tradeoff as a whole in dedicated vs. shared base.
推奨される理由: count=exact は、大きなテーブルに対するクエリの速度を低下させます。
正確な合計を要求すると、Postgres はクエリごとにフィルター処理された結果の表示行をカウントする必要が生じますが、そのコストはテーブルに応じて増加し、無料の操作ではありません。
PostgreSQL は、そのままではインデックス付きの行カウンターを維持しません。 MVCC では、行の可視性は、それを読み取るトランザクションによって異なります。したがって、正確な COUNT(*) は、事前計算された値を読み取るのではなく、候補行を訪問する必要があります。これは、Postgres エコシステムの構造上の制限として十分に文書化されており、ClickHouse のような分析ベンダーも含めて、自社の近似カウンターを Postgres のトランザクション動作と比較します。
PostgREST は、これら 3 つのカウント戦略をネイティブに文書化しています (postgrest.org、2026 年 8 月 24 日にアクセス)。 exact 戦略では、スキャンの価格で合計を保証します。 planned は、クエリ プランナーからほぼ無料の見積もりを返します。 estimated は、しきい値に基づいて 2 つを自動的に切り替えます。この選択は表面的なものではありません。数百万行のテーブルで count=exact を必要とするページネーションでは、ユーザーが最後のページを参照しない場合でも、各ページでこのスキャンの費用が発生します。
count=exact を指定しないと、db-max-rows の切り捨てが表示されません
正確な合計を明示的に要求しない限り、行キャップにより、本文またはヘッダーに何も示されずに PostgREST 応答が切り詰められることがあります。想定ではなく、専用の Aurabase インスタンス上で実際の条件で測定しました。
PGRST_DB_MAX_ROWS=5を含む 10 行のテスト テーブルでは、PostgREST v12.2.3 は、次の 2 つのまったく異なる状況でまったく同じ Content-Range ヘッダーをレンダリングします。
| クエリ | レンダリングされたライン | コンテンツ範囲 | メタ (オーラベース) |
|---|---|---|---|
| ?limit=50 (カウントなし) | 5/10 実質 | 0-4/* | {} |
| ?limit=50&count=exact | 5/10 実質 | 0-4/10 | {合計: 10} |
count=exactがないと、5 行の応答は、実際に 5 行しか含まれないテーブルと区別がつきません。 Content-Range: 0-4/* は、適用される制限ではなく、表示される行を記述します。この場合、実際の上限はどこにも表示されず、Aurabase SDK の PostgREST パスで直接測定されます。
PostgREST デプロイメントで db-max-rows (Aurabase のデフォルトは 1000) が設定されている場合、完全なページを検出するために data.length を要求された制限と比較するクライアントは誤ってしまう可能性があります。サーバーの上限がこの制限を下回るとすぐにエラーが表示されます。唯一信頼できる信号は、受信した行数を count=exactによって返された total と比較することです。これにより、前のセクションで説明したコストのトレードオフが直接実行されます。
移行後のスキーマ キャッシュの再ロード
PostgREST は、起動時に Postgres スキーマをメモリに保持します。 DDL (テーブルの作成、列の追加) の後、新しいルートが応答する前にこのキャッシュをリロードする必要があります。このリロードは非同期です。
このウィンドウに到着する書き込みは、テーブルが実際に Postgres 側に存在する場合でも、一時的な 404 (キャッシュがまだ最新ではない) を受け取る可能性があります。 Aurabase ゲートウェイは、 postgrest_proxy.rsで検証された制限付き再試行ループでこれを吸収します。最大 8 回の試行、増加するバックオフ (試行ごとに 250 ミリ秒と 100 ミリ秒)、最悪の場合は累積 3.5 秒です。このメカニズムは書き込みにのみ影響し、読み取りには影響しません。
ゲートウェイはリロード信号を発行せず、ただ待機するだけです。唯一の実際のトリガーは、DDL パス上のデータベース サービスによって発行される pg_notify('pgrst', 'reload schema') です。移行パスがこのシグナルの発行を忘れた場合、変更されることのないキャッシュ上で 8 回の試行が実行され、そのリスクは隠蔽されずにコード コメントにそのまま記載されています。
セルフホスト型 PostgREST デプロイメントの場合、レッスンは一般化されます。アプリケーション内の各 DDL パスは、プロセスへの NOTIFY または SIGUSR1 シグナルを介してリロードをトリガーする必要があります。それ以外の場合、移行により、展開直後に断続的なエラーに見せかけた p99 レイテンシ スパイクが発生します。
生のスループットではなく、アーキテクチャがスライスするもの
ここに記載されている 4 つの制限には 1 つの共通点があります。分離された HTTP スループット テストでは何も見られませんが、4 つすべてが PostgREST デプロイメントが運用環境に拡張されるかどうかを決定します。
- 接続バジェット: テナントあたりのスループットに関係なく、共有クラスター上で同時にアクティブになるテナントの数を制限します。
- 正確な COUNT のコスト: 負荷ではなくテーブルとともに増加します。
planned/estimatedでバイパスされます。 - サイレント切り捨て: 正しく構成された行キャップでも、不十分にインストルメントされたページングが中断される可能性があります。
- スキーマのリロード: 各移行後のレイテンシ ウィンドウ。リロード信号が適切に配線されている場合は制限され、それ以外の場合は無制限です。
セルフホスト型 PostgREST、Hasura スタイルの GraphQL レイヤー、またはカスタム API のいずれを選択する場合でも、これら 4 つの軸は、個別の req/s 数値よりも優れた比較ポイントとなります。 PostgREST vs Hasura vs カスタム APIの比較を参照してください。データベースの前に立つプーラーの選択も同様に重要です。比較 PgBouncer vs Supervisor vs PgCat では、PostgREST がトランザクション モードでプーラーを通過できない理由を詳しく説明しています。
よくある質問
覚えておくべきこと
PostgREST は、HTTP 負荷だけで壊れることはほとんどありません。そのためには、そのアーキテクチャが単純すぎます。本番環境で何が中断されるかは、本番環境の周囲にあるものです。つまり、そのレプリカが開いたままにする接続の数、正確な総コストはいくらかです。これには、切り捨てが表示されたままになるかどうか、および移行後のウィンドウの持続時間も含まれます。
これら 4 つの制限は Aurabase に固有のものではありません。セルフホスト型または管理型のすべての PostgREST デプロイメントに適用されます。このコードが示しているのは、マルチテナント展開によって、実稼働環境でそれらが驚くべきものになるのではなく、どのように明示的にされるかということです。