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 状态机的完整状态跃迁路径。箭头上的标签为触发事件,节点颜色区分正常流(蓝色)、补偿流(橙色)和异常终止(红色)。

Saga 状态机流转图 PENDING RUNNING PARTIALLY_SUCCEEDED COMPENSATING COMPLETED COMPENSATED FAILED START_EVENT ALL_COMPLETED STEP_FAILED COMPENSATE_TRIGGER ALL_COMPENSATED COMPENSATION_FAILED 说明:蓝色节点为正常执行流,橙色节点为补偿流,绿色节点为终态,红色节点为不可恢复失败。

4.4 补偿触发条件

补偿不是随意触发的,必须满足精确的条件组合。错误的触发条件会导致数据不一致或重复补偿。以下是补偿触发的三大条件及其判断逻辑。

条件类别 具体条件 判断逻辑
业务失败 本地事务执行返回业务异常(如余额不足、库存不足) 步骤执行结果码 ≠ SUCCESS
技术异常 网络超时、数据库连接失败、服务不可用 捕获到 RuntimeException / TimeoutException
超时触发 步骤执行超过配置的最大等待时间 定时器检测到步骤状态仍为 IN_PROGRESS 且已超时
重要: 补偿触发后,状态机必须立即锁定该 Saga 实例,禁止任何新的正向步骤提交。同时,补偿操作应按照“后进先出”(LIFO)的顺序执行,即先补偿最后成功的步骤,再依次向前补偿。这是保证业务语义正确的关键。

4.5 补偿触发后的状态机行为

当补偿触发条件满足时,状态机执行以下动作序列:

  1. 状态锁定:将 Saga 实例状态从 PARTIALLY_SUCCEEDED 更新为 COMPENSATING,并写入补偿开始时间戳。
  2. 补偿队列构建:从执行历史中提取所有已成功步骤的列表,按执行顺序逆序排列。
  3. 逐步骤补偿:依次调用每个步骤的补偿操作。每个补偿操作执行后,记录补偿结果。
  4. 补偿结果判断:
    • 所有补偿操作成功 → 状态跃迁至 COMPENSATED。
    • 任一补偿操作失败 → 状态跃迁至 FAILED,触发告警。
  5. 最终一致性保证: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