4. Saga 状态机设计:状态定义、事件驱动、状态流转图与补偿触发条件
Saga 状态机是分布式事务编排的核心骨架。它定义了事务从开始到最终结束的每一个合法状态、触发状态跃迁的事件、以及补偿操作介入的精确条件。本章将围绕状态定义、事件驱动模型、状态流转图与补偿触发条件四个维度,构建一个可落地、可观测的 Saga 状态机设计体系。
4.1 核心状态定义
一个标准的 Saga 事务实例在其生命周期中会经历以下 7 种核心状态。这些状态覆盖了正常执行、异常回滚、以及边界超时场景。
| 状态名称 | 状态标识 | 含义 | 是否可补偿 |
|---|---|---|---|
| 待开始 | PENDING | Saga 实例已创建,但尚未执行任何本地事务 | 否 |
| 执行中 | RUNNING | 正在按顺序执行本地事务步骤 | 是(部分步骤已提交) |
| 部分成功 | PARTIALLY_SUCCEEDED | 部分步骤执行成功,但后续步骤失败,等待补偿 | 是 |
| 补偿中 | COMPENSATING | 正在反向执行已成功步骤的补偿操作 | 否(补偿本身不可再补偿) |
| 已完成 | COMPLETED | 所有步骤执行成功,事务正常结束 | 否 |
| 已补偿 | COMPENSATED | 所有补偿操作执行成功,事务回滚完成 | 否 |
| 失败 | FAILED | 补偿过程中发生不可恢复错误,需要人工介入 | 否 |
设计要点: 状态定义应遵循“最小完备”原则。PENDING 和 FAILED 是常被忽略但至关重要的边界状态。PENDING 用于防止重复提交,FAILED 用于兜底人工处理,避免事务“悬空”。
4.2 事件驱动模型
Saga 状态机采用事件驱动架构。每个状态跃迁都由一个明确的事件触发,事件携带上下文载荷(如步骤执行结果、异常堆栈、超时标记)。事件驱动模型使状态机具备异步、解耦、可重放三大特性。
核心事件类型如下:
- START_EVENT:启动 Saga 实例,状态从 PENDING → RUNNING。
- STEP_SUCCEEDED:单个本地事务执行成功,状态保持在 RUNNING。
- STEP_FAILED:单个本地事务执行失败,状态从 RUNNING → PARTIALLY_SUCCEEDED。
- COMPENSATE_TRIGGER:触发补偿流程,状态从 PARTIALLY_SUCCEEDED → COMPENSATING。
- COMPENSATION_SUCCEEDED:单个补偿操作成功,状态保持在 COMPENSATING。
- COMPENSATION_FAILED:单个补偿操作失败,状态从 COMPENSATING → FAILED。
- ALL_COMPENSATED:所有补偿操作完成,状态从 COMPENSATING → COMPENSATED。
- ALL_COMPLETED:所有步骤执行成功,状态从 RUNNING → COMPLETED。
- TIMEOUT:步骤执行超时,等价于 STEP_FAILED 事件。
事件幂等性: 事件消费者(状态机引擎)必须对重复事件做幂等处理。例如,同一个 STEP_SUCCEEDED 事件重复到达时,状态机不应重复提交本地事务或重复记录日志。建议为每个事件携带全局唯一 ID(如 UUID),并在状态机侧做去重校验。
4.3 状态流转图
下图展示了 Saga 状态机的完整状态跃迁路径。箭头上的标签为触发事件,节点颜色区分正常流(蓝色)、补偿流(橙色)和异常终止(红色)。
4.4 补偿触发条件
补偿不是随意触发的,必须满足精确的条件组合。错误的触发条件会导致数据不一致或重复补偿。以下是补偿触发的三大条件及其判断逻辑。
| 条件类别 | 具体条件 | 判断逻辑 |
|---|---|---|
| 业务失败 | 本地事务执行返回业务异常(如余额不足、库存不足) | 步骤执行结果码 ≠ SUCCESS |
| 技术异常 | 网络超时、数据库连接失败、服务不可用 | 捕获到 RuntimeException / TimeoutException |
| 超时触发 | 步骤执行超过配置的最大等待时间 | 定时器检测到步骤状态仍为 IN_PROGRESS 且已超时 |
重要: 补偿触发后,状态机必须立即锁定该 Saga 实例,禁止任何新的正向步骤提交。同时,补偿操作应按照“后进先出”(LIFO)的顺序执行,即先补偿最后成功的步骤,再依次向前补偿。这是保证业务语义正确的关键。
4.5 补偿触发后的状态机行为
当补偿触发条件满足时,状态机执行以下动作序列:
- 状态锁定:将 Saga 实例状态从 PARTIALLY_SUCCEEDED 更新为 COMPENSATING,并写入补偿开始时间戳。
- 补偿队列构建:从执行历史中提取所有已成功步骤的列表,按执行顺序逆序排列。
- 逐步骤补偿:依次调用每个步骤的补偿操作。每个补偿操作执行后,记录补偿结果。
- 补偿结果判断:
- 所有补偿操作成功 → 状态跃迁至 COMPENSATED。
- 任一补偿操作失败 → 状态跃迁至 FAILED,触发告警。
- 最终一致性保证:COMPENSATED 状态表示业务层面已回滚至初始状态;FAILED 状态表示需要人工或离线任务修复。
幂等补偿设计: 补偿操作本身必须支持幂等调用。例如,扣减库存的补偿是增加库存,但增加库存操作可能因网络重试而被调用多次。补偿操作的幂等键应使用 SagaId + StepId + 补偿序号 的组合。
4.6 状态机与补偿触发的关系总结
下表将状态、事件、补偿触发条件三者关联,形成完整的决策矩阵:
| 当前状态 | 到达事件 | 补偿触发条件 | 下一状态 |
|---|---|---|---|
| PENDING | START_EVENT | 无 | RUNNING |
| RUNNING | STEP_FAILED | 业务失败 / 技术异常 / 超时 | PARTIALLY_SUCCEEDED |
| PARTIALLY_SUCCEEDED | COMPENSATE_TRIGGER | 自动触发(状态机内部决策) | COMPENSATING |
| COMPENSATING | COMPENSATION_FAILED | 补偿操作抛出异常 | FAILED |
| COMPENSATING | ALL_COMPENSATED | 所有补偿操作成功 | COMPENSATED |
| RUNNING | ALL_COMPLETED | 无(正常结束) | COMPLETED |