兄弟们,最近处理了一个seata的issue,关于seata分布式事务长期回滚失败后,突然回滚成功了:
这个问题的出现需要以下两个契机:
- 在执行分布式事务期间,有本地事务与分布式事务操作同一张表中的数据导致脏写产生;
- 在回滚时,seata对比afterImage与当前数据不一致,导致回滚失败,此时会一直重试;
- 当手工校准数据后,某一时刻afterImage与当前数据一致,此时回滚重试成功,ABA问题产生;
从源码中定位原因
为了避免ABA问题的产生,通过与seata社区的大佬讨论,最终决定在回滚时,如果对比afterImage与当前数据不一致的情况下,不再尝试回滚重试。这样的话,即使后续通过人工校准后,也不会回滚了。但是这样有另一个问题,就是人工校准后,这个分布式事务就一直遗留在数据库中无法删除了。针对这个问题,seata应该要提供一个restful api让开发人员在数据校准后能够删除掉对应的分布式事务数据。
在seata源码中,如果校验afterImage与当前数据不一致后,会抛出SQLException,最终会被上层代码捕获包装成BranchTransactionException异常,但是里面的code属性是BranchRollbackFailed_Retriable,这也是导致seata一直重试回滚的根本原因:
Result<Boolean> afterEqualsCurrentResult = DataCompareUtils.isRecordsEquals(afterRecords, currentRecords);
if (!afterEqualsCurrentResult.getResult()) {
// 先比较afterImage与当前数据,如果不一致,那么再比较当前数据和beforeImage是否一致
Result<Boolean> beforeEqualsCurrentResult = DataCompareUtils.isRecordsEquals(beforeRecords, currentRecords);
// 如果当前数据和beforeImage一致,那么不需要回滚了,因为相当于已经回滚了
if (beforeEqualsCurrentResult.getResult()) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Stop rollback because there i