PROD欧州主権の BaaS プラットフォームダッシュボードを開く →

パフォーマンス · 11 分読み取り

PostgREST: 本番環境におけるベンチマークと実際の制限

Affane Daylami · Fondateur · 2026年5月18日

ブログに戻る

PostgREST 自体がボトルネックになることはほとんどありません。専用の Aurabase インスタンスでは、レプリカは 50 ~ 250 ミリコアの CPU と 64 ~ 128 MB の RAM で実行されます。これは、HTTP リクエストを SQL に変換する軽量の Haskell バイナリです。本番環境で現れる実際の制限は別の場所にあります。そのうちの 4 つが最も頻繁に登場します。レプリカが消費する Postgres 接続の予算と、MVCC での正確な COUNT のコストです。各スキーマ移行後のレイテンシ ウィンドウと同様に、応答の切り捨てもヘッダー内で非表示のままになる可能性があります。

この英語のテキストはフランス語のオリジナルから自動的に生成されたもので、まだレビューされていません。
このページは自動翻訳されました。英語版が正式です。

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 マニフェストは、控えめなリソースを設定します。

50-250m
レプリカあたりの CPU
リクエスト→制限
64-128
レプリカあたりの MB RAM
リクエスト→制限
2
プロジェクトごとのレプリカ
高可用性 (P22)

これらのレプリカが実際に消費するのは CPU ではなく、Postgres プライマリへの接続です。各 PostgREST インスタンスは、テナントにデプロイされた PgBouncer プーラーを経由せずに、プライマリ (-rw) に直接接続します。この選択については、PostgREST の互換性に関する記事ですでに詳しく説明されています。: LISTEN/NOTIFY スキーマのリロード メカニズムには永続的な接続が必要であり、トランザクション モードのプーラーとは互換性がありません。この記事で追加される内容: 接続に関して、実際のコストはいくらか、そしてそのピークはどこでしょうか。

レプリカごとのこのプールのサイズ (PGRST_DB_POOL) は、プロジェクト レベルに応じて意図的に異なります。これは、各プロジェクトの PostgREST マニフェストを構築する関数 k8s_tenant.rsで検証されています。

ベアリングPGRST_DB_POOL / レプリカレプリカつながり・目覚めたプロジェクト
専用(プレミアム、A1)10 (PostgREST のデフォルト)220
共有 (フリート、無料/プロ/チーム)2 (Aurabase のデフォルト、低め)24
deploy/cnpg/tenant-postgrest.yaml (実際の抽出、プロビジョナーによって置換された値)yaml
# プライマリ上のレプリカごとの接続のフィンガープリント。
env:
  - { name: PGRST_DB_POOL, value: "PGRST_DB_POOL_VALUE" }  # 10 (専用) または 2 (共有)

専用レベルでは制約が緩和されます。プロジェクトには独自の 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.

同時にアクティブなプロジェクトのレベル別の予算、共有 Postgres クラスター無料レベル: 5 つの同時アクティブ プロジェクト (最大接続数 50、プーラー 20)。プロレベル: 7 (最大接続数 100、プーラー 60)。チームレベル: 10 (max_connections 200、プーラー 150)。 Aurabase コード (fleet.rs::derive_wake_budget) から派生した式、10 接続の固定予約、awake プロジェクトごとに 4 接続。024681012無料 (最大接続数 50)5つのプロジェクトプロ (最大接続数 100)7つのプロジェクトチーム (最大接続数 200)10件のプロジェクト

ソース: 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 のトランザクション動作と比較します。

terminalbash
# 大規模なテーブルではコストがかかります: フィルタリングされた結果の MVCC スキャンを強制します
curl "https://<gateway>/v1/db/<project_id>/orders?status=eq.paid" \
  -H "apikey: <clé>" -H "Prefer: count=exact"

# PostgREST によって文書化された、より安価な代替手段
  -H "Prefer: count=planned"   # プランナーによる見積り
  -H "Prefer: count=estimated" # しきい値を超えて計画されている、正確に下回っている

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=exact5/10 実質0-4/10{合計: 10}

count=exactがないと、5 行の応答は、実際に 5 行しか含まれないテーブルと区別がつきません。 Content-Range: 0-4/* は、適用される制限ではなく、表示される行を記述します。この場合、実際の上限はどこにも表示されず、Aurabase SDK の PostgREST パスで直接測定されます。

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 は大規模な運用に十分な速さですか?+
PostgREST 自体は軽量のプロセスです。専用の Aurabase インスタンスでは、レプリカは 50 ~ 250 ミリコアの CPU と 64 ~ 128 MB の RAM で実行され、プロジェクトの Kubernetes マニフェストで検証されています。生の HTTP スループットが運用環境の制限要因になることはほとんどありません。全体がスケールするかどうかを決定するのは、PostgREST バイナリ単独の速度ではなく、Postgres の接続予算、正確な COUNT のコスト、およびスキーマ キャッシュです。
PostgREST 応答が db-max-rows によって切り捨てられたかどうかを確認するにはどうすればよいですか?+
PostgREST によって返される Content-Range ヘッダーにはこれが記載されていません。 db-max-rows あたり 5 行に制限されたレスポンスは、専用の Aurabase インスタンス上の実際の条件で測定された、実際には 5 行しか含まれていないテーブルと区別がつきません。これを検出する唯一の信頼できる方法は、受信した行数と Prefer: count=exact によって返された合計行数を比較することです。このヘッダーがなければ、切り捨ては表示されません。
正確な COUNT は常に PostgREST リクエストを遅くしますか?+
推奨: count=exact Postgres はクエリごとにフィルター処理された結果の表示行をカウントするように強制されますが、MVCC によりテーブル サイズに応じてコストが増加します。 Postgres は、そのままではインデックス付きの行カウンターを維持しません。 PostgREST には、公式ドキュメントに記載されている、count=planned (スケジューラーによる推定) と count=estimated (しきい値を超えた自動切り替え) という 2 つのより安価な代替手段が用意されています。
PostgREST はいくつの Postgres 接続を消費しますか?+
これは、PGRST_DB_POOL にレプリカの数を乗算した値に完全に依存します。 Aurabase コードで検証: 専用インスタンス (プレミアム層) は、デフォルトでレプリカごとに 10 個の接続、または 2 つのレプリカで合計 20 個の接続を開きます。共有レベルは、共有クラスターの同じ max_connections バジェットでより多くのテナントに対応するために、このプールをレプリカごとに 2 つ、または起動されたプロジェクトごとに 4 つの接続に自発的に下げます。
公式の PostgREST ベンチマークはありますか?+
このプロジェクトは、GitHub 上に専用リポジトリ PostgREST/postgrest-benchmark を維持しており、個別のマーケティング数値を公開するのではなく、リリースごとのスループットの変動を追跡します。ここでは実行したり再公開したりしません。この記事では、私たちが独自に再現したベンチではなく、私たちのコードと公式 PostgREST ドキュメントで検証されたアーキテクチャ上の制限について説明します。
#
結論

覚えておくべきこと

PostgREST は、HTTP 負荷だけで壊れることはほとんどありません。そのためには、そのアーキテクチャが単純すぎます。本番環境で何が中断されるかは、本番環境の周囲にあるものです。つまり、そのレプリカが開いたままにする接続の数、正確な総コストはいくらかです。これには、切り捨てが表示されたままになるかどうか、および移行後のウィンドウの持続時間も含まれます。

これら 4 つの制限は Aurabase に固有のものではありません。セルフホスト型または管理型のすべての PostgREST デプロイメントに適用されます。このコードが示しているのは、マルチテナント展開によって、実稼働環境でそれらが驚くべきものになるのではなく、どのように明示的にされるかということです。

導入の準備はできていますか?

5 分でバックエンドが完成します。

クレジット カードは不要 · 500 MB 無料 · 50,000 MAU