Fescar - RM 全域性事務提交回滾流程
開篇
這篇文章的目的主要是講解RM在接收TC的請求後執行全域性分支事務提交(doBranchCommit)和全域性分支事務回滾(doBranchRollback)的流程。
全域性的分支事務提交過程和回滾過程也算RM處理流程中核心的一環,瞭解以後並結合之前講解的本地事務提交流程就能夠較好的理解整個過程了。
全域性事務操作流程
整體流程
public class RMHandlerAT extends AbstractRMHandlerAT implements
RMInboundHandler, TransactionMessageHandler {
private DataSourceManager dataSourceManager = DataSourceManager.get();
@Override
protected void doBranchCommit(BranchCommitRequest request, BranchCommitResponse response)
throws TransactionException {
String xid = request.getXid();
long branchId = request.getBranchId();
String resourceId = request.getResourceId();
String applicationData = request.getApplicationData();
LOGGER.info("AT Branch committing: " + xid + " " + branchId + " " + resourceId + " " + applicationData);
BranchStatus status = dataSourceManager.branchCommit(xid, branchId, resourceId, applicationData);
response.setBranchStatus(status);
LOGGER.info("AT Branch commit result: " + status);
}
@Override
protected void doBranchRollback(BranchRollbackRequest request, BranchRollbackResponse response)
throws TransactionException {
String xid = request.getXid();
long branchId = request.getBranchId();
String resourceId = request.getResourceId();
String applicationData = request.getApplicationData();
LOGGER.info("AT Branch rolling back: " + xid + " " + branchId + " " + resourceId);
BranchStatus status = dataSourceManager.branchRollback(xid, branchId, resourceId, applicationData);
response.setBranchStatus(status);
LOGGER.info("AT Branch rollback result: " + status);
}
}
說明:
- doBranchCommit()通過dataSourceManager.branchCommit()去執行分支事務提交。
- doBranchRollback()通過dataSourceManager.branchRollback()去執行分支事務回滾。
- dataSourceManager是DataSourceManager物件。
doBranchCommit流程
public class DataSourceManager implements ResourceManager {
private ResourceManagerInbound asyncWorker;
public void setAsyncWorker(ResourceManagerInbound asyncWorker) {
this.asyncWorker = asyncWorker;
}
public BranchStatus branchCommit(String xid, long branchId, String resourceId, String applicationData)
throws TransactionException {
return asyncWorker.branchCommit(xid, branchId, resourceId, applicationData);
}
}
public class AsyncWorker implements ResourceManagerInbound {
public BranchStatus branchCommit(String xid, long branchId, String resourceId, String applicationData)
throws TransactionException {
if (ASYNC_COMMIT_BUFFER.size() < ASYNC_COMMIT_BUFFER_LIMIT) {
ASYNC_COMMIT_BUFFER.add(new Phase2Context(xid, branchId, resourceId, applicationData));
} else {
LOGGER.warn("Async commit buffer is FULL.
Rejected branch [" + branchId + "/" + xid + "] will be handled by housekeeping later.");
}
return BranchStatus.PhaseTwo_Committed;
}
public synchronized void init() {
LOGGER.info("Async Commit Buffer Limit: " + ASYNC_COMMIT_BUFFER_LIMIT);
timerExecutor = new ScheduledThreadPoolExecutor(1,
new NamedThreadFactory("AsyncWorker", 1, true));
timerExecutor.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
doBranchCommits();
} catch (Throwable e) {
LOGGER.info("Failed at async committing ... " + e.getMessage());
}
}
}, 10, 1000 * 1, TimeUnit.MILLISECONDS);
}
private void doBranchCommits() {
if (ASYNC_COMMIT_BUFFER.size() == 0) {
return;
}
Map<String, List<Phase2Context>> mappedContexts = new HashMap<>();
Iterator<Phase2Context> iterator = ASYNC_COMMIT_BUFFER.iterator();
while (iterator.hasNext()) {
Phase2Context commitContext = iterator.next();
List<Phase2Context> contextsGroupedByResourceId = mappedContexts.get(commitContext.resourceId);
if (contextsGroupedByResourceId == null) {
contextsGroupedByResourceId = new ArrayList<>();
mappedContexts.put(commitContext.resourceId, contextsGroupedByResourceId);
}
contextsGroupedByResourceId.add(commitContext);
iterator.remove();
}
for (String resourceId : mappedContexts.keySet()) {
Connection conn = null;
try {
try {
DataSourceProxy dataSourceProxy = DataSourceManager.get().get(resourceId);
conn = dataSourceProxy.getPlainConnection();
} catch (SQLException sqle) {
LOGGER.warn("Failed to get connection for async committing on " + resourceId, sqle);
continue;
}
List<Phase2Context> contextsGroupedByResourceId = mappedContexts.get(resourceId);
for (Phase2Context commitContext : contextsGroupedByResourceId) {
try {
UndoLogManager.deleteUndoLog(commitContext.xid, commitContext.branchId, conn);
} catch (Exception ex) {
LOGGER.warn("Failed to delete undo log [" +
commitContext.branchId + "/" + commitContext.xid + "]", ex);
}
}
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException closeEx) {
LOGGER.warn("Failed to close JDBC resource while deleting undo_log ", closeEx);
}
}
}
}
}
}
說明:
- doBranchCommit()操作的核心實現通過AsyncWorker完成,AsyncWorker類其實是一個生成消費模型。
- doBranchCommit()把需要提交的任務新增到AsyncWorker的ASYNC_COMMIT_BUFFER佇列當中。
- AsyncWorker內部timerExecutor負責啟動執行commit動作執行緒執行doBranchCommits()動作。
- doBranchCommits動作內部負責刪除多餘的UndoLog, UndoLogManager.deleteUndoLog。
- doBranchCommit()的本質任務就是刪除備份的回滾日誌而已。
doBranchRollback流程
public class DataSourceManager implements ResourceManager {
public BranchStatus branchRollback(String xid, long branchId, String resourceId, String applicationData)
throws TransactionException {
DataSourceProxy dataSourceProxy = get(resourceId);
if (dataSourceProxy == null) {
throw new ShouldNeverHappenException();
}
try {
// 執行回滾操作
UndoLogManager.undo(dataSourceProxy, xid, branchId);
} catch (TransactionException te) {
if (te.getCode() == TransactionExceptionCode.BranchRollbackFailed_Unretriable) {
return BranchStatus.PhaseTwo_RollbackFailed_Unretryable;
} else {
return BranchStatus.PhaseTwo_RollbackFailed_Retryable;
}
}
return BranchStatus.PhaseTwo_Rollbacked;
}
}
public final class UndoLogManager {
private static String SELECT_UNDO_LOG_SQL =
"SELECT * FROM " + UNDO_LOG_TABLE_NAME + " WHERE log_status = 0 AND branch_id = ? AND xid = ? FOR UPDATE";
public static void undo(DataSourceProxy dataSourceProxy, String xid, long branchId)
throws TransactionException {
assertDbSupport(dataSourceProxy.getTargetDataSource().getDbType());
Connection conn = null;
ResultSet rs = null;
PreparedStatement selectPST = null;
try {
conn = dataSourceProxy.getPlainConnection();
// The entire undo process should run in a local transaction.
conn.setAutoCommit(false);
// Find UNDO LOG
selectPST = conn.prepareStatement(SELECT_UNDO_LOG_SQL);
selectPST.setLong(1, branchId);
selectPST.setString(2, xid);
rs = selectPST.executeQuery();
// 遍歷所有回滾日誌
while (rs.next()) {
Blob b = rs.getBlob("rollback_info");
String rollbackInfo = StringUtils.blob2string(b);
BranchUndoLog branchUndoLog = UndoLogParserFactory.getInstance().decode(rollbackInfo);
for (SQLUndoLog sqlUndoLog : branchUndoLog.getSqlUndoLogs()) {
TableMeta tableMeta = TableMetaCache.getTableMeta(dataSourceProxy, sqlUndoLog.getTableName());
sqlUndoLog.setTableMeta(tableMeta);
AbstractUndoExecutor undoExecutor = UndoExecutorFactory.getUndoExecutor(
dataSourceProxy.getDbType(), sqlUndoLog);
undoExecutor.executeOn(conn);
}
}
deleteUndoLog(xid, branchId, conn);
conn.commit();
} catch (Throwable e) {
if (conn != null) {
try {
conn.rollback();
} catch (SQLException rollbackEx) {
LOGGER.warn("Failed to close JDBC resource while undo ... ", rollbackEx);
}
}
throw new TransactionException(BranchRollbackFailed_Retriable, String.format("%s/%s", branchId, xid), e);
} finally {
try {
if (rs != null) {
rs.close();
}
if (selectPST != null) {
selectPST.close();
}
if (conn != null) {
conn.close();
}
} catch (SQLException closeEx) {
LOGGER.warn("Failed to close JDBC resource while undo ... ", closeEx);
}
}
}
}
說明:
- doBranchRollback操作的核心實現通過UndoLogManager完成,UndoLogManager.undo()負責執行回滾。
- undo()操作的核心是通過SELECT_UNDO_LOG_SQL日誌去獲取回滾日誌內容。
- 根據undoLog物件通過UndoExecutorFactory.getUndoExecutor獲取回滾的執行者Executor物件。
- undoExecutor.executeOn(conn)執行回滾操作,不同的回滾操作物件不同的undoExecutor。
- deleteUndoLog(xid, branchId, conn)執行日誌刪除操作。
期待
下篇文章會針對undoExecutor作具體的介紹。
Fescar原始碼分析連載
相關文章
- Fescar - RM InsertExecutor介紹
- 分散式事務中介軟體 Fescar—RM 模組原始碼解讀分散式原始碼
- sourceTree“重置提交”和“提交回滾”的區別
- 如何在 pyqt 中實現全域性事件匯流排QT事件
- 解密分散式事務框架-Fescar解密分散式框架
- 十四、Vue專案 - 對全域性事件的解綁(非常重要 !!!)Vue事件
- 分散式柔性事務的TCC方案分散式
- 分散式柔性事務之事務訊息詳解分散式
- openGauss 子事務併發回滾流程最佳化
- Golang 實現 Redis(10): 本地原子性事務GolangRedis
- vue-geventbus – 一個優雅的 Vue 全域性事件處理外掛Vue事件
- vue-geventbus - 一個優雅的 Vue 全域性事件處理外掛Vue事件
- 分散式事務解決方案——柔性事務與服務模式分散式模式
- Vue一個案例引發的動態元件與全域性事件繫結總結Vue元件事件
- [提問交流]onethink 只有全域性的配置項嗎?
- 分散式場景之剛性事務-2PC詳解分散式
- 阿里巴巴開源分散式事務解決方案 Fescar阿里分散式
- 來了!阿里開源分散式事務解決方案Fescar阿里分散式
- 來了!阿里開源分散式事務解決方案 Fescar阿里分散式
- Linux 系統的常用命令之 rm ,rm -rf , rm -f 以及rm 命令的其他引數命令Linux
- 阿里巴巴開源分散式事務解決方案 FESCAR【轉】阿里分散式
- 一種基於柔性事務的分散式事務解決方案設計探究分散式
- Linux rm命令Linux
- 深度剖析一站式分散式事務方案Seata(Fescar)-Server分散式Server
- 深度剖析一站式分散式事務方案 Seata(Fescar)-Server分散式Server
- 基於Spring Cloud Netflix的TCC柔性事務和EDA事件驅動示例SpringCloud事件
- 避免 rm 誤操作
- AI 大模型輔助測試全流程提效AI大模型
- [提問交流]onethink自定義模型的開發流程模型
- 2.8.2 全域性資料服務GDS
- Fescar example解析 - TM傳送邏輯
- rm命令弱爆了!
- rm 刪除檔案
- Linux基礎命令---rmLinux
- REST API設計:如何處理Http併發一致性事務更新? - mscharhagRESTAPIHTTP
- 測試流程規範--提測規範(釘釘、郵件)
- 關於事務回滾註解@Transactional
- sqlserver遇到回滾事務的操作策略SQLServer