Seata-AT全局事务回滚

2,649 阅读1分钟

上面一文中我们介绍了全局事务的开始与提交,那么本文主要阐述AT模式下二阶段回滚步骤

  • TM发起全局回滚
  • TC通知全局回滚
  • RM通过undolog实现全局回滚

一、TM发起全局回滚

 @Override
    public void rollback() throws TransactionException {
        if (role == GlobalTransactionRole.Participant) { // @1
            // Participant has no responsibility of rollback
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Ignore Rollback(): just involved in global transaction [{}]", xid);
            }
            return;
        }
        assertXIDNotNull();

        int retry = ROLLBACK_RETRY_COUNT <= 0 ? DEFAULT_TM_ROLLBACK_RETRY_COUNT : ROLLBACK_RETRY_COUNT;
            while (retry > 0) {
                try {
                    status = transactionManager.rollback(xid); // @2
                    break;
                } catch (Throwable ex) {
                    LOGGER.error("Failed to report global rollback [{}],Retry Countdown: {}, reason: {}", this.getXid(), retry, ex.getMessage());
                    retry--;
                    if (retry == 0) {
                        throw new TransactionException("Failed to report global rollback", ex);
                    }
                }
            }
    }

代码@1:限制只有TM可以发起全局事务回滚

代码@2:交由DefaultTransactionManager发送全局回滚请求GlobalRollbackRequest

二、TC通知全局回滚

TC接收到 GlobalRollbackRequest请求后会由DefaultCore::rollback操作,获取每个分支事务的会话session,发送回滚通知

    public boolean doGlobalRollback(GlobalSession globalSession, boolean retrying) throws TransactionException {
for (BranchSession branchSession : globalSession.getReverseSortedBranches()) {
      BranchStatus currentBranchStatus = branchSession.getStatus();
      if (currentBranchStatus == BranchStatus.PhaseOne_Failed) {
          globalSession.removeBranch(branchSession);
          continue;
      }
      BranchStatus branchStatus = branchRollback(globalSession, branchSession);
}

三、RM通过undolog实现全局回滚

RM接收到TC发送来的回滚通知,找到事务XID关联的undolog并对比before/after进行回滚 在AbstractUndoLogManager类的undo方法中,先建立jdbc数据库链接,然后通过sql获取undolog数据,再遍历每个undolog,按照前image回滚到对于的db

List<SQLUndoLog> sqlUndoLogs = branchUndoLog.getSqlUndoLogs();
if (sqlUndoLogs.size() > 1) {
    Collections.reverse(sqlUndoLogs);
}
for (SQLUndoLog sqlUndoLog : sqlUndoLogs) {
    TableMeta tableMeta = TableMetaCacheFactory.getTableMetaCache(dataSourceProxy.getDbType()).getTableMeta(
        conn, sqlUndoLog.getTableName(), dataSourceProxy.getResourceId());
    sqlUndoLog.setTableMeta(tableMeta);
    AbstractUndoExecutor undoExecutor = UndoExecutorFactory.getUndoExecutor(
        dataSourceProxy.getDbType(), sqlUndoLog);
    undoExecutor.executeOn(conn);
}