当 PostgreSQL 需要承载跨越数小时甚至数天的复杂工作流时,传统的存储过程或外部定时任务往往难以同时满足可靠性、可观测性和故障恢复的要求。Microsoft 为 Azure HorizonDB 开发的 pg_durable 扩展提供了一种新的思路:将持久化执行引擎直接嵌入数据库内核,让长时间运行的任务享有与事务数据同等级别的可靠性保障。
持久化执行的语义边界
pg_durable 的核心承诺是 "每一步都持久化到磁盘"。这意味着一个多步骤工作流在执行过程中,每个完成节点的状态都会被写入 df 和 duroxide 两个系统 Schema 的表中。当数据库因崩溃、重启或计划维护而中断时,引擎能够精确恢复到中断前的执行位置,而非简单地从头重试。
这种设计与传统的事务块有本质区别。普通的 BEGIN ... COMMIT 包裹的存储过程一旦遭遇连接中断,整个事务会回滚,所有中间状态丢失。而 pg_durable 将工作流建模为有向图,节点间的依赖关系和执行状态被显式持久化。已完成节点的输出被保存,待执行节点等待调度,这种细粒度的状态管理使得故障恢复可以精确到 "步骤" 级别,而非 "事务" 级别。
检查点恢复的三层机制
pg_durable 的故障恢复能力建立在三层机制之上。第一层是执行图持久化:工作流提交时,整个执行图被序列化并存储在数据库表中,与业务数据共享同一套持久化基础设施。这意味着工作流状态天然享有 PostgreSQL 的 WAL 保护、备份和 PITR(时间点恢复)能力。
第二层是后台 Worker 的重新附着。pg_durable 通过 shared_preload_libraries 机制启动专用后台进程执行工作流。当数据库重启后,Worker 进程会重新扫描 duroxide Schema 中的执行状态表,识别出正在运行的实例并重新附着。这种设计避免了将执行状态存储在外部协调器中的复杂性,所有状态都在数据库内部完成一致性管理。
第三层是步骤级重试策略。对于失败节点,pg_durable 支持配置重试次数和退避策略,仅重试失败的步骤而不影响已完成的节点。这种 "断点续传" 能力对于 ETL 作业、大规模 Embedding 生成等耗时操作尤为重要 —— 在数百万行的处理任务中,重新执行已完成的 99% 工作是不可接受的。
身份隔离与权限边界
在多租户或权限敏感的环境中,执行身份的隔离是事务边界设计的另一个关键维度。pg_durable 采用 "提交时捕获身份" 的策略:工作流以提交该任务的用户的权限执行,而非以 Worker 进程的权限执行。
这一设计通过捕获 session_user 和 current_user 实现,即使在 SET ROLE 的上下文中提交任务,引擎也会正确识别有效角色。这意味着用户无法通过提交持久化函数来提升自己的权限,多租户工作负载的隔离性依赖于数据库既有的角色和授权模型。
从架构角度看,这种设计消除了外部协调器常见的 "服务账户权限过大" 问题。外部编排工具通常需要配置一个具有广泛权限的服务账户来访问数据库,而 pg_durable 将权限检查下推到 PostgreSQL 的原生机制,每个工作流实例都携带明确的身份上下文。
故障转移与状态一致性
在具备流复制或高可用配置的 PostgreSQL 集群中,pg_durable 的行为遵循主从架构的基本原则。后台 Worker 仅在主库上运行,只读副本可以查询监控视图但不参与执行。当发生故障转移时,新晋升的主库上的 Worker 进程会从 duroxide Schema 中读取执行状态,从最后一个检查点恢复工作流。
这种设计的一个关键优势是状态与数据的一致性。由于执行状态存储在普通表中,它随业务数据一起被复制到备库。故障转移后,工作流的恢复点与数据的恢复点天然一致,不会出现 "数据已恢复但工作流状态丢失" 或相反的不一致情况。
对于需要跨数据库协调的场景,pg_durable 支持通过 df.start(..., database => 'other_db') 语法向其他数据库发起调用,但执行状态的持久化仍由目标数据库的引擎负责。这种设计保持了故障恢复边界的清晰性 —— 每个数据库独立管理自己的工作流状态。
工程实践中的边界考量
尽管 pg_durable 提供了强大的持久化能力,工程师仍需注意其边界。首先是幂等性约束:引擎保证不会重复执行已完成的步骤,但对于调用外部 API 的步骤,如果 API 调用在发送后、确认前发生中断,仍可能导致重复调用。这类操作需要应用层通过幂等键等机制自行保障。
其次是升级兼容性。在预览阶段,pg_durable 的函数定义和执行状态不保证跨主版本兼容。升级前需要排空或取消运行中的实例,否则可能面临状态不可恢复的风险。
最后是监控与可观测性。pg_durable 将所有执行信息暴露为 SQL 可查询的视图,包括实例状态、执行历史、节点详情和系统指标。通过查询 df._worker_epoch 表可以确认 Worker 进程的健康状态 —— 心跳间隔超过 15 秒通常意味着 Worker 异常。
结语
pg_durable 代表了数据库引擎向 "自包含工作流平台" 演进的一个方向。通过将持久化执行状态纳入数据库的事务边界,它消除了外部协调器带来的网络故障面和状态同步复杂性。对于以 PostgreSQL 为核心数据存储的应用而言,这种架构简化了长时间运行任务的可靠性设计,使得 ETL、AI 管道、定时维护等场景能够在不引入额外基础设施的情况下获得企业级的故障恢复能力。
参考来源
- Microsoft Learn: Durable Functions in Azure HorizonDB
- GitHub: microsoft/pg_durable
- GitHub: microsoft/duroxide (Rust 持久化执行运行时)
内容声明:本文无广告投放、无付费植入。
如有事实性问题,欢迎发送勘误至 i@hotdrydog.com。