Skip to content

什么是 Durable Execution

Durable Execution 可以翻译成“持久化执行”。它解决的问题是:一段业务代码跑到一半时,进程、机器、网络、外部服务随时可能失败;系统恢复后,业务代码要从正确位置继续,而不是从头盲目重试。

普通重试为什么不够

看一个订单处理函数:

python
def process_order(order):
    payment = charge_card(order.card, order.amount)
    reserve_inventory(order.sku, order.quantity)
    send_receipt(order.email, payment.id)
    return {"payment_id": payment.id}

如果 send_receipt() 前进程崩溃,普通 retry 会重新执行整个函数。后果可能是:

步骤第一次执行重试后风险
扣款已成功可能重复扣款
锁库存已成功可能重复占用库存
发邮件未执行可能最终成功

重试确实提升了成功概率,但它没有回答一个核心问题:哪些步骤已经真的发生过?

Durable Execution 的基本模型

Durable Execution 把函数执行拆成一串可以记录的步骤:

故障恢复时,运行时不会问“函数跑到哪一行了”,而是问“Journal 里已经有哪些事实”。

核心组件

概念作用
Invocation一次 handler 调用,是执行生命周期的单位
JournalInvocation 的执行历史,记录每个 durable step 的结果
Durable Step被运行时保护的副作用或非确定性操作
Replay失败后重新进入函数,用 Journal 结果替代已完成步骤
Idempotency Key把重复入口请求映射到同一个 Invocation
Timer被持久化的等待,不依赖进程内存
State与工作流或对象 key 绑定的持久状态

ctx.run() 解决什么问题

Restate 的 SDK 通过 context actions 暴露能力。以 Python SDK 为例,非确定性的数据库调用、HTTP 调用、随机数、LLM 调用等应放进 ctx.run / ctx.run_typed,让结果进入 execution log。官方文档强调,重放时这些结果会从日志恢复,而不是重新执行。

在教学版里,我们实现一个更小的接口:

python
payment = ctx.run("charge-payment", lambda: charge_card(order))

它做三件事:

  1. 根据当前 invocation id 与 step index 查 Journal。
  2. 如果已有完成记录,直接返回记录里的结果。
  3. 如果没有记录,执行函数,并把结果写入 Journal 后再返回。

可恢复和可回滚不是一回事

Durable Execution 不等于事务回滚。扣款一旦真的调用成功,系统通常无法“自动撤销”。Durable Execution 的目标是避免重复副作用,并让补偿逻辑有可靠上下文。

机制关注点
数据库事务一组数据库写入要么提交,要么回滚
幂等 API同一个业务 key 多次调用只产生一次效果
Durable Execution多步骤业务流程在失败后从正确位置继续
Saga长事务失败后执行补偿步骤

本章练习

  1. 找一个你熟悉的三步骤业务流程,标出哪些步骤有副作用。
  2. 判断这些步骤是否天然幂等。
  3. 设计一个 Journal 表,至少包含 invocation_idstep_indexstep_nameresult

下一章我们看 Restate 如何把这个模型做成生产级运行时。

Teaching project inspired by Restate's public architecture and documentation.