事物补偿模式 Compensating Transaction Pattern
为了保证最终一致性, 在错误发生时, 沿着整个错误的传递路径进行undo操作. 对于复杂的工作流来说, 尽可能保证相互依赖的流程上数据语义的最终一致性.
问题
在分布式环境中, 大量的数据在节点间不断的传输和被修改. 为了保证整个系统的性能, 任何一个节点往往都是要求最终一致性, 而不是全局强一致性. 在最终一致性的模式下, 分布式环境中的每个点维护自己的状态机, 从全局来看, 整个系统处于不一致的状态. 但所有的节点在执行完规定操作后, 整个系统可以最终达到一致的状态(没有新数据进来的情况下)
这种设计带来的问题就是当一个操作在某个环节出问题了, 希望对数据进行回滚, 但是这个操作很可能无法成功. 因为上游的服务可能已经使用了错误的数据, 而下游服务可能还没有把错误数据写入. 回滚过程就不是简单的达到一个正确的一致性状态, 而是达到一个业务可以容忍的一致性状态
因此我们需要根据业务执行一定的补偿策略, 尽可能的回滚受到影响的数据, 及时终止错误数据的传播, 并对已经出错无法恢复的业务执行一定的补偿操作, 如根据日志进行非常重的重算, 或者等待新的正确数据覆盖旧数据等.
解决
这种设计模式是和业务紧耦合的, 一般来说, 是通过维护一个业务流.系统会对整个业务流进行状态记录, 当回滚时, 针对workflow的状态机进行回滚. 由于状态机是维护在各个节点上的, 所以回滚过程可以并发进行.
从这个描述, 我们知道, 能够执行这种操作的业务必须是能够接受最终一致性,所有操作必须是幂等的
决策
错误判断 一个业务是否失败了往往是难以判断的, 一种情况是服务端的exception则可以判断为立即失败. 另外一种, 如一个预约系统, 预约医生后医生因急事无法赴约, 这种业务端的失败很难被捕捉到
补偿机制难以被确定 如何回滚状态机, 回滚过程失败如何挽救等等都难以进行逻辑编程
非幂等操作 非幂等的操作无法回滚很容易理解, 如何把幂等操作转化成幂等操作是一个非常大的挑战
操作序列的影响 在回滚时可能又改变了很多状态, 能够单独回撤一个状态, 还是一起回滚, 回撤时的顺序是否有影响?
示例
我们假设有这样的一个业务场景是某旅游服务提供商, 客户依次进行如下操作
- 订Flight 1航班, 从北京到上海
- 订Flight 2航班, 从上海到杭州
- 订Flight 3航班, 从杭州回北京
- 在上海H1酒店订一个房间
- 在杭州H2酒店订一个房间
这一系列操作在业务上是相互关联的, 但每个操作本身对于数据库来说是原子的. 由于业务的异步性和延时性, 很可能客户的所有航班和杭州的H2旅馆反馈正常使用, 但上海H1旅馆因为种种原因在2小时后返回失败信息, 或者更进一步的, 返回信息表示取消之前成功的操作.
一个服务非常好的公司, 会注意到这个操作链, 通知用户, 并根据用户的意愿对关联环节进行"回滚". 这里的"回滚"往往是基于策略的补偿式的. 如发现用户的航班购买成功, 但旅馆失败,则补偿更高级别的同区域旅馆, 旅馆成功但航班失败则补偿票价并自动取消旅馆的订房.
类似这样的业务操作, 需要能够理解用户意图, 并把多个原子操作合并成相互关联的一个大操作来面对.
同时再业务层对回滚进行策略编程, 形成基于业务的回滚机制
No comments:
Post a Comment