背景:数据密集型任务的可靠性困境
在数据管道和后台任务处理中,一个长期存在的矛盾是:状态存储在 PostgreSQL 中,但任务编排却依赖外部系统。团队通常需要组合 pg_cron、任务队列、状态表和轮询 Worker 来实现可靠的背景作业。当数据库崩溃或进程重启时,已经完成的步骤需要重新执行,失败行的清理和重放逻辑散落在应用代码各处。
pg_durable 的核心思路是将持久化执行(Durable Execution)这一业界标准模式引入数据库内部。工作流以 SQL 形式定义,PostgreSQL 在执行过程中自动进行 checkpoint,崩溃后从最后一个成功步骤恢复,无需外部服务参与。
核心机制:Checkpoint 与确定性重放
pg_durable 将函数建模为 SQL 步骤构成的有向图。每个步骤执行完毕后,运行时将状态持久化到 PostgreSQL 表中。如果发生崩溃、重启或步骤失败,执行从最后一个 checkpoint 自动恢复,而非从头开始。
DSL 使用两个核心操作符定义流程:
|=> 'name':为步骤命名,便于后续引用~>:顺序执行,前一个步骤的输出作为后一个步骤的输入
并行执行通过 df.join() 实现,条件分支和循环也有对应的原语。整个工作流以 df.start() 启动,返回实例 ID 供状态查询和生命周期管理。
架构解析:扩展、运行时与存储
pg_durable 作为 PostgreSQL 扩展(基于 pgrx 构建),完全在数据库进程内部运行,不依赖 Redis、Temporal 等外部服务。其架构分为三层:
SQL DSL 层:提供 df.* 模式下的函数和表,用户在此定义工作流图、启动实例、查询状态。
后台 Worker:PostgreSQL 后台进程托管 duroxide 运行时(Rust 实现),负责任务编排、定时器和子工作流管理。
状态存储层:duroxide-pg 库将运行时状态持久化到 duroxide.* 模式,包括实例状态、执行历史和工作队列。
这种架构的优势在于计算贴近数据:工作流直接操作 PostgreSQL 表,无需跨网络序列化状态,事务边界天然与数据库一致。
典型应用场景
pg_durable 特别适合以下工作负载:
向量嵌入管道:文档分块、调用嵌入 API、写入 pgvector,每个阶段都可独立重试。
数据摄取管道:Staging、去重、转换、发布的批量处理,单条记录失败不影响整个批次。
扇出聚合:并行执行多个独立查询,结果汇聚后继续下游处理。
外部 API 工作流:数据富化、分类、Webhook 调用等需要可靠重试的异步操作。
定时维护任务:检测表膨胀、发送通知、等待人工确认后执行下一步。
部署与权限配置
pg_durable 要求 PostgreSQL 17 或 18,通过 Debian 包或源码编译安装。安装后需将 pg_durable 加入 shared_preload_libraries 并重启数据库。
权限模型采用行级安全(RLS):
- 普通用户通过
df.grant_usage('role')获得访问权限 - 用户只能查看和管理自己提交的工作流实例
- 后台 Worker 角色(默认
postgres)需为超级用户,以绕过 RLS 管理所有实例
变量存储在 df.vars 中,按用户隔离作用域,避免跨用户数据泄露。
局限性与选型建议
pg_durable 的设计 intentionally SQL-shaped,以下场景可能不适用:
- 单条
INSERT ... SELECT可完成的简单操作 - 需要亚毫秒级同步响应的在线请求
- 无法安装扩展或运行后台 Worker 的受限环境
- 跨多异构系统的复杂编排
- 无法映射为 SQL 步骤、HTTP 调用或简单控制流的任意业务逻辑
对于复杂编排需求,可考虑将 pg_durable 与 duroxide/duroxide-pg 库直接集成,在 Rust、Python 或 Node 中编写工作流逻辑,同时复用 PostgreSQL 作为状态存储后端。
总结
pg_durable 代表了 "计算贴近数据" 理念在事务型数据库中的实践。通过将持久化执行引擎嵌入 PostgreSQL,它消除了外部编排服务的运维负担,同时保持了 SQL 原生体验和事务一致性。对于已深度使用 PostgreSQL 的团队,这是简化后台任务架构、提升可靠性的务实选择。
参考来源
- GitHub: microsoft/pg_durable
内容声明:本文无广告投放、无付费植入。
如有事实性问题,欢迎发送勘误至 i@hotdrydog.com。