PostgreSQLで再起動後もバックグラウンドタスクを完走させる方法
このようなシナリオを想像してみてください:複雑なデータ処理ワークフローを書いているとします。レコードを取得し、外部APIに送信し、データベースのステータスを更新し、レポート生成を開始する必要があります。データベースが「クラッシュ」したり、サーバーが処理の途中で再起動したりした場合、标准的なPL/pgSQLプロシージャは単に中止されます。結果は「宙吊り」になったデータと頭痛の種です:どのステージで全てが止まったのか、どうやって安全に再開するのか?
通常、私たちはこれで回避策を使います。pg_cronを設定し、ジョブキューを持つテーブルを作成し、PythonやGoでデータベースを постоянноポーリングするワーカーを書きます。最悪の場合、TemporalやAirflowのような重量級怪物プロジェクトに引きずり込みます。しかし、Microsoftのエンジニアたちは这一切れが不要複雑なものであり、pg_durable拡張機能をリリースしました。
なぜ開発者に必要なのか
このプロジェクトの主なアイデアは、PostgreSQLに障害に対して回復力のある長時間実行関数を実行する能力を与えることです。作成者の用語では、これは「Durable Execution(永続的実行)」と呼ばれます。
ワークフローがpg_durableを通じて定義されている場合、データベースは各ステップ後に状態を保存します。重いクエリやAPI呼び出しの実行中にサーバーが再起動しても、拡張機能は最後の成功したチェックポイントからタスクを再開します。cronジョブ、ワーカー、ステータステーブルを糊付けする必要はもうありません。
内部でどのように動作するか
このプロジェクトはpgrxを使用してRustで書かれています。アーキテクチャ的には、ラッパーだけでなく、データベース内の本格的な実行環境です。いくつかのレイヤーで構成されています:
- SQL DSL:タスクグラフを記述するための演算子のセット。
- Background Worker:Postgres内のバックグラウンドプロセスで、実行を管理します。
- Duroxide:決定論的リプレイとチェックポイントを処理するオーケストレーションエンジン(これもMicrosoftの開発)。
興味深いことに、作成者は「SQLネイティブ」アプローチを選択しました。コンソールやマイグレーションで、~>や|=>のような特別な演算子を使ってロジックを記述します。
主な機能
生活を本当に簡単にする3つのことがあります。
外部サービスなしのフォールトトレランス
キューにRedisや個別のTemporalインスタンスは不要です。全てはdf.*とduroxide.*テーブル内に存在します。データと制御ロジックは同じトランザクション環境にあります。これにより、タスクがキューに存在하지만データベースの変更がまだコミットされていないという古典的な分散システムの問題が解消されます。
並列実行とマージ
演算子を使用すると、タスク実行を簡単に複数の並列ストリームに「フォーク」し、その後完了を待つことができます。READMEには明確な例があります:ユーザー、注文、収益を同時にカウントし、その後全てを1つのレポートステップに統合します。
外部システムとの統合
拡張機能にはdf.http()関数があります。これは、外部のマイクロサービスやニューラルネットワークAPIを長時間実行プロセスから直接呼び出すことができることを意味します。APIが500エラーを返した場合、pg_durableはデータベース全体の動作をブロックせずに待機してリトライできます。
コード例
SQLで直接シンプルなタスクを作成するとこのような感じになります:
-- Запускаем процесс: берем 100 необработанных документов и обновляем их статус
SELECT df.start(
'SELECT id FROM documents WHERE processed = false LIMIT 100' |=> 'batch'
~> 'UPDATE documents SET processed = true WHERE id = ANY($batch)'
);
このコードは、実行が保証されたWorkflowインスタンスを作成します。UPDATEが失敗した場合、システムはどのbatchのデータを処理しようとしていたかを正確に把握します。
どこで役立つか
pg_durableが時間を大幅に節約するシナリオがいくつかあります。
まず、AIパイプラインです。何千もの行をエンベディングに通してpgvectorに保存する必要がある場合、これは理想的なツールです。テキストのチャンキング、OpenAI API呼び出し、データベースへのアップサートが1つの信頼性の高いパイプラインにパッケージされます。
次に、大規模データ処理(ETL)です。WALが不足したときに倒下する怪物のようなPL/pgSQLプロシージャを書く代わりに、チェックポイント付きの小さなステップに作業を分解できます。
3番目に、管理タスクの自動化です。例えば、テーブルブロートのチェック、通知の送信、承認の待機など、これら全てをDurable Functionとして記述できます。
ニュアンスと制限事項
このプロジェクトはPreviewステータスです。これは本番環境に投入するにはまだ早すぎるということですが、内部ツールには最適です。
重要な制限事項:PostgreSQL 17または18が必要です。古いバージョンの場合はアップグレードする必要があります。もう1つの点はセキュリティです。デフォルトでは、shared_preload_libraries内のすべての関数のセットアップにはスーパーユーザー権限が必要ですが、開発者は通常のロール用にdf.grant_usage() 통한権限システムを用意しています。
このシステムはSQLに最適化されています。PythonやNode.jsでトリッキーなループを持つ複雑なビジネスロジックが必要な場合は、アプリケーションコードから直接Duroxideエンジンを使用する方が良いでしょう。pg_durableはspecificallyデータをできるだけ近くに保つことを目的にしています。
MicrosoftはPostgresに積極的に投資しており(Citusの買収を思い出してください)、pg_durableはデータベースを本格的なアプリケーションプラットフォームに変えるためのもう1つのステップです。バックグラウンドタスクが、最も不便な瞬間に「ドロップオフ」するのが面倒,或者はシンプルなパイプラインのために外部オーケストレータを設定するのにうんざりしているなら、ぜひこのリポジトリをチェックしてください。始めるには、シンプルなDockerコンテナやCodespacesで十分で、どちらもリポジトリのDevelopmentタブで構成済みです。
関連プロジェクト