Hotdry.

Article

pg_durable 在 Serverless 函数编排中的事务性持久化执行与状态机设计

深入解析微软 pg_durable 扩展如何在 PostgreSQL 内实现事务性持久化执行,为 Serverless 函数编排提供零外部依赖的状态机设计与故障恢复机制。

2026-06-08systems

Serverless 架构的函数编排长期面临一个核心矛盾:计算单元的无状态特性与业务流程的有状态需求之间的冲突。当函数链中的某个环节因超时、内存限制或底层节点故障而中断时,传统方案依赖外部编排器(如 AWS Step Functions、Temporal 或 Airflow)来持久化状态并协调重试。这种架构不仅引入额外的运维复杂度,还造成数据与编排逻辑的分层割裂。微软开源的 pg_durable 扩展提供了一种截然不同的思路 —— 将持久化执行引擎直接嵌入 PostgreSQL,利用数据库的原生事务机制实现 Serverless 函数编排的可靠性保障。

Serverless 编排的持久化挑战

在典型的 Serverless 函数编排场景中,一个工作流可能包含多个阶段:数据抽取、外部 API 调用、结果聚合、状态更新。每个阶段都可能因以下原因失败:

  • 执行中断:函数超时或容器回收导致任务中途停止
  • 部分失败:批量处理中部分记录成功、部分失败,需要精细的重试策略
  • 状态漂移:应用层维护的进度表与实际情况不一致,重启后难以确定恢复点
  • 锁竞争:长时间事务持有行锁,影响并发性能

传统解决方案通常采用 "队列 + 工作进程 + 状态表" 的三层架构,但这意味着需要维护消息队列(如 Redis、RabbitMQ)、调度器和持久化存储之间的数据一致性。pg_durable 的核心洞察在于:如果业务数据已经存储在 PostgreSQL 中,那么工作流的状态、进度和重试逻辑也应该在同一事务边界内管理。

状态机设计:SQL DSL 与执行图

pg_durable 使用一套声明式的 SQL DSL 来定义工作流图。两个核心运算符构成了状态机的基础:

  • ~>(波浪箭头):顺序执行,前一个步骤的输出作为后一个步骤的输入
  • |=>(管道箭头):并行扇出,将输入集合分发给多个独立执行的分支

这种设计使得复杂的工作流可以用纯 SQL 表达。例如,一个典型的文档处理流水线可以定义为:

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

在这个例子中,首先并行查询待处理的文档批次(|=> 'batch'),然后顺序执行批量更新(~>)。pg_durable 的运行时会将每个步骤的执行状态持久化到 df.instancesdf.nodes 系统表中,形成可查询、可审计的执行历史。

从状态机角度看,每个工作流实例都是一个有限状态机,节点代表执行步骤,边代表状态转换。pg_durable 的后台工作器(background worker)负责驱动状态转换,在步骤完成后自动提交检查点。这种设计与 Temporal 的 "事件溯源" 模式类似,但完全基于 PostgreSQL 的存储引擎实现,无需外部服务。

事务性持久化执行机制

pg_durable 的可靠性保证建立在 PostgreSQL 的 ACID 事务和 WAL(Write-Ahead Logging)机制之上:

检查点持久化:每个工作流步骤完成后,运行时会将状态变更写入 duroxide.* 模式下的内部表,这些变更与业务数据在同一事务中提交。如果数据库在步骤执行期间崩溃,重启后 WAL 回放会确保已完成的步骤不会重复执行。

确定性重放:基于 duroxide 运行时的设计,工作流的执行是确定性的 —— 给定相同的输入和历史状态,执行路径总是可重现的。这使得故障恢复后可以从最后一个成功检查点继续执行,而非从头开始。

行级安全隔离:pg_durable 通过 PostgreSQL 的 RLS(Row-Level Security)实现多租户隔离。每个用户只能查看和管理自己提交的工作流实例,df.vars 中的变量也按用户作用域隔离。这种设计特别适合多租户的 Serverless 平台场景。

权限模型:扩展安装后不会自动授予 PUBLIC 权限,管理员需要通过 df.grant_usage('app_role') 显式授权。后台工作器角色(默认 postgres)需要超级用户权限以绕过 RLS 管理所有实例,而应用角色仅获得最小必要权限。

Serverless 场景下的落地参数

在评估 pg_durable 是否适合特定的 Serverless 编排场景时,需要考虑以下参数:

适用场景

  • ETL 管道:数据抽取、转换、加载的阶段性处理
  • 嵌入向量流水线:文档分块、调用 Embedding API、写入 pgvector
  • 外部 API 工作流:数据丰富、分类、Webhook 回调
  • 定时维护任务:膨胀检测、通知、等待审批的复杂流程
  • 扇出聚合:独立查询并行执行后合并结果

不适用场景

  • 亚毫秒级同步请求处理(durable execution 的持久化开销在毫秒级)
  • 单条 SQL 语句即可完成的简单操作
  • 无法安装扩展或运行后台工作器的托管数据库环境
  • 需要任意应用逻辑且无法映射为 SQL 步骤的场景

部署 checklist

  1. 确认 PostgreSQL 版本支持(17 或 18)
  2. pg_durable 加入 shared_preload_libraries 并重启
  3. 执行 CREATE EXTENSION pg_durable
  4. 配置后台工作器角色权限
  5. 为应用角色授予 df.grant_usage()
  6. 设置监控:查询 df.instances 表获取工作流状态

架构权衡与风险边界

pg_durable 的 "计算贴近数据" 理念带来了架构简化的优势,但也存在明确的边界:

运维收益:消除了外部编排器的部署、监控和故障排查成本,工作流状态与业务数据共享备份和恢复策略,审计日志天然统一。

性能考量:检查点的持久化写入增加了单步骤的延迟,不适合高频、低延迟的同步调用场景。对于长时运行的后台任务,这种开销可以忽略不计。

扩展限制:如果工作流步骤需要调用非 HTTP SDK 或复杂的内存内控制流,需要将这些逻辑封装为 SQL 函数或 HTTP 端点。pg_durable 的设计 intentionally SQL-shaped,不支持任意的应用代码嵌入。

锁定风险:虽然 pg_durable 通过短事务和检查点机制避免了长时间持有锁,但在高并发场景下仍需关注 df.instancesdf.nodes 表的写入热点。

结语

pg_durable 代表了数据库能力边界扩展的一个新方向:将原本需要外部服务的编排能力下沉到存储层。对于已经深度使用 PostgreSQL 的团队而言,这种 "零额外基础设施" 的方案可以显著简化 Serverless 函数编排的架构复杂度。通过将状态机、检查点和故障恢复机制内嵌到数据库事务中,pg_durable 实现了计算与数据的紧密耦合 —— 这正是微软 HorizonDB 等云原生 PostgreSQL 服务将其作为内置功能的原因。在评估引入此技术时,关键在于准确识别工作流的持久化需求与延迟敏感度,在架构简化与性能约束之间找到平衡点。


资料来源

systems

内容声明:本文无广告投放、无付费植入。

如有事实性问题,欢迎发送勘误至 i@hotdrydog.com