3. 错误类型分类:业务错误、系统错误与数据一致性错误
在审批流骨架事务补偿设计中,错误并非铁板一块。不同错误类型对事务边界、补偿策略和恢复路径的要求截然不同。将错误精确分类,是设计补偿动作的前提——只有知道“错在哪一层”,才能决定“补偿到哪一步”。
3.1 错误分类全景
从企业级分布式系统的视角,审批流中的错误可归纳为三大类:业务错误、系统错误和数据一致性错误。三者对事务的影响范围、可预测性和补偿复杂度依次递增。
| 错误类别 | 典型场景 | 可预测性 | 补偿策略 | 事务影响范围 |
|---|---|---|---|---|
| 业务错误 | 驳回、超时、审批人拒绝 | 高(业务规则可枚举) | 正向回滚 + 状态回退 | 单流程实例 |
| 系统错误 | 宕机、网络分区、服务熔断 | 低(基础设施不可控) | 幂等重试 + 状态持久化 | 跨服务、跨节点 |
| 数据一致性错误 | 脏读、幻读、写偏序 | 中(并发时序导致) | 补偿事务 + 版本校验 | 跨资源、跨库 |
设计原则:业务错误优先用“状态机回退”解决;系统错误优先用“幂等+重试”兜底;数据一致性错误必须引入“补偿事务+乐观锁”双重保障。三种策略不可混用,否则补偿逻辑会陷入无限循环。
3.2 业务错误:驳回与超时
业务错误是审批流中最常见、最可控的错误类型。它们由业务规则触发,错误语义明确,补偿路径清晰。
- 驳回(Rejection):审批人主动拒绝当前申请。补偿动作:将流程实例回退到上一审批节点,释放已占用的资源预留(如预算锁定、库存预占),并触发通知。
- 超时(Timeout):审批人在规定时限内未做出响应。补偿动作:自动升级审批人、转交或按预设规则执行默认动作(如自动驳回)。超时补偿需要与业务SLA对齐。
- 审批人拒绝(Decline):与驳回类似,但通常伴随原因说明。补偿动作:记录拒绝原因,回退状态,并允许申请人修改后重新提交。
关键指标:业务错误的补偿成功率应达到 99.9% 以上。如果补偿失败,通常是状态机设计缺陷——例如未记录前驱状态,导致无法回退。
3.3 系统错误:宕机与网络分区
系统错误是分布式系统的“常态”。它们不可预测,但可以通过架构设计降低影响。审批流作为企业业务骨架,必须对系统错误具备弹性。
- 宕机(Crash):审批服务节点、数据库或消息队列不可用。补偿策略:本地状态持久化 + 服务重启后自动恢复未完成流程。关键设计:事务日志必须落盘,且恢复过程幂等。
- 网络分区(Network Partition):服务间通信中断,导致审批状态无法同步。补偿策略:引入“超时+重试”机制,配合断路器防止级联故障。网络恢复后,通过补偿任务对齐状态。
- 服务熔断(Circuit Breaker):下游依赖服务响应过慢或错误率过高。补偿策略:快速失败,将流程实例标记为“待补偿”,由后台调度器异步重试。
避坑提示:系统错误补偿最忌讳“无限重试”。必须设置最大重试次数和退避策略(如指数退避)。超过阈值后,应转入人工处理队列,避免资源耗尽。
3.4 数据一致性错误:脏读与幻读
数据一致性错误是分布式事务中最隐蔽、最难排查的错误。它们通常发生在并发访问同一资源时,导致审批流状态与真实业务状态不一致。
| 错误类型 | 定义 | 审批流场景 | 补偿方案 |
|---|---|---|---|
| 脏读 | 读取到未提交的中间状态数据 | 审批人A读取到审批人B尚未提交的“已通过”标记,做出错误决策 | 引入版本号或乐观锁,禁止读取未提交状态 |
| 幻读 | 同一查询在事务内返回不同结果集 | 审批流统计“待办数量”时,因并发插入新实例导致计数不一致 | 使用可重复读隔离级别,或通过补偿事务重新统计 |
| 写偏序 | 两个事务基于同一快照做出决策,导致最终状态冲突 | 两个审批人同时“通过”同一流程的不同版本,导致状态分裂 | 引入分布式锁或CAS(Compare-And-Swap)机制 |
最佳实践:在审批流核心状态变更点(如“通过”、“驳回”、“转交”)强制使用乐观锁(version 字段)。每次更新时校验 version,若不一致则回滚并提示冲突。这是防止幻读和写偏序的最轻量方案。
3.5 错误分类与补偿策略映射
将三类错误与补偿策略进行映射,形成可执行的决策矩阵。这是骨架事务补偿设计的核心参考。
3.6 错误分类在补偿设计中的落地要点
- 错误分类必须显式编码:在错误日志和补偿事件中携带
error_category字段(如BUSINESS、SYSTEM、CONSISTENCY),便于补偿调度器选择正确的处理策略。 - 业务错误优先本地补偿:不依赖分布式协调,直接在流程引擎内部完成状态回退,延迟最低。
- 系统错误必须幂等:所有补偿操作(如释放资源、回退状态)必须支持重复执行,且结果一致。
- 数据一致性错误需要全局视角:引入分布式事务协调器(如 Saga 或 TCC),确保跨服务状态最终一致。
- 错误分类是动态的:同一个异常(如数据库连接超时)在不同上下文可能被归类为系统错误或数据一致性错误。设计时需保留重分类能力。
工程建议:在代码中为每个补偿操作定义
compensate() 方法,并显式标注其处理的错误类型。例如:@Compensates(errorCategory = "BUSINESS")。这能让补偿链路在运行时自检,避免错误策略误匹配。