Expand description
workflow 模式:把事务流程写成一个普通函数,崩溃后从断点续跑。
§跟前四种模式的差别
SAGA / TCC / msg / XA 都要求提交时就把步骤声明清楚。真实业务经常不满足:
第三步做不做取决于第二步返回了什么,中间还有 if、有循环。
workflow 模式让你直接写:
ⓘ
tc.workflow("下单", |mut wf| async move {
let oid = wf.branch("建订单").on_rollback("local://取消订单")
.run_with(|| async { (BranchResult::Success, new_order_id()) }).await?;
wf.branch("扣款").on_rollback("local://退款")
.run(|| async { deduct(&oid).await }).await?;
// 控制流是真的控制流
if need_ship(&oid) {
wf.branch("发货").on_rollback("local://退货")
.run(|| async { ship(&oid).await }).await?;
}
Ok(())
})§崩溃恢复靠重放 + 结果记忆化
进程崩了重启,TC 会把这个函数从头再跑一遍。已经成功过的分支不重新执行, 而是把上次存的返回值原样还给你 —— 所以函数会沿着上次的路径走到断点, 然后继续往下。
第一次: 建订单(真跑,存 oid) → 扣款(真跑) → 崩溃
重启后: 建订单(记忆化,还回 oid) → 扣款(记忆化) → 发货(真跑) → 完成
↑ 副作用不会重做§⚠ 你的函数必须是确定性的
重放是从头再跑,所以分支之间的那些代码会被执行多次。它们必须在相同的 分支返回值下走相同的路径:
- ❌
if rand() > 0.5、if now().hour() < 12、读一个会变的全局状态 - ❌ 在分支外面直接写数据库 —— 那部分不会被记忆化,重放时会重复执行
- ✅ 所有副作用都放进
branch(...).run(...)里
写岔了会怎样?本模块会当场发现并拒绝继续(见 WorkflowError::Diverged),
而不是静默补偿错对象。这是刻意的:静默走错比停下来严重得多。
§为什么这个模式只在嵌入式形态下提供
因为「步骤」是代码,没法表示成一个 URL 存进数据库。DTM 那边也是同理: workflow 的函数体在客户端进程里,TC 只存状态。 我们把 TC 也放在同一个进程里,所以这件事反而更自然。
Structs§
- Branch
Builder - Workflow
Ctx - 传给 workflow 函数的上下文。分支都从这里开。
- Workflow
Registry - 按名字存 workflow 函数。
Enums§
- Workflow
Error - 函数跑不下去的原因。用
?往外抛。