pg_durable 深度教學 — PostgreSQL 內建持久化執行引擎

§1 專案定位與解決的問題

1.1 pg_durable 是什麼

pg_durable 是 Microsoft 推出的 PostgreSQL 擴充套件(extension),以 Rust 語言(透過 pgrx 框架)實作,將 durable execution(持久化執行) 模式直接嵌入 PostgreSQL 資料庫內部。

durable execution 已是業界標準模式(Temporal、Azure Durable Functions、AWS Step Functions 皆為此類),pg_durable 的獨特價值在於:不需要任何外部服務——沒有 Redis、沒有 message queue、沒有獨立的 orchestrator——所有 workflow 狀態直接存在 PostgreSQL 內部表中。

1.2 它解決什麼痛點

目前許多團隊處理長時間背景工作的方式:

  1. pg_cron + jobs table + status columns + polling worker:需要自己管理重試、部分完成、狀態追蹤
  2. 外部 orchestrator(Airflow / Temporal / Step Functions)回呼 Postgres:增加基礎設施複雜度
  3. Queue + workers + 獨立 state table:retry 和 partial failure 邏輯散落各處
  4. plpgsql procedure:crash 後必須從頭重跑

pg_durable 將 workflow 定義、checkpoint、retry state、progress tracking 全部收進 PostgreSQL,消除上述所有拼接層。

1.3 核心設計理念

  • Compute close to data:計算與資料同位,減少網路跳躍
  • SQL-native:用 SQL 運算子組合工作流,不需學新語言
  • Zero infrastructure overhead:一個 extension 取代整套 orchestration stack
  • Exactly-once semantics via checkpoint:每個步驟完成後持久化,crash 後從上次成功處繼續

1.4 關鍵數據

指標
GitHub Stars1,448
Forks32
主要語言Rust(透過 pgrx)
授權PostgreSQL License
支援 PG 版本17, 18
首次發佈2026-02-13
最後活動2026-06-06
相關雲服務Azure HorizonDB

§2 核心概念

2.1 Durofut(Durable Future)

pg_durable 的核心資料結構是 Durofut——一個 JSON 格式的 function graph 節點。每個 Durofut 包含:

  • node_type:節點類型(SQL / THEN / IF / JOIN / LOOP / HTTP 等)
  • left_node / right_node:子節點(形成 DAG)
  • query:SQL 查詢或設定 JSON
  • result_name:具名結果(用於跨步驟傳遞資料)

2.2 兩階段架構

Phase 1 — Graph Construction(同步、在使用者 transaction 內)

  • 使用者呼叫 df.start(...) 時,DSL 運算子將 SQL 表達式組合為 Durofut JSON tree
  • 系統將 graph 拆解為 nodes,寫入 duroxide.instancesduroxide.nodes
  • 回傳 instance_id

Phase 2 — Orchestration Execution(非同步、background worker)

  • Background worker 從 orchestrator_queue 取出任務
  • 使用 lock_token 機制確保 exactly-once 語意
  • 逐節點執行:SQL query → checkpoint → 下一個節點
  • 支援平行 fan-out、條件分支、迴圈、HTTP 呼叫

2.3 DSL 運算子

運算子意義範例
~>Then(序列)'SELECT 1' ~> 'SELECT 2'
|=>Named result(具名輸出)'SELECT id FROM t' |=> 'batch'
|~>Fan-out(平行分支)step1 |~> step2 |~> step3
df.join(...)Fan-in(等待全部完成)df.join(a, b, c)
df.race(...)Fan-in(等待任一完成)df.race(a, b)
df.when(...)條件分支df.when('cond', then_step, else_step)
df.loop(...)迴圈df.loop(body, 'exit_condition')
df.http(...)外部 HTTP 呼叫df.http('https://api.example.com', 'POST')
df.wait(...)暫停等待df.wait('1 hour')

§3 系統架構圖


graph TD
    subgraph UserSession["使用者 Session(Phase 1)"]
        A["SQL: df.start(...)"] --> B["DSL 運算子
~> |=> |~> join race when loop"] B --> C["Durofut JSON Tree
(function graph)"] C --> D["寫入 duroxide.instances
+ duroxide.nodes"] D --> E["回傳 instance_id"] end subgraph BGWorker["Background Worker(Phase 2)"] F["orchestrator_queue
取出任務"] --> G["lock_token 鎖定
(exactly-once)"] G --> H{"節點類型?"} H -->|SQL| I["execute_sql
(SPI 執行)"] H -->|HTTP| J["df.http()
(SSRF 防護)"] H -->|JOIN/RACE| K["平行 fan-out
→ fan-in"] H -->|IF/WHEN| L["條件分支"] H -->|LOOP| M["迴圈"] I --> N["Checkpoint
持久化狀態"] J --> N K --> N L --> N M --> N N --> O{"還有下一個
節點?"} O -->|Yes| F O -->|No| P["標記 Completed
寫入 df.instances"] end D -.->|enqueue| F subgraph Monitoring["監控與查詢"] Q["df.instances — 查詢狀態"] R["df.metrics() — 執行統計"] S["df.explain() — 視覺化 plan"] end P -.-> Q

graph LR
    subgraph CoreModules["Rust 核心模組"]
        lib["lib.rs
PG extension 入口
GUC 設定
2546 行"] dsl["dsl.rs
DSL 運算子實作
1090 行"] types["types.rs
Durofut 結構
變數替換
1436 行"] worker["worker.rs
Background worker
604 行"] ssrf["ssrf.rs
SSRF IP blocklist
domain allowlist
1007 行"] monitoring["monitoring.rs
df.metrics()
477 行"] explain["explain.rs
df.explain()
746 行"] exec["execute_function_graph.rs
核心執行引擎
1040 行"] end lib --> dsl lib --> worker dsl --> types worker --> exec exec --> types exec --> ssrf lib --> monitoring lib --> explain

§4 安裝與環境設定

4.1 從 Debian 套件安裝(推薦)

Tagged releases 在 GitHub release assets 提供 .deb 套件:

1# 下載 .deb(以 PG 17 為例)
2wget https://github.com/microsoft/pg_durable/releases/download/v0.2.2/pg-durable-postgresql-17_0.2.2-1_amd64.deb
3
4# 安裝
5sudo dpkg -i pg-durable-postgresql-17_0.2.2-1_amd64.deb

設定 postgresql.conf

1shared_preload_libraries = 'pg_durable'

重啟 PostgreSQL,然後在目標資料庫建立 extension:

1CREATE EXTENSION pg_durable;

4.2 從原始碼編譯

前置需求:

  • PostgreSQL 17 或 18(含 dev headers)
  • Rust toolchain(rustup)
  • pgrx CLI
1# 安裝 pgrx
2cargo install --locked cargo-pgrx
3cargo pgrx init --pg17 $(which pg_config)
4
5# 編譯與安裝
6cargo pgrx install --release

4.3 Docker 開發環境

1# 使用 docker-compose
2docker-compose up -d
3
4# 或使用 devcontainer(VS Code / GitHub Codespaces)
5# .devcontainer/devcontainer.json 已預設好

4.4 Azure HorizonDB

pg_durable 已內建於 Azure HorizonDB,無需安裝:

1https://aka.ms/AzureHorizonDB

§5 使用方式與範例

5.1 基本序列工作流

1-- 三步驟序列執行:建立 temp table → 插入資料 → 查詢
2SELECT df.start(
3    'CREATE TEMP TABLE IF NOT EXISTS demo(id int, val text)'
4    ~> $$INSERT INTO demo VALUES (1, 'hello'), (2, 'world')$$
5    ~> 'SELECT count(*) FROM demo'
6);

5.2 具名結果與變數傳遞

1-- |=> 運算子將查詢結果命名,後續步驟用 $name 引用
2SELECT df.start(
3    'SELECT id FROM documents WHERE processed = false LIMIT 100' |=> 'batch'
4    ~> 'UPDATE documents SET processed = true WHERE id = ANY($batch)'
5);

5.3 平行 Fan-out / Fan-in

 1-- 三個查詢平行執行,全部完成後 join
 2SELECT df.start(
 3    df.join(
 4        'SELECT count(*) FROM users'   |=> 'user_count',
 5        'SELECT count(*) FROM orders'  |=> 'order_count',
 6        'SELECT sum(revenue) FROM sales' |=> 'total_revenue'
 7    )
 8    ~> $$SELECT format('Users: %s, Orders: %s, Revenue: %s',
 9                       $user_count, $order_count, $total_revenue)$$
10);

5.4 條件分支

1SELECT df.start(
2    'SELECT count(*) > 1000 FROM orders' |=> 'is_large'
3    ~> df.when(
4        '$is_large = true',
5        'SELECT run_batch_process()',   -- then
6        'SELECT run_simple_process()'   -- else
7    )
8);

5.5 外部 HTTP 呼叫(需啟用 http feature)

 1-- 呼叫 embedding API
 2SELECT df.start(
 3    'SELECT content FROM documents WHERE id = 42' |=> 'text'
 4    ~> df.http(
 5        'https://api.openai.com/v1/embeddings',
 6        'POST',
 7        '{"input": "$text", "model": "text-embedding-3-small"}',
 8        '{"Authorization": "Bearer ${OPENAI_KEY}"}'::jsonb
 9    ) |=> 'embedding'
10    ~> $$UPDATE documents SET embedding = '$embedding'::vector WHERE id = 42$$
11);

5.6 查詢執行狀態

 1-- 查詢所有 instance 的狀態
 2SELECT * FROM df.instances;
 3
 4-- 查詢特定 instance
 5SELECT * FROM df.instances WHERE instance_id = 'xxx';
 6
 7-- 取得執行統計
 8SELECT * FROM df.metrics();
 9
10-- 視覺化執行 plan
11SELECT df.explain('instance_id_here');

§6 資安掃描報告

🟢 整體評等:綠燈(安全設計良好)

pg_durable 在安全方面展現了高品質的工程實踐:

6.1 SSRF 防護(強)

項目評估
IP blocklist🟢 硬編碼於 ssrf.rs,涵蓋 RFC 1918 / loopback / link-local / IPv6 保留地址
Domain allowlist🟢 編譯時期決定,超級使用者也無法繞過
預設行為🟢 無 feature flag 時,所有 HTTP 呼叫被阻擋
Redirect 防護🟢 阻擋重導向(防止 SSRF bypass)

ssrf.rs 提供四級安全模式:

  • (預設)全部阻擋:df.http() 在 DSL 時期就報錯
  • http-allow-azure-domains:只允許 Azure 網域
  • http-allow-test-domains:加上 github.com + httpbingo.org(測試用)
  • http-allow-all:開發用,禁止用於生產

6.2 SQL 注入防護(強)

項目評估
SPI 參數化🟢 全面使用 Spi::run_with_args() 做參數綁定
search_path 硬化🟢 v0.2.0 升級腳本將所有 PL/pgSQL 函數加上 SET search_path = pg_catalog, df, pg_temp
Schema 隔離🟢 使用獨立的 df schema(公開 API)和 duroxide schema(內部)

6.3 權限模型(良好)

項目評估
SECURITY INVOKER🟢 所有 df.* 函數預設 SECURITY INVOKER
df.http() 權限🟢 v0.2.0+ 預設不授權給 PUBLIC,需明確 GRANT
RLS 支援🟢 有完整的 E2E 測試(15_rls.sql
使用者隔離🟢 有完整的 E2E 測試(13_user_isolation.sql
Delegated grants🟢 有明確的權限委派測試(18_delegated_grants.sql

6.4 Unsafe Rust 使用(可接受)

項目評估
unsafe 區塊數量🟡 約 10 處,全部在 pgrx FFI 呼叫上(GetUserId、PostPortNumber、process_shared_preload_libraries_in_progress)
評估🟢 這些是 pgrx 框架與 PostgreSQL C API 互動的標準做法,非自創 unsafe 邏輯

6.5 機密管理(適當)

項目評估
.env.example🟢 僅含 ACR registry 設定,無真實 secret
硬編碼 credential🟢 未發現任何硬編碼密碼或 API key
build.rs🟡 使用 Command::new("date") 取得編譯時間戳——無安全風險

6.6 已知安全議題(Open Issues)

  • #185SECURITY DEFINERdf.start() 的互動需要更好的文件說明,以及新增 start_use_session_user GUC
  • #214df.http() allowlist 不管控其他 SQL extension 的網路行為(可能造成混淆)

6.7 結論

pg_durable 的安全設計是 PostgreSQL extension 領域中的優秀範例。SSRF 防護(編譯時鎖定、superuser 也無法繞過)和 SQL 注入防護(全面參數化 + search_path 硬化)都遠超一般水準。少量 unsafe Rust 皆為 pgrx FFI 標準用法,非安全疑慮。


§7 程式碼結構導覽

7.1 檔案統計

類型檔案數說明
Rust (.rs)19核心邏輯
SQL (.sql)69Schema migration + E2E 測試
Shell (.sh)14測試腳本、CI 工具
總檔案數235
核心 Rust 程式碼~9,000 行src/ 下所有 .rs
SQL 測試~6,000 行tests/e2e/sql/

7.2 關鍵目錄

 1pg_durable/
 2├── src/                          # Rust 核心
 3│   ├── lib.rs                    # PG extension 入口、GUC、SQL function 定義
 4│   ├── dsl.rs                    # DSL 運算子(~>, |=>, join, race, when, loop)
 5│   ├── types.rs                  # Durofut 結構、變數替換引擎
 6│   ├── worker.rs                 # Background worker 生命週期
 7│   ├── ssrf.rs                   # SSRF 防護(IP blocklist + domain allowlist)
 8│   ├── monitoring.rs             # df.metrics() 實作
 9│   ├── explain.rs                # df.explain() 視覺化
10│   ├── client.rs                 # HTTP client
11│   ├── registry.rs               # Function registry
12│   ├── activities/               # Activity 執行器
13│   │   └── execute_sql.rs        # SQL 節點執行
14│   └── orchestrations/           # Orchestration 引擎
15│       └── execute_function_graph.rs  # 核心 graph traversal
16├── sql/                          # PostgreSQL schema + migration
17│   ├── pg_durable--0.1.1.sql     # 初始 schema(3729 行)
18│   ├── pg_durable--0.1.1--0.2.0.sql  # 權限硬化升級
19│   ├── pg_durable--0.2.0--0.2.1.sql
20│   └── pg_durable--0.2.1--0.2.2.sql
21├── tests/e2e/sql/                # E2E SQL 測試(21+ 檔案)
22├── scripts/                      # 測試、部署、CI 腳本
23├── examples/                     # 使用範例
24│   ├── azure-functions/          # 搭配 Azure Functions
25│   ├── azure-http-domains/       # Azure 服務整合
26│   ├── invoice-approval/         # 發票審批工作流
27│   └── operational-scenarios/    # 運維場景
28├── docs/                         # 技術文件
29│   ├── ARCHITECTURE.md           # 架構深度說明
30│   └── api-reference.md          # API 參考
31└── prompts/                      # AI agent prompts(merge / release / test 等)

§8 與同類工具的比較

特性pg_durableTemporalAirflowpg_cron + DIY
部署複雜度🟢 單一 extension🔴 獨立叢集🔴 獨立服務🟡 需自建
語言SQLGo/Java/PythonPython DAGSQL + app code
Checkpoint 機制🟢 自動🟢 自動🟡 task 級別🔴 手動
資料位置🟢 同 DB🔴 外部🔴 外部🟢 同 DB
Crash recovery🟢 自動🟢 自動🟡 task 重試🔴 手動
平行執行🟢 原生🟢 原生🟢 原生🔴 手動
HTTP 呼叫🟡 需 feature flag🟢 任意🟢 任意🟢 任意
適用規模中小型大型大型小型
跨系統編排🔴 SQL 為主🟢 任意🟢 任意🔴 有限

結論:pg_durable 的 sweet spot 是「資料已在 Postgres、workflow 可用 SQL 表達、不想維護額外基礎設施」的團隊。若 workflow 主要跨多個異質系統,Temporal 或 Airflow 仍較適合。


§9 實戰建議與最佳實踐

9.1 何時使用 pg_durable

  • 資料已在 PostgreSQL,workflow 步驟主要是 SQL 操作
  • 需要 crash-safe 的批次處理或 ETL pipeline
  • 團隊規模較小,不想維護 Temporal / Airflow 叢集
  • AI/ML pipeline 需要 per-row / per-document 的 durable execution

9.2 設計原則

  1. 步驟粒度適中:每個步驟應該是一個有意義的 checkpoint 點,太細會增加 overhead,太粗會降低恢復精度
  2. 善用 named results|=> 運算子讓資料在步驟間流動,避免 temp table 污染
  3. 平行 fan-out 要收斂|~> 必須搭配 df.join()df.race() 收斂
  4. HTTP 呼叫要考慮冪等性:步驟重試時,外部 API 呼叫應該是冪等的

9.3 監控

1-- 定期查看 metrics
2SELECT * FROM df.metrics();
3
4-- 找出失敗的 instances
5SELECT * FROM df.instances WHERE status = 'failed';
6
7-- 視覺化特定執行的 plan
8SELECT df.explain('instance_id');

9.4 版本升級

1-- 升級 extension(例如 0.2.1 → 0.2.2)
2ALTER EXTENSION pg_durable UPDATE TO '0.2.2';

升級腳本會自動執行 schema migration,包括安全硬化(search_path、權限調整)。


§10 開發與測試

10.1 本地開發環境

 1# Clone repo
 2git clone https://github.com/microsoft/pg_durable.git
 3cd pg_durable
 4
 5# 使用 devcontainer(推薦)
 6# VS Code: Reopen in Container
 7
 8# 或手動設定
 9cargo install --locked cargo-pgrx
10cargo pgrx init --pg17 $(which pg_config)

10.2 測試

 1# 全部測試
 2bash scripts/test-all-local.sh
 3
 4# 單元測試
 5bash scripts/test-unit.sh
 6
 7# E2E 測試(Docker)
 8bash scripts/test-e2e-docker.sh
 9
10# E2E 測試(本地)
11bash scripts/test-e2e-local.sh
12
13# 升級測試
14bash scripts/test-upgrade.sh
15
16# 覆蓋率報告
17bash scripts/test-coverage.sh
18
19# SQL 靜態分析(pgspot)
20bash scripts/run-pgspot.sh

10.3 E2E 測試涵蓋範圍

測試檔案涵蓋內容
01_core_primitives.sql基本序列、平行、named results
02_conditionals.sqlIF/WHEN 條件分支
03_loops.sqlLOOP 迴圈
04_variables_and_results.sql變數替換、結果傳遞
06_http_and_ssrf.sqlHTTP 呼叫、SSRF 防護
07_signals.sqlSignal 機制
11_cross_connection.sql跨連線行為
12_extension_lifecycle.sqlExtension 安裝/升級/移除
13_user_isolation.sql使用者隔離、SECURITY DEFINER
14_database.sql多資料庫
15_rls.sqlRow-Level Security
17_superuser_guc.sqlSuperuser GUC 設定
18_delegated_grants.sql權限委派
21_typed_results.sql型別化結果
47_http_dsl_disabled.sqlHTTP 功能停用時的行為

§11 未來發展與社群

11.1 近期發展方向(從 Issues 觀察)

  • pgrx 升級(#209):升級到 pgrx v0.18.1 以支援更新的 PG 版本
  • Schema 重組(#201, #202):將 duroxide schema 和 operators 整理到更清晰的命名空間
  • 安全強化(#185):完善 SECURITY DEFINER 互動文件、新增 start_use_session_user GUC
  • HTTP allowlist 文件(#214):釐清 df.http() allowlist 的適用範圍

11.2 Azure HorizonDB 整合

pg_durable 是 Microsoft 新推出的 Azure HorizonDB(PostgreSQL 雲服務)的核心元件之一。這代表 pg_durable 有 Microsoft 的長期維護承諾和企業級使用場景背書。

11.3 貢獻指南

  • 遵循 CONTRIBUTING.md 和 Microsoft CLA
  • 使用 prompts/ 目錄下的 AI agent prompts 輔助開發
  • PR 前跑 bash scripts/test-all-local.sh + bash scripts/run-pgspot.sh

11.4 適合的應用場景展望

  • Biomedical data pipeline:基因定序資料批次處理,per-sample durable execution
  • 金融 ETL:交易結算、對帳、報表生成的 crash-safe pipeline
  • IoT 資料處理:感測器資料 ingest → transform → aggregate → alert
  • AI/ML embedding pipeline:搭配 pgvector,chunk → embed → upsert 全程 durable

文件產生日期:2026-06-08 來源:https://github.com/microsoft/pg_durable 授權:PostgreSQL License INTERNAL ONLY