返回

Seata TCC 模式中的一个小问题及其解决方法

后端

在最近的一篇文章中,我探讨了如何使用 Seata TCC 模式在 Spring Cloud 中实现分布式事务。在该过程中,我偶然发现了一个小问题,并向社区提交了一个 issue。在本文中,我将通过源码分析来深入研究这个问题及其解决方案。

问题

在 TCC 模式中,TCC 资源管理器负责协调分布式事务的参与者。在 Seata 中,TCC 资源管理器通过执行 prepare()、commit() 和 rollback() 方法来实现。在特定情况下,当 commit() 方法返回 null 时,Seata 可能会将事务标记为回滚。

源码分析

为了了解这个问题的根本原因,我们深入研究了 Seata TCC 资源管理器的源码。在 Seata 2.0.1 版本中,TCCResourceManagerImpl 类的 commit() 方法如下所示:

public TCCResult commit(BranchType branchType, Object branchId, Object context) throws Throwable {
    Branch branch = manager.getBranch(branchId);
    if (branch == null) {
        throw new InvalidBranchIdException("No branch with branchId " + branchId);
    }
    TccAction action = (TccAction)branch.getAction();
    try {
        Object result = action.commit(context);
        // Seata 事务回滚条件:commit() 返回 null 时,标记事务为回滚
        if (result == null) {
            return new TCCResult(true, null);
        }
        return new TCCResult(false, result);
    } catch (Throwable ex) {
        return new TCCResult(true, ex);
    }
}

从这段代码中,我们可以看到,当 commit() 方法返回 null 时,TCCResourceManagerImpl 会将事务标记为回滚(即第一个 if 语句)。这正是导致问题的根源。

解决方案

为了解决这个问题,Seata 社区在 2.1.0 版本中引入了修复。修复的内容是将 commit() 方法返回 null 时的处理逻辑从回滚修改为不标记为回滚(即取消第一个 if 语句)。

修改后的 commit() 方法如下所示:

public TCCResult commit(BranchType branchType, Object branchId, Object context) throws Throwable {
    Branch branch = manager.getBranch(branchId);
    if (branch == null) {
        throw new InvalidBranchIdException("No branch with branchId " + branchId);
    }
    TccAction action = (TccAction)branch.getAction();
    try {
        Object result = action.commit(context);
        // 移除 commit() 返回 null 时标记事务为回滚的逻辑
        return new TCCResult(false, result);
    } catch (Throwable ex) {
        return new TCCResult(true, ex);
    }
}

通过移除 commit() 返回 null 时标记事务为回滚的逻辑,Seata 可以正确地处理 commit() 返回 null 的情况,而不会将事务标记为回滚。

结论

Seata TCC 模式中存在的这个问题是由 commit() 方法返回 null 时错误地将事务标记为回滚造成的。Seata 社区在 2.1.0 版本中通过修改 commit() 方法返回 null 时的处理逻辑解决了这个问题。通过了解这个问题的根源及其解决方案,我们可以更好地理解 Seata TCC 模式的内部机制,并避免在分布式事务处理中遇到类似的问题。