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

pg_durable: マイクロソフトがデータベース内での耐久性実行をオープンソース化

概要

pg_durable は、PostgreSQLで耐障害性のある長時間実行SQLワークフローを実現する拡張機能。 cronジョブや外部ワーカー、キュー の複雑な連携を不要にし、SQLでワークフローを定義可能。 クラッシュや再起動後も自動で再開 し、状態管理や進捗追跡もPostgres内で完結。 Azure HorizonDB などで利用可能、追加インフラ不要。 データに近い場所で計算処理を実現するための新しい標準パターン。

pg_durableとは何か

  • PostgreSQL拡張機能 として動作、外部サービス不要
  • SQLでワークフローを定義 し、各ステップごとにチェックポイント保存
  • クラッシュ・再起動・失敗時にも 途中から自動再開
  • 耐障害性・可観測性 をPostgres単体で実現

主な利用対象者

  • バックエンド/データエンジニア :データに近い場所でワークフローを管理したい場合
  • DBA/SRE :再起動に耐える自動化RunbookやSQL監査性重視の運用
  • AI・データパイプライン開発チーム :行単位やバッチ単位で堅牢な処理が必要な場面

コアアイデア

  • pg_durable関数 はSQLステップのグラフで構成
  • 各ステップごとに 耐障害チェックポイント を自動作成
  • 失敗やクラッシュ時に 手動で状態を復元せずとも自動再開

想定ユースケース

  • ベクトル埋め込みパイプライン :チャンク分割、API呼び出し、pgvectorへのupsert
  • 大規模バッチのETL :ステージング、重複排除、変換、公開
  • 定期メンテナンス :膨張検知、通知、承認待ち、次アクション実行
  • ファンアウト集約 :並列クエリ実行後の結果結合
  • 外部API連携 :エンリッチメント、分類、Webhook型SQL呼び出し

従来の課題と解決

  • pg_cron+jobsテーブル+ワーカー 等で複雑な状態管理
  • 外部オーケストレーター (Airflow, Temporal, Step Functions, Argo等)との連携コスト
  • 再起動・失敗時の手動リカバリ ・部分的再実行問題
  • アプリ層の並列処理による部分失敗バグ・ドリフト
  • ワークフロー定義がSQL・ワーカー・キュー等に分散

アーキテクチャの変化

  • ワークフロー定義がSQL内に統合
  • 進捗・リトライ状態管理もPostgres内に集約
  • アプリ層のワーカーやキューの削減
  • df.instances等のPostgresテーブルで可視化・監査

適さないケース

  • 単一SQL文で済むジョブ
  • サブミリ秒単位の同期処理が必須な場合
  • Postgres拡張やバックグラウンドワーカーが使えない環境
  • ワークフローがPostgres外の異種システムを跨ぐ場合
  • SQLステップやHTTP呼び出しに収まらない独自ロジックが主な場合

基本的な使い方

  • SQLでワークフローを定義 (例:'sql1' |=> 'batch' ~> 'sql2')
  • df.start()でワークフロー開始、インスタンスID取得
  • 各ステップの間で自動チェックポイント
  • Postgresから進捗・結果をクエリ可能

主な特徴

  • 耐障害性 :状態はPostgreSQLに永続化、クラッシュ・フェイルオーバー対応
  • SQLネイティブ :合成演算子で柔軟な関数定義
  • データベース親和性 :スケジューリング・条件分岐・並列実行のプリミティブ
  • ゼロインフラ :追加サービス不要、PostgreSQL拡張のみ

クイック例

SELECT df.start(
  'SELECT id FROM documents WHERE processed = false LIMIT 100' |=> 'batch'
  ~> 'UPDATE documents SET processed = true WHERE id = ANY($batch)'
);

インストールとセットアップ

  • Debianパッケージ (PostgreSQL 17/18対応)をGitHubリリースから取得
  • pg_durableをshared_preload_librariesに追加後、Postgres再起動
  • CREATE EXTENSION pg_durable; で拡張有効化
  • ユーザー権限付与必須 (df.grant_usage('role')等)

開発・テスト環境

  • PostgreSQL 17/18, Rust (nightly), cargo-pgrx 0.16.1が必須
  • VS Code Dev Container やローカル、Docker対応
  • pg-start.shでローカルクラスタ初期化・起動
  • E2Eテスト・回帰テスト が充実

マルチユーザー・権限管理

  • 拡張インストール後は明示的にアプリケーションロールへ権限付与が必要
  • RLS(行レベルセキュリティ)で各ユーザー専用のインスタンス/ノード管理
  • 管理者はsuperuser権限が必須
  • df.varsはユーザーごとにスコープ分離

CI/CDとテスト

  • cargo fmt, clippy, ユニット/E2Eテスト、pg_regressによる自動チェック
  • .github/workflows/ci.ymlでCIワークフロー管理

ドキュメントとサポート

  • User Guide, MVP Guide, Examples で利用方法・内部実装を詳細解説
  • GitHub Issues でバグ・要望受付(セキュリティはSECURITY.md参照)
  • Microsoft Open Source Code of Conduct 準拠

内部アーキテクチャ

  • pgrxで構築されたPostgreSQL拡張
  • SQL DSLで関数グラフを構築、バックグラウンドワーカーでdurable実行
  • Rust製ライブラリduroxide(オーケストレーション)、duroxide-pg(状態永続化)を活用
  • Postgres内のdf.(DSLグラフ)、duroxide.(ランタイム状態)スキーマ

他言語での利用

  • Rust, Python, Node等でdurable関数を記述したい場合 は、duroxide+duroxide-pgを直接利用可能
  • pg_durableはSQLでの記述を簡易化するラッパー

現状と今後

  • プレビュー版 として公開中
  • 機能追加・安定化進行中、フィードバック歓迎

Hackerたちの意見

2026年はPostgresキューの年だね!(DBOS[0], pgQue[1]) コミュニティがこれに貢献してくれて、使える選択肢が増えるのは素晴らしいよね。でも、元アプリエンジニアとしては、キューのロジックはコードの中、Gitに入れておきたい派なんだ。まあ、適切なツールがあれば考えが変わるかも。:)

そうだね、作業が難しくなるか、単に違うだけかもしれないけど、ドキュメントや情報(検索可能なドキュメント、投稿、経験)、ツールが不足してる気がする。バージョン管理、デバッグ、テスト、リリースの話はどうなってるの?データのローカリティやスタックの簡素化のためにすべてを一緒にするのはクールだけど、「正しく」物事を行う方法に関する有用な知識を失う気がする。

「キューのロジックはコードにしたい」に+1。データの内容は、私がそれに対して取るアクションほど頻繁には変わらないから、データの扱い方を変えるたびにマイグレーション(これはオールオアナッシングの操作だよ)をやる意味がわからない。これが、Supabaseで何か少しでもトリビアルなことをするためにPostgres関数を作らなきゃならなかったのが本当に嫌だった理由でもある。それはさておき、前のスタートアップではPostgresの上にシンプルなジョブキューを手作りしたよ(ロック、ポーリング、カラムでの予約、ポーリング、ジョブ完了を示すための予約更新)。pgqueのようなものがあれば、もっと洗練されてたと思う。

同じことだけど、ビジネスロジックに関係ないDBレベルのことには役立つかもね。こういうタイプのメンテナンスパッケージはいつも持ってたし、データベース自体と一緒にデプロイできたらちょっとクールだと思う。でも、コードレイヤーにある方が好きっていうのには同意するよ。

元アプリエンジニアとしては、キューのロジックはコードに、Gitに入れておきたいんだけど、適切なツールがあれば考えが変わるかもね。:) つまり、DBトリガーがあるプロジェクトではSQLコードもGitに入れてたし、Djangoのマイグレーションを使ってローカルでセットアップできるように、そこに押し込まれたものもあったと思うよ。

これって、昔からある問題に対する間違った解決策に感じるな。Apache AirflowみたいなDAGスケジューラーがずっと前から解決してるのに。なんで制御フローをデータベースに保存して、コードにしないのかが理解できない。ちょっと変な感じ。プロジェクトを否定するつもりはないけど、まだ理解できてない気がする。

マイクロソフトには、その手のことのために独自のDurable Taskフレームワークがあって、temporalのように自己ホスト型のスタンドアロンサービスとしても、Azure Functionsでサーバーレスとしても動かせるんだ。確か、airflowやtemporalよりも前に出てきたと思う。このフレームワークはデータベース特有のユースケースみたい。利点は、ジョブの正確な状態をデータベース内で追跡できることだと思う。ワークフローログとコードベースを照らし合わせて、状態を一行ずつ追跡する必要がないからね。それに、オーバーヘッドやレイテンシが少なくて、運用的にも一つのことを立ち上げる手間が減ると思う。

使わない方がいい時 > … > ワークフローは主にPostgresの外にあって、いろんな異種システムにまたがる。 このプロジェクトはTemporalみたいなものとどう比較できるの?この特定の推奨が示す制限を誤解してるのかな?

同意する - ここにある例を見ても、このプロジェクトの価値が理解できない。https://github.com/microsoft/pg_durable/blob/main/examples/i... 技術的には面白い成果だと思うけど、このSELECT文を読むのはかなり奇妙だよね。df.start( @> ( ($$SELECT ... FROM demo.invoices WHERE status = 'pending'$$ |=> 'inv') ~> df.if_rows('inv', $$UPDATE ... SET status = 'processing'$$ ~> (df.http(...) |=> 'resp') ~> df.if($$SELECT $r.ok$$, -- 分類、分岐、信号を待つ ... ), df.sleep(5) ) ), 'invoice-approval-pipeline' );

正しく理解しているなら、Absurd(Pi LLMハーネスの開発者による)は、純粋なDBアプローチをできるだけ最小限に抑えているみたい。自分も最近このトピックに興味を持ち始めたばかりだけど。https://github.com/earendil-works/absurd

ちょっとした指摘だけど、absurdはMario Zechnerがearendilに参加する前に始めたオリジナルのプロジェクトみたいだね。彼がコミットに見当たらないのもそのせいかもしれないけど、詳しいことは知らないから、ちょっと気になってる。

なんでデータベースの外にあるオーケストレーションツールを使うより、これを使いたいのか説明してくれる人いる?READMEやいくつかの例を読んだけど、まだ理解できてない。

アーキテクチャの中でデータベースが唯一の「状態を持つ」コンポーネントの場合、便利なこともあるよね。また、すべての「状態」が一つのデータベースにあるなら、一貫したバックアップを取るチャンスが増える。

よく統合されたアプリケーションワークフロー(例えば、フロントエンドアプリのパーマリンクでの進捗報告)や、アプリ再起動に強い再開可能なワークフローが作れるし、余計なインフラを追加しなくて済む。私たちはそれをhttps://transport.data.gouv.frで使ってる(かなりの処理をするElixirアプリ)けど、助かってるよ。pg_durableにはまだ詳しくないけど、似たようなソリューションは使ったり実装したりしたことがあるから、共感できる。

データベースのスナップショットPITRは、耐久性のあるジョブを含むすべてを復元できるってことだね。データストアの一部である他のバックアップと同期する必要もないし、ETLパイプラインや状態機械型のジョブにはうってつけだよ。もしETLがほとんどSQLなら、同じサーバーで実際のジョブを実行するのも助けになるよね。

ここで貢献してる者です。マイクロソフトでは、Postgresの顧客は大きく2つのグループに分かれてるみたい。できるだけデータベース内でやりたい派と、あなたの意見に賛成してアプリや計算をDBの外に置きたい派。

仕事でAzureに縛られてて、常にAzure pgが現代に追いつくのを待ってるんだ。例えば、これが使えない: https://www.paradedb.com/blog/hybrid-search-in-postgresql-th... それに、ウルトラワイドな高次元ベクトルも使えない。pg_durableをオープンソースにしてくれるのは嬉しいけど、AWSで得られる基本的な機能を取り入れてくれないかな?

ごめん、これについては考えたと思うけど、Postgres vCurrentがインストールされたベアVMを作れない理由は何なの?

Azure Cosmos DBの方がベクター検索には向いてるんじゃない?

ParadeDBはAGPLだから、一般的にはハイパースケーラーでは利用できないよ。でも、Azure HorizonDB(おそらくすぐにFlexでも)でhttps://github.com/timescale/pg_textsearchを使えるよ。ちなみに、私はpg_textsearchのメンテナーで、今はAzureにいるんだ。ベクターサポートについてのあなたのコメントはちょっと理解できなかったけど、pgvector + diskannが提供している以上のものを求めてるの?(どちらもAzureで利用可能だよ)

データベースって、スケールさせるのが一番難しいインフラの一つじゃない?なんで追加の長時間実行されるジョブを載せたいと思うの?

後でコンサルティング料金で稼ぐために、今は準備を整えなきゃ。

Postgresでの長時間実行されるジョブは、全然新しいことじゃないよ。pg_cronがその一例だね。結局のところ、これらのワークロードは外部コンポーネントによってトリガーされようが、データベースに対して実行されることになるんだ。データベースからのHTTPクエリも、データやAIパイプラインの追加コンポーネントからの往復や失敗ポイントを避けるために、人気が出てきてるしね。でも、データにコンピュートを持ってくるか、その逆かっていうのは、結構意見が分かれるデザインの選択だよね。

ドキュメントや例を読んでいて、いくつか不明な点があるんだけど:df.wait_for_schedule() これはどういう呼び出しなの?アプリケーションから呼び出すとイドポテントなの?同じパラメータで2回実行したら、ダブルティックになるの?これはクエリコンソールから手動で一度だけ実行するの?それともマイグレーションスクリプトの一部として実行するの?この部分について: -- 人間の信号を待つ(5分タイムアウト) ~> (df.wait_for_signal('approval', 300) |=> 'sig') ~> df.if( $$SELECT NOT ($sig::jsonb->>'timed_out')::boolean AND ($sig::jsonb->'data'->>'approved')::boolean$$, timed_outはタイムアウト時に返される固定の定数なの?エラーや例外の処理はどうするかもすぐには分からないね。

あなたは耐久性のある関数を作成して、その実行をdf.start()で同時に開始しているんだね。これによって、この耐久性のある関数の実行を表すインスタンスIDが返されるよ。これを使って、以降この実行を参照できるようになる。耐久性のある関数内ではdf.wait_for_signal()を呼び出してるけど、この呼び出しはこの関数のインスタンス内で一度だけ行われるんだ。重複はないよ。df.start()の呼び出しはタイムアウトして再実行すると重複するかもしれないけど、その場合は別の関数インスタンスが作成されることになる。SQLの実行中に「未処理」のエラーが発生すると、その関数インスタンスは失敗するよ。そのステータスには発生した正確なエラーが表示される。

大手のテック企業を除けば、Postgresだけで十分だと思うよ。メッセージブローカーや分析用データベースもPostgresの上に構築できるし。ただ、今のPostgresは拡張性が全然ないから、実際には無理なんだよね。Rustみたいな別の言語でPostgresを再構築して、プラグイン可能なアプリケーションレイヤーを追加するのがいいと思う。野心的だけど、役に立つし、必要だと思うな。

そういうのがあるって知ってたら嬉しいかも。pgrustってやつ。

今のPostgresは拡張性が全然ない。PostGIS、pgvector、TimescaleDB、Citus、pg_cron、pgmq、Apache AGE、ParadeDB、hstore、plv8、postgres_fdw、pg_partman、pg_stat_statements… 拡張APIが君の主張を可能にしてるんだよ。それを削除しちゃったら、君が求めてる機能を消し去ることになる。

Postgresをあんまり理解してないんじゃないかな。

Postgresには100以上の人気の拡張があるよ。それらはPostgresの内部データ構造に依存してるから、もし誰かがRustでPostgresを再構築して、最初からこれらの拡張をサポートしなかったら、もう終わりだね。

彼らがPGRXを公に使い始めたから、今後スポンサーになってくれるといいな。