
2026/04/19 1:50
PgQue:ゼロ・ブロート PostgreSQL キュー
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
PgQue は、管理プロバイダー(RDS、Aurora、Supabase など)を含むすべてのプラットフォームで動作するように設計された、軽量な純粋な PL/pgSQL キューシステムとして、長年存在した PgQ アーキテクチャを再活性化させます。その主な利点は、C 拡張機能、共有ライブラリ、外部デーモン、または厳しいプロバイダーの承認が必要とされないことであり、これによりインフラストラクチャの複雑性が大幅に減少するとともに、堅牢な ACID 準拠およびトランザクション安全性が保証されます。PGMQ や pg-boss といった競合ソリューションがロック機構に依存しているのに対し、PgQue は独自のネイティブファンアウト機能を備えており、消費者は共有イベントログに対してそれぞれ個別のカーソルを保持します。スナップショットベースのバッチ処理と TRUNCATE レイアウトを採用することでパフォーマンス劣化を防ぎ、ベンチマークによりデータ肥大化なしで約 86,000 イベント/秒の高い挿入率を達成することが確認されました。エンドツーエンド配信遅延はデフォルトで通常約 1~2 秒(マイクロ秒スケーリングの一回呼び出しあたり)であり、将来的にはサブ秒間配送を実現するためのロジカルデコーディングベースのウェイクアップ機能の導入を目指しています。言語非依存な SQL API は内蔵リトライロジックとデッドレターキューをサポートし、複雑な外部フレームワークに限定されていた高度なメッセージング機能を備えたポータブルソリューションを開発者に提供します。インストールには 1 つの SQL ファイル(
sql/pgque.sql)と、pg_cron または外部スケジューラによって駆動される定期的なタイカーが必要で、読み取り、書き込み・消費、管理の 3 つの役割を作成します。Apache-2.0 ライセンス(PgQ から派生したコードを含む)の下にあり、Python、Go、TypeScript の複数のドライバとサンプルライブラリをサポートしています。本文
PgQue – PgQ ユニバーサルエディション
軽量化のゼロにこだわった Postgres キュー。インストールは 1 つの SQL ファイル、スケジュールは
pg_cron で。
コンテンツ
- PgQue の価値(Why)
- レイテンシへのトレードオフ
- 比較
- インストール
- ロールと権限
- プロジェクトステータス
- ドキュメント
- クイックスタート
- クライアントライブラリ
- ベンチマーク
- アーキテクチャ
- コントリビューション
- ライセンス
Why PgQue(PgQue の価値)
PgQue は、運用環境で最も歴史の長い Postgres キューアーキテクチャの一つである PgQ を、あらゆる Postgres プラットフォーム(マネージドプロバイダーを含む)で動作させる形に再構築しました。
PgQ は Skype で設計され、何億人ものユーザー向けのメッセージングを処理するために採用されました。また、大型の自己管理型 Postgres デプロイメントで 10 年以上稼動していました。標準的な PgQue は C の拡張機能(
pgq)と外部デーモン(pgqd)に依存しており、これは多くのマネージド Postgres プラティフォームでは動作しません。
PgQue は、この戦術の試練をくぐり抜けたエンジンを純粋な PL/pgSQL に再構築しました。これにより、「ゼロ・ブロット」キューのパターンは SQL が実行できるあらゆる場所で作動し、スタックに別の分散システムを追加する必要がなくなります。
拡張機能へのアンチテーゼ。 任意の Postgres 14+(RDS、Aurora、Cloud SQL、AlloyDB、Supabase、Neon を含む)で動作する純粋な SQL + PL/pgSQL です。C の拡張機能は不要で、
shared_preload_libraries も必要としません。プロバイダーからの承認や再起動も不要です。
歴史的な文脈(2 つのデッキから):
- Marko Kreen (Skype) – PGCon 2009 — PgQ
- Alexander Kukushkin (Microsoft) – 2026 — PgQ の再発見
Why PgQue(PgQue の価値その 2)
多くの Postgres キューは、
SKIP LOCKED 加上 DELETE および/または UPDATE に依存しています。これはトイ例では機能しますが、結果としてデッド・タプル(死んだ行)、VACUUM の負荷増大、インデックスのブロット、および持続的な負荷下でのパフォーマンス劣化を引き起こします。
PgQue はこの一連の問題を回避します。行ごとの削除ではなく、スナップショットベースのバッチ処理と
TRUNCATE ベースのテーブル回転を使用します。ホットパスは予測可能に保たれます:
- 設計上ゼロ・ブロット — メインキュー経路上でデッド・タプルが発生しません
- パフォーマンス劣化なし — 稼動期間が数ヶ月経っても遅くなりません
- 重負荷システム向け — オリジナル PgQ アーキテクチャが設計された持続的な負荷環境向け
- 本物の Postgres ガランテッド機能 — ACID トランザクション、トランザクショナルなエンキュー/コンシュム、WAL、バックアップ、レプリケーション、SQL の可視性
- マネージド・Postgres に対応 — カスタムビルド不要、C 拡張機能不要、別々のデーモン不要
PgQue は、データベース内キューにありがちなブロットの負担を課さず、Postgres の耐久性とトランザクショナルな挙動を備えたキューのセマンティクスを提供します。
レイテンシへのトレードオフ
PgQue は行ごとの請求(claiming)ではなく、スナップショットベースのバッチ処理を中心に設計されています。これがホットパスでのゼロ・ブロット、持続的な負荷下での安定した動作、および Postgres 内部でのクリーンな ACID セマンティクスを実現する要因です。
トレードオフはエンドツーエンドの配信レイテンシ(送信から消費者がイベントを受け取るまでの間隔)です。デフォルト構成では、エンドツーエンドの配信は通常 〜1~2 秒 で完了します:次のティックにかかる最多で 1 秒に、および消费者的ポーリング間隔が含まれます。呼び出しあたりのレイテンシ(送信/受信/ACK 関数自体)はマイクロ秒レベルに維持されます。
配信レイテンシを削減する方法:
- ティック周波数とキュー閾値のチューニング
- テストやデモ用、または即時バッチ実行には
を使用force_tick() - 将来バージョンでは、ティック間隔を切ることなく 1 秒未満の配信を実現するために、ロジカル・デコードベースのウェイクアップ機能を追加する可能性があります。
あなたの最優先事項が単位のミリ秒単位のディスパッチである場合、PgQue は適さないツールです。負荷下でのブロットのない安定性が最優先事項であれば、そこで PgQue がフィットします。
比較
| 特徴 | PgQue | PgQ | PGMQ | River | Que | pg-boss |
|---|---|---|---|---|---|---|
| スナップショットベースのバッチ処理(行ロックなし) | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
| 持続的な負荷下でのゼロ・ブロット | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
| 外部デーモンまたはワーカーバイナリ不要 | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |
| 純粋な SQL インストール、マネージド Postgres 対応 | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ |
| ランタイム不依存の SQL API | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |
| 複数の独立したコンシューマ(ファントーアウト) | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ |
| リトライ機能付きバックオフ | ✅ | ✅ | ⚠️ | ✅ | ✅ | ✅ |
| 組み込みのデッドレターキュー | ✅ | ❌ | ⚠️ | ⚠️ | ❌ | ✅ |
凡例: ✅ はい · ❌ いいえ · ⚠️ 部分的/間接的
備考:
- PgQ は Skype 時代のキューエンジン(約 2007 年)を元にしており、同じスナップショット/回転アーキテクチャですが、C の拡張機能と外部デーモン(pgqd)が必要で、マネージド Postgres では利用できません。PgQue はこの両方の制約を取り除きました。
- 外部デーモンなし: PgQue は
(または独自のスケジューラー)によるティックを使用し、PGMQ は可視性タイムアウトを使用します。River、Que、および pg-boss は Go/Ruby/Node.js ワーカーバイナリを必要とします。pg_cron - Que はアドバイザリーロック(SKIP LOCKED ではない)を使用しており、請求によるデッド・タプルはありませんが、完了したジョブはそれでも
されます。Brandur のブロットに関する投稿は Heroku における Que についてのものです。Ruby 限定です。DELETE - PGMQ リトライ は可視性タイムアウトによる再配信(
トラッキング)であり、設定可能なバックオフや最大試行回数はありません。read_ct - pg-boss のファントーアウトは
によるコピー・パー・キューで、共有イベントログと独立したカーソルとは異なります。publish()/subscribe()
カテゴリ: River、Que、および pg-boss(そして Oban、graphile-worker、solid_queue、good_job)はジョブキューフレームワークです。PgQue はファントーアウトを備えた高スループットストリーミングに最適化されたイベント/メッセージキューです。
PgQue を差別化する要素
1. イベントテーブルのゼロ・ブロット(設計上)
SKIP LOCKED キュー(PGMQ、River、pg-boss、Oban、graphile-worker)は UPDATE + DELETE で行を処理し、VACUUM が必要となるデッド・タプルを作成します。持続的な負荷下では、文書化された失敗を引き起こします:
- Brandur/Heroku (2015) — 1 時間以内に 60k のバッカルク。
- PlanetScale (2026) — OLAP を併用した状態で 800 ジョブ/秒での死亡スパイラル。
- River issue #59 — autovacuum の飢餓( starvation)。
Oban Pro はそれを緩和するためにテーブルパーティショニングをリリースしました。PGMQ は積極的な autovacuum 設定を提供しています。PgQue の
TRUNCATE 回転は、構築上の設計によりゼロのデッド・タプルを作成します。チューニングは不要です。xmin ホライゾンピンナリングにも免疫があります。
2. ネイティブなファントーアウト
登録された各コンシューマは共有イベントログ上で独自のカーソルを維持し、独立してすべてのイベントを受け取ります。これは競合消費者(
SKIP LOCKED)では各ジョブが 1 つのワーカーに行くのとは異なります。pg-boss はファントーアウトがありますが、それはコピー・パー・キュー(イベントあたり購読者につき INSERT 1 回)です。PgQue のモデルは共有ログ上の位置であり、データ複製なし、原子的なバッチ境界、後発の購読者が追いつきます。Kafka トピックに近いものであり、ジョブキューとは異なります。
PgQue とジョブキューの使用タイミング
- PgQue を選ぶ時: イベント駆動型のファントーアウトが必要であり、ブロットの調整が不要であり、ランタイム不依存的な SQL API が望ましく、各ジョブの優先順位やワーカーフレームワークは必要ない場合。
- ジョブキューを選ぶ時: 各ジョブのライフサイクル、ミリ秒単位のレイテンシ(3ms 未満)、優先順位キュー、cron スケジューリング、ユニークなジョブ、または深いエコシステム統合(Elixir/Go/Node.js/Ruby)が必要である場合。
インストール
要件: Postgres 14+ および定期的に
pgque.ticker() を呼び出すもの(デフォルトでは 1 秒ごと)。pg_cron が推奨デフォルトです:すべての主要なマネージド Postgres プロバイダー(RDS、Aurora、Cloud SQL、AlloyDB、Supabase、Neon)でプリインストールまたはコマンド一つで利用可能。自己管理型 Postgres の場合は pg_cron 設置ガイドに従ってください。外部スケジューラー(システム cron、systemd、アプリ内のワーカーループ)も代替手段として機能します(下記参照)。
psql セッション内:
begin; \i sql/pgque.sql commit;
またはシェルの場合、
psql --single-transaction を使用して単一トランザクション保証を得ます:
PAGER=cat psql --no-psqlrc --single-transaction -d mydb -f sql/pgque.sql
PgQue のデータベースと同じ場所に pg_cron
が存在する場合:
pg_cronpgque.start() はデフォルトのティックとメンテナンスジョブを作成します。
別のデータベースにある
の場合:
pg_cron
pg_cron は 1 つの指定されたデータベース(cron.database_name、通常は postgres)でジョブを実行します。PgQue スキーマが別のデータベースにある場合は、データベース間のパターンを使用して pgque.ticker() および pgque.maint() を呼び出します。
Todo: 今後のリリースでは、これを自動的に検知し、
から正しいpgque.start()コールをエミットする予定です。cron.schedule_in_database
のログ衛生( hygiene):
ティックは毎秒動作し、pg_cron
cron.job_run_details に時平均で約 3,600 行を追加しますが、内蔵のクレンジング機能はありません。グローバルに alter system set cron.log_run = off; を設定するか、定期的なクレンジングスケジュールを設定してください(両方のレシピはチュートリアルを参照)。
pg_cron がない場合
PgQue のインストール自体は可能です。ティックとメンテナンスはアプリケーションまたは外部スケジューラーから駆動します:
PAGER=cat psql --no-psqlrc -c "select pgque.ticker()" # 1 秒ごと PAGER=cat psql --no-psqlrc -c "select pgque.maint()" # 30 秒ごと
重要: PgQue は機能するティックがない場合メッセージを配信しません。エンキューは動作しますが、新しいものは消費者が何も見ないのは、ティックが作成されていないからです。
pg_cron を使用しない場合は、ご自身で pgque.ticker() および pgque.maint() を実行してください。
現在ではインストールを一方向(アップグレードおよび再インストール経路はまだ固められていますが)と扱ってください。アンインストールには:
\i sql/pgque_uninstall.sql を使用します。
ロールと権限
インストールは 3 つのロールを作成します。アプリケーションユーザーはスーパーユーザである必要はありません——アクセスパターンに合ったどのロールを割り当てても構いません。
| ロール | 目的 | 付与されるアクセス権 |
|---|---|---|
| pgque_reader | ダッシュボード、メトリクス、デバッグ | , , , およびすべてのテーブルに対する |
| pgque_writer | 送信元とコンシューマ(多くのアプリ) | を継承 + モダン API (, , , , , , ) および基礎的な PgQ プリミティブ (, , , , , , ) |
| pgque_admin | オペレーター、マイグレーション | を継承 + スキーマ/テーブル/シークエンスの全アクセス権。 は および (スーパーユーザ専用 - スキーマを削除するため) から取り消されます。 |
典型的なアプリ設定:
\i sql/pgue.sql select pgque.start(); -- オプション pg_cron ティッカー + メイン create user app_orders with password '...'; -- 実際のパスワードに置換してください grant pgque_writer to app_orders; create user metrics with password '...'; -- 実際のパスワードに置換してください grant pgque_reader to metrics;
DDL クラスの操作 (
create_queue, drop_queue, start, stop, maint, ticker, force_tick) は pgque_writer に付与されず、管理者/マイグレーションロールによって実行すべきです。現在はデフォルトで PUBLIC ですが、PUBLIC から取り消し pgque_admin だけに付与する道筋はロードマップにあります。
プロジェクトステータス
PgQue は製品および API レヤーとして初期段階です。PgQ そのものは Skype スケールで 10 年以上稼動しています。ここでは新規要素としては、パッケージング、モダン化、マネージド Postgres の互換性、そしてそのコアを囲む上位レベルの PgQue API です。
デフォルトインストールは v0.1 で小規模に保たれており、追加の API は
sql/experimental/ 下に居住しており、推奨する価値があるまでにはなります。blueprints/PHASES.md を参照してください。
ドキュメント
- チュートリアル — ハンズオンガイド。新規ユーザーはこちらから開始してください。
- リファレンス — すべて出荷された関数とロール。
- 例 — パターン:ファントーアウト、エグzakly-once、バッチ読み込み、定期的なジョブ。
- ベンチマーク — スループット測定と手法。
- PgQ 概念 — 用語集(バッチ、ティック、回転)コントリビューター向け。
- PgQ の歴史 — このエンジンが生まれた場所。
クイックスタート
1. キュー+コンシューマを作成
select pgque.create_queue('orders'); select pgque.subscribe('orders', 'processor');
2. メッセージを送信
select pgue.send('orders', '{"order_id": 42, "total": 99.95}'::jsonb);
3. キューを進める(pg_cron を使用する場合オプション)
-- force_tick はラグ/カウント閾値をバイパスします — デモ/テストに便利です select pgque.force_tick('orders'); select pgue.ticker();
4. 受信
-- batch_id は返された各行で同じです select * from pgque.receive('orders', 'processor', 100);
5. ACK
select pgque.ack(:batch_id);
送信、ティック、および受信は別トランザクションで行うべきです——それが PgQ のスナップショットベース設計が意図通りに機能する所為です。通常動作では、
pg_cron または外部スケジューラーが pgque.ticker() を駆動します。force_tick() は主にデモ、テスト、および手動操作のために使用されます。より長いチュートリアルやファントーアウト、エグzakly-once、定期的なジョブなどのパターンについては例を参照してください。
クライアントライブラリ
PgQue は SQL 第一主義なので、あらゆる Postgres ドライバが動作します。Python、Go、TypeScript のクライアントライブラリの例が存在しますが、未公開でまだ進化しており、安定した SDK らではなく統合パターンを示しています。コントリビューションを歓迎します。
Python (pgque-py) — psycopg 3
from pgue import PgqueClient, Consumer client = PgqueClient(conn) client.send("orders", {"order_id": 42}) consumer = Consumer(dsn, queue="orders", name="processor", poll_interval=30) @consumer.on("order.created") def handle_order(msg): process_order(msg.payload) consumer.start()
Go (pgque-go) — pgx/v5
client, _ := pgque.Connect(ctx, "postgresql://localhost/mydb") consumer := client.NewConsumer("orders", "processor") consumer.Handle("order.created", func(ctx context.Context, msg pgue.Message) error { return processOrder(msg) }) consumer.Start(ctx)
TypeScript (pgue-ts) — node-postgres
const client = new PgqueClient('postgresql://localhost/mydb'); await client.connect(); await client.send('orders', { order_id: 42 }, 'order.created'); await client.subscribe('orders', 'processor'); const messages = await client.receive('orders', 'processor', 100); if (messages.length > 0) await client.ack(messages[0].batch_id);
あらゆる言語
select pgue.send('orders', '{"order_id": 42}'::jsonb); select * from pgque.receive('orders', 'processor', 100); select pgque.ack(batch_id);
ベンチマーク
prelimiary ノートパソコンの数字:PL/pgSQL インサートで ~86k ev/s、コンシューマ読み込みレートで ~2.4M ev/s、30 分間の持続的なテスト下でデッドタプルの成長ゼロ。完全なテーブルと手法については docs/benchmarks.md を参照してください。
サーバークラス数の次期追跡予定。
アーキテクチャ
PgQue は PgQ の実証されたコアアーキテクチャ(スナップショットベースバッチ分離、ホットパス上の 3 テーブル
TRUNCATE 回転、独立したリトライ/遅延/デッドレターテーブル、独立したコンシューマカーソル)を維持し、その上に乗せてモダンな API レヤーを追加しました。完全な仕様については blueprints/SPECx.md を参照し、バッチ/ティック/回転用語集については docs/pgq-concepts.md を参照してください。
コントリビューション
blueprints/SPECx.md の仕様と実装計画を参照してください。新しいコードは Red/Green TDD に従うべきです——最初に失敗するテストを書き、その後修正します。
ライセンス
Apache-2.0。LICENSE を参照してください。
PgQue は PgQ から派生したコードを含みます(ISC ライセンス、Marko Kreen / Skype Technologies OU)。NOTICE を参照してください。