Poison

ChainedTransactionManager

此前我曾写过一个简单的多数据源事务管理器,其实现机制相当简单,开启事务时从多个数据源获取连接并设置事务的相关属性,然后执行业务逻辑,在执行业务逻辑过程中对 DAO 层使用 AOP 变更 TransactionSynchronizationManager (Spring Framework 5.3.14 API) 中当前线程绑定的数据源并执行 SQL 操作,最后根据是否产生了异常进行对应的提交或者回滚操作。不足之处在于其不能保证多数据源的数据一致性,比如数据源 A 提交事务后因为某些原因导致数据源 B 没能进行事务提交,那么就出现了数据不一致的情况。后面我看到 Spring Data 中的 ChainedTransactionManager,即链式事务管理器,发现实现方式几乎一致,其文档中提到:

PlatformTransactionManager implementation that orchestrates transaction creation, commits and rollbacks to a list of delegates. Using this implementation assumes that errors causing a transaction rollback will usually happen before the transaction completion or during the commit of the most inner PlatformTransactionManager.

The configured instances will start transactions in the order given and commit/rollback in reverse order, which means the PlatformTransactionManager most likely to break the transaction should be the last in the list configured. A PlatformTransactionManager throwing an exception during commit will automatically cause the remaining transaction managers to roll back instead of committing.

As consequence, a transaction can get into a state, where the first PlatformTransactionManager has committed its transaction and a subsequent PlatformTransactionManager failed to commit its transaction (e.g. caused by an I/O error or the transactional resource failed to commit for other reasons). In that case, commit(TransactionStatus) throws a HeuristicCompletionException to indicate a partially committed transaction. Rollback isn’t affected as the natural consequence of a missing commit is a rollback of a transactional resource. ChainedTransactionManager should be only used if the application can tolerate or recover from inconsistent state caused by partially committed transactions. In any other case, the use of ChainedTransactionManager is not recommended.

Instead of using ChainedTransactionManager for attaching callbacks to transaction commit (pre commit/post commit), either register a TransactionSynchronization to explicitly follow transaction cleanup with simplified semantics in case of exceptions.

源码参见:ChainedTransactionManager.java at 2.6.0。正如文档中所提到的,链式事务管理器应该仅仅被用于应用可以容忍事务部分提交造成的不一致状态的场景下。自 Spring Data 2.5 开始,该类已经被标记为废弃了,因为该实现并不能保证多数据源的一致提交或回滚,可能出现部分提交或者部分回滚的情况。相关 issue 可以参考文末链接。完善的分布式事务还是建议使用 TCC/SAGA 等方案进行实现。

Reference

Deprecate ChainedTransactionManager DATACMNS-1817 · Issue #2232 · spring-projects/spring-data-commons · GitHub
Deprecate ChainedTransactionManager by mp911de · Pull Request #2286 · spring-projects/spring-data-commons · GitHub
Reactive transaction manager · Issue #2323 · spring-projects/spring-data-commons · GitHub
What Is Seata