Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: when use tcc fence, add a config to enable empty rollback #4532

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions common/src/main/java/io/seata/common/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -166,4 +166,9 @@ public interface Constants {
*/
String SKIP_CHECK_LOCK = "skipCheckLock";

/**
* the constant ENABLE_EMPTY_ROLLBACK;
*/
String ENABLE_EMPTY_ROLLBACK = "enableEmptyRollback";

}
44 changes: 31 additions & 13 deletions tcc/src/main/java/io/seata/rm/tcc/TCCFenceHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package io.seata.rm.tcc;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.Date;
Expand Down Expand Up @@ -166,10 +167,11 @@ public static boolean commitFence(Method commitMethod, Object targetTCCBean,
* @param branchId the branch transaction id
* @param args rollback method's parameters
* @param actionName the action name
* @param enableEmptyRollback if enable empty rollback
* @return the boolean
*/
public static boolean rollbackFence(Method rollbackMethod, Object targetTCCBean,
String xid, Long branchId, Object[] args, String actionName) {
String xid, Long branchId, Object[] args, String actionName, boolean enableEmptyRollback) {
return transactionTemplate.execute(status -> {
try {
Connection conn = DataSourceUtils.getConnection(dataSource);
Expand All @@ -182,7 +184,7 @@ public static boolean rollbackFence(Method rollbackMethod, Object targetTCCBean,
throw new TCCFenceException(String.format("Insert tcc fence record error, rollback fence method failed. xid= %s, branchId= %s", xid, branchId),
FrameworkErrorCode.InsertRecordError);
}
return true;
return enableEmptyRollback ? invokeTwoPhaseRollback(rollbackMethod, targetTCCBean, status, args) : 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());
Expand Down Expand Up @@ -238,17 +240,33 @@ private static boolean updateStatusAndInvokeTargetMethod(Connection conn, Method
boolean result = TCC_FENCE_DAO.updateTCCFenceDO(conn, xid, branchId, status, TCCFenceConstant.STATUS_TRIED);
if (result) {
// invoke two phase method
Object ret = method.invoke(targetTCCBean, args);
if (null != ret) {
if (ret instanceof TwoPhaseResult) {
result = ((TwoPhaseResult) ret).isSuccess();
} else {
result = (boolean) ret;
}
// If the business execution result is false, the transaction will be rolled back
if (!result) {
transactionStatus.setRollbackOnly();
}
result = invokeTwoPhaseRollback(method, targetTCCBean, transactionStatus, args);
}
return result;
}

/**
* invoke two phase method
* @param method the method
* @param targetTCCBean the targetTCCBean
* @param transactionStatus the transactionStatus
* @param args the args
* @return the boolean
* @throws IllegalAccessException the IllegalAccessException
* @throws InvocationTargetException the InvocationTargetException
*/
private static boolean invokeTwoPhaseRollback(Method method, Object targetTCCBean, TransactionStatus transactionStatus, Object[] args) throws IllegalAccessException, InvocationTargetException {
boolean result = true;
Object ret = method.invoke(targetTCCBean, args);
if (null != ret) {
if (ret instanceof TwoPhaseResult) {
result = ((TwoPhaseResult) ret).isSuccess();
} else {
result = (boolean) ret;
}
// If the business execution result is false, the transaction will be rolled back
if (!result) {
transactionStatus.setRollbackOnly();
}
}
return result;
Expand Down
4 changes: 3 additions & 1 deletion tcc/src/main/java/io/seata/rm/tcc/TCCResourceManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,10 @@ public BranchStatus branchRollback(BranchType branchType, String xid, long branc
// add idempotent and anti hanging
if (Boolean.TRUE.equals(businessActionContext.getActionContext(Constants.USE_TCC_FENCE))) {
try {
boolean enableEmptyRollback = Boolean.TRUE.equals(
businessActionContext.getActionContext(Constants.ENABLE_EMPTY_ROLLBACK));
result = TCCFenceHandler.rollbackFence(rollbackMethod, targetTCCBean, xid, branchId,
args, tccResource.getActionName());
args, tccResource.getActionName(), enableEmptyRollback);
} catch (SkipCallbackWrapperException | UndeclaredThrowableException e) {
throw e.getCause();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@
*/
boolean useTCCFence() default false;

/**
* whether use TCC fence and enableEmptyRollback is true.
* seata will not intercept empty rollback
* @return the boolean
*/
boolean enableEmptyRollback() default false;

/**
* commit method's args
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ protected void initBusinessContext(Map<String, Object> context, Method method,
context.put(Constants.ROLLBACK_METHOD, businessAction.rollbackMethod());
context.put(Constants.ACTION_NAME, businessAction.name());
context.put(Constants.USE_TCC_FENCE, businessAction.useTCCFence());
context.put(Constants.ENABLE_EMPTY_ROLLBACK, businessAction.enableEmptyRollback());
}
}

Expand Down