当前位置:网站首页>The new version of Alibaba Seata finally solves the idempotence, suspension and empty rollback problems of TCC mode
The new version of Alibaba Seata finally solves the idempotence, suspension and empty rollback problems of TCC mode
2022-07-25 00:10:00 【InfoQ】
TCC review
try-commit

try-cancel

TCC advantage
Asynchronous submission
Same as library mode
- TM: Manage global affairs , Including starting global transactions , Submit / Roll back global transactions ;
- RM: Manage branch transactions ;
- TC: Manage the status of global and branch transactions .


RM Code example
@LocalTCC
public interface StorageService {
/**
* Deducting the inventory
* @param xid overall situation xid
* @param productId product id
* @param count Number
* @return
*/
@TwoPhaseBusinessAction(name = "storageApi", commitMethod = "commit", rollbackMethod = "rollback", useTCCFence = true)
boolean decrease(String xid, Long productId, Integer count);
/**
* Commit transaction
* @param actionContext
* @return
*/
boolean commit(BusinessActionContext actionContext);
/**
* Roll back the transaction
* @param actionContext
* @return
*/
boolean rollback(BusinessActionContext actionContext);
}
TCC Existing problems
CREATE TABLE IF NOT EXISTS `tcc_fence_log`
(
`xid` VARCHAR(128) NOT NULL COMMENT 'global id',
`branch_id` BIGINT NOT NULL COMMENT 'branch id',
`action_name` VARCHAR(64) NOT NULL COMMENT 'action name',
`status` TINYINT NOT NULL COMMENT 'status(tried:1;committed:2;rollbacked:3;suspended:4)',
`gmt_create` DATETIME(3) NOT NULL COMMENT 'create time',
`gmt_modified` DATETIME(3) NOT NULL COMMENT 'update time',
PRIMARY KEY (`xid`, `branch_id`),
KEY `idx_gmt_modified` (`gmt_modified`),
KEY `idx_status` (`status`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
idempotent
@Override
public BranchStatus branchCommit(BranchType branchType, String xid, long branchId, String resourceId,
String applicationData) throws TransactionException {
TCCResource tccResource = (TCCResource)tccResourceCache.get(resourceId);
// Omit judgment
Object targetTCCBean = tccResource.getTargetBean();
Method commitMethod = tccResource.getCommitMethod();
// Omit judgment
try {
//BusinessActionContext
BusinessActionContext businessActionContext = getBusinessActionContext(xid, branchId, resourceId,
applicationData);
Object[] args = this.getTwoPhaseCommitArgs(tccResource, businessActionContext);
Object ret;
boolean result;
// annotation useTCCFence Whether the property is set to true
if (Boolean.TRUE.equals(businessActionContext.getActionContext(Constants.USE_TCC_FENCE))) {
try {
result = TCCFenceHandler.commitFence(commitMethod, targetTCCBean, xid, branchId, args);
} catch (SkipCallbackWrapperException | UndeclaredThrowableException e) {
throw e.getCause();
}
} else {
// Ellipsis logic
}
LOGGER.info("TCC resource commit result : {}, xid: {}, branchId: {}, resourceId: {}", result, xid, branchId, resourceId);
return result ? BranchStatus.PhaseTwo_Committed : BranchStatus.PhaseTwo_CommitFailed_Retryable;
} catch (Throwable t) {
// Omit
return BranchStatus.PhaseTwo_CommitFailed_Retryable;
}
}
public static boolean commitFence(Method commitMethod, Object targetTCCBean,
String xid, Long branchId, Object[] args) {
return transactionTemplate.execute(status -> {
try {
Connection conn = DataSourceUtils.getConnection(dataSource);
TCCFenceDO tccFenceDO = TCC_FENCE_DAO.queryTCCFenceDO(conn, xid, branchId);
if (tccFenceDO == null) {
throw new TCCFenceException(String.format("TCC fence record not exists, commit fence method failed. xid= %s, branchId= %s", xid, branchId),
FrameworkErrorCode.RecordAlreadyExists);
}
if (TCCFenceConstant.STATUS_COMMITTED == tccFenceDO.getStatus()) {
LOGGER.info("Branch transaction has already committed before. idempotency rejected. xid: {}, branchId: {}, status: {}", xid, branchId, tccFenceDO.getStatus());
return true;
}
if (TCCFenceConstant.STATUS_ROLLBACKED == tccFenceDO.getStatus() || TCCFenceConstant.STATUS_SUSPENDED == tccFenceDO.getStatus()) {
if (LOGGER.isWarnEnabled()) {
LOGGER.warn("Branch transaction status is unexpected. xid: {}, branchId: {}, status: {}", xid, branchId, tccFenceDO.getStatus());
}
return false;
}
return updateStatusAndInvokeTargetMethod(conn, commitMethod, targetTCCBean, xid, branchId, TCCFenceConstant.STATUS_COMMITTED, status, args);
} catch (Throwable t) {
status.setRollbackOnly();
throw new SkipCallbackWrapperException(t);
}
});
}
Empty rollback

//TCCFenceHandler class
public static Object prepareFence(String xid, Long branchId, String actionName, Callback<Object> targetCallback) {
return transactionTemplate.execute(status -> {
try {
Connection conn = DataSourceUtils.getConnection(dataSource);
boolean result = insertTCCFenceLog(conn, xid, branchId, actionName, TCCFenceConstant.STATUS_TRIED);
LOGGER.info("TCC fence prepare result: {}. xid: {}, branchId: {}", result, xid, branchId);
if (result) {
return targetCallback.execute();
} else {
throw new TCCFenceException(String.format("Insert tcc fence record error, prepare fence failed. xid= %s, branchId= %s", xid, branchId),
FrameworkErrorCode.InsertRecordError);
}
} catch (TCCFenceException e) {
// Omit
} catch (Throwable t) {
// Omit
}
});
}
//TCCFenceHandler class
public static boolean rollbackFence(Method rollbackMethod, Object targetTCCBean,
String xid, Long branchId, Object[] args, String actionName) {
return transactionTemplate.execute(status -> {
try {
Connection conn = DataSourceUtils.getConnection(dataSource);
TCCFenceDO tccFenceDO = TCC_FENCE_DAO.queryTCCFenceDO(conn, xid, branchId);
// non_rollback
if (tccFenceDO == null) {
// Do not execute rollback logic
return true;
} else {
if (TCCFenceConstant.STATUS_ROLLBACKED == tccFenceDO.getStatus() || TCCFenceConstant.STATUS_SUSPENDED == tccFenceDO.getStatus()) {
LOGGER.info("Branch transaction had already rollbacked before, idempotency rejected. xid: {}, branchId: {}, status: {}", xid, branchId, tccFenceDO.getStatus());
return true;
}
if (TCCFenceConstant.STATUS_COMMITTED == tccFenceDO.getStatus()) {
if (LOGGER.isWarnEnabled()) {
LOGGER.warn("Branch transaction status is unexpected. xid: {}, branchId: {}, status: {}", xid, branchId, tccFenceDO.getStatus());
}
return false;
}
}
return updateStatusAndInvokeTargetMethod(conn, rollbackMethod, targetTCCBean, xid, branchId, TCCFenceConstant.STATUS_ROLLBACKED, status, args);
} catch (Throwable t) {
status.setRollbackOnly();
throw new SkipCallbackWrapperException(t);
}
});
}
update tcc_fence_log set status = ?, gmt_modified = ?
where xid = ? and branch_id = ? and status = ? ;
Hang

public static boolean rollbackFence(Method rollbackMethod, Object targetTCCBean,
String xid, Long branchId, Object[] args, String actionName) {
return transactionTemplate.execute(status -> {
try {
Connection conn = DataSourceUtils.getConnection(dataSource);
TCCFenceDO tccFenceDO = TCC_FENCE_DAO.queryTCCFenceDO(conn, xid, branchId);
// non_rollback
if (tccFenceDO == null) {
// Insert anti hang record
boolean result = insertTCCFenceLog(conn, xid, branchId, actionName, TCCFenceConstant.STATUS_SUSPENDED);
// Ellipsis logic
return true;
} else {
// Ellipsis logic
}
return updateStatusAndInvokeTargetMethod(conn, rollbackMethod, targetTCCBean, xid, branchId, TCCFenceConstant.STATUS_ROLLBACKED, status, args);
} catch (Throwable t) {
// Ellipsis logic
}
});
}
//TCCFenceHandler class
public static Object prepareFence(String xid, Long branchId, String actionName, Callback<Object> targetCallback) {
return transactionTemplate.execute(status -> {
try {
Connection conn = DataSourceUtils.getConnection(dataSource);
boolean result = insertTCCFenceLog(conn, xid, branchId, actionName, TCCFenceConstant.STATUS_TRIED);
// Ellipsis logic
} catch (TCCFenceException e) {
if (e.getErrcode() == FrameworkErrorCode.DuplicateKeyException) {
LOGGER.error("Branch transaction has already rollbacked before,prepare fence failed. xid= {},branchId = {}", xid, branchId);
addToLogCleanQueue(xid, branchId);
}
status.setRollbackOnly();
throw new SkipCallbackWrapperException(e);
} catch (Throwable t) {
// Omit
}
});
}
summary
边栏推荐
- [nuxt 3] (x) runtime configuration
- Google Earth engine - the use of the neighborhood tobands function
- 痛并快乐的-NIO编程
- HTB-Aragog
- 91. (leaflet chapter) leaflet situation plotting - offensive direction drawing
- LeetCode_ 392_ Judgement subsequence
- Fast development board for Godson solid state drive startup (burning system to solid state) - partition
- EF core: self referencing organizational structure tree
- Discussion on line segment tree
- Js----- Chapter 4 array
猜你喜欢

With screen and nohup running, there is no need to worry about deep learning code anymore | exiting the terminal will not affect the operation of server program code

Quartus:17.1版本的Quartus安装Cyclone 10 LP器件库

ROS manipulator movelt learning notes 3 | kinect360 camera (V1) related configuration

Heap and stack in embedded development

Opengauss kernel analysis: query rewriting

Unity+photon self made multiplayer TPS game

Processing PDF and JPG files in VB6

Live broadcast preview | online seminar on open source security governance models and tools

JS ------ Chapter 3 JS cycle

See project code Note 1
随机推荐
Only by learning these JMeter plug-ins can we design complex performance test scenarios
Internal network mapping port to external network
2022 Henan Mengxin League game 2: Henan University of technology I - 22
Optaplanner will abandon DRL (drools) scoring method!!!
Beisen prospectus: the advantages of the track are prominent, and integration + medium and large customers are plus points
[untitled]
dpkg : Breaks: libapt-pkg5.0 (< 1.7~b) but 1.6.15 is to be installedE: Broken packages
C language: deep analysis function stack frame
Live broadcast preview | online seminar on open source security governance models and tools
LeetCode_392_判断子序列
Coding builds an image, inherits the self built basic image, and reports an error unauthorized: invalid credential Please confirm that you have entered the correct user name and password.
Discussion on line segment tree
Restructuredtext grammar summary for beginners
多线程&高并发(全网最新:面试题 + 导图 + 笔记)面试手稳心不慌
动态规划-01背包滚动数组优化
Two numbers that appear only once in the array
2022 Henan Mengxin League game 2: Henan University of technology K - Rice
Opengauss kernel analysis: query rewriting
Grafana - influxdb visual K6 output
Deep and direct visual slam