上面一文中我们介绍了全局事务的开始与提交,那么本文主要阐述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);
}