Skip to content

Commit

Permalink
bugfix: fix tcc fence dead lock (#6679)
Browse files Browse the repository at this point in the history
  • Loading branch information
chengliefeng committed Aug 19, 2024
1 parent b5c1801 commit ef83bc3
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 3 deletions.
10 changes: 10 additions & 0 deletions common/src/main/java/org/apache/seata/common/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -225,4 +225,14 @@ public interface Constants {
*/
String JACKSON_JSON_TEXT_PREFIX = "{\"@class\":";

/**
* The constant DEAD_LOCK_SQL_STATE
*/
String DEAD_LOCK_SQL_STATE = "40001";

/**
* The constant DEAD_LOCK_ERROR_CODE
*/
int DEAD_LOCK_ERROR_CODE = 1213;

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@

import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
Expand All @@ -28,6 +32,7 @@

import javax.sql.DataSource;

import org.apache.seata.common.Constants;
import org.apache.seata.common.exception.ExceptionUtil;
import org.apache.seata.common.exception.FrameworkErrorCode;
import org.apache.seata.common.exception.SkipCallbackWrapperException;
Expand All @@ -41,10 +46,14 @@
import org.apache.seata.integration.tx.api.fence.store.CommonFenceStore;
import org.apache.seata.integration.tx.api.fence.store.db.CommonFenceStoreDataBaseDAO;
import org.apache.seata.integration.tx.api.remoting.TwoPhaseResult;
import org.apache.seata.rm.tcc.api.BusinessActionContext;
import org.apache.seata.rm.tcc.api.BusinessActionContextUtil;
import org.apache.seata.rm.tcc.utils.MethodUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

/**
Expand Down Expand Up @@ -108,7 +117,8 @@ public static void setTransactionTemplate(TransactionTemplate transactionTemplat
*/
@Override
public Object prepareFence(String xid, Long branchId, String actionName, Callback<Object> targetCallback) {
return transactionTemplate.execute(status -> {
TransactionTemplate template = createTransactionTemplateForTransactionalMethod(null);
return template.execute(status -> {
try {
Connection conn = DataSourceUtils.getConnection(dataSource);
boolean result = insertCommonFenceLog(conn, xid, branchId, actionName, CommonFenceConstant.STATUS_TRIED);
Expand Down Expand Up @@ -146,7 +156,8 @@ public Object prepareFence(String xid, Long branchId, String actionName, Callbac
@Override
public boolean commitFence(Method commitMethod, Object targetTCCBean,
String xid, Long branchId, Object[] args) {
return transactionTemplate.execute(status -> {
TransactionTemplate template = createTransactionTemplateForTransactionalMethod(MethodUtils.getTransactionalAnnotationByMethod(commitMethod, targetTCCBean));
return template.execute(status -> {
try {
Connection conn = DataSourceUtils.getConnection(dataSource);
CommonFenceDO commonFenceDO = COMMON_FENCE_DAO.queryCommonFenceDO(conn, xid, branchId);
Expand Down Expand Up @@ -188,7 +199,8 @@ public boolean commitFence(Method commitMethod, Object targetTCCBean,
@Override
public boolean rollbackFence(Method rollbackMethod, Object targetTCCBean,
String xid, Long branchId, Object[] args, String actionName) {
return transactionTemplate.execute(status -> {
TransactionTemplate template = createTransactionTemplateForTransactionalMethod(MethodUtils.getTransactionalAnnotationByMethod(rollbackMethod, targetTCCBean));
return template.execute(status -> {
try {
Connection conn = DataSourceUtils.getConnection(dataSource);
CommonFenceDO commonFenceDO = COMMON_FENCE_DAO.queryCommonFenceDO(conn, xid, branchId);
Expand Down Expand Up @@ -218,6 +230,16 @@ public boolean rollbackFence(Method rollbackMethod, Object targetTCCBean,
return result;
} catch (Throwable t) {
status.setRollbackOnly();
Throwable cause = t.getCause();
if (cause != null && cause instanceof SQLException) {
SQLException sqlException = (SQLException) cause;
String sqlState = sqlException.getSQLState();
int errorCode = sqlException.getErrorCode();
if (Constants.DEAD_LOCK_SQL_STATE.equals(sqlState) && Constants.DEAD_LOCK_ERROR_CODE == errorCode) {
// MySQL deadlock exception
LOGGER.error("Common fence rollback fail. xid: {}, branchId: {}, This exception may be due to the deadlock caused by the transaction isolation level being Repeatable Read. The seata server will try to roll back again, so you can ignore this exception. (To avoid this exception, you can set transaction isolation to Read Committed.)", xid, branchId);
}
}
throw new SkipCallbackWrapperException(t);
}
});
Expand Down Expand Up @@ -353,6 +375,31 @@ private static void addToLogCleanQueue(final String xid, final long branchId) {
}
}

/**
* Creating a transactionTemplate with business transactional attributes
* @param transactional Transactional annotation
* @return
*/
private TransactionTemplate createTransactionTemplateForTransactionalMethod(Transactional transactional) {
Map<String, Object> businessActionContext = Optional.ofNullable(BusinessActionContextUtil.getContext()).map(BusinessActionContext::getActionContext).orElse(null);
if (transactional == null && businessActionContext == null) {
return transactionTemplate;
}
if (transactional != null) {
TransactionTemplate template = new TransactionTemplate(Objects.requireNonNull(transactionTemplate.getTransactionManager()));
template.setIsolationLevel(transactional.isolation().value());
return template;
} else {
boolean containIsolation = businessActionContext.containsKey(Constants.TX_ISOLATION);
if (!containIsolation) {
return transactionTemplate;
}
TransactionTemplate template = new TransactionTemplate(Objects.requireNonNull(transactionTemplate.getTransactionManager()));
template.setIsolationLevel((int) businessActionContext.get(Constants.TX_ISOLATION));
return template;
}
}

/**
* clean fence log that has the final status runnable.
*
Expand Down
10 changes: 10 additions & 0 deletions tcc/src/main/java/org/apache/seata/rm/tcc/TCCResourceManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ public BranchStatus branchCommit(BranchType branchType, String xid, long branchI
applicationData);

Object[] args = this.getTwoPhaseCommitArgs(tccResource, businessActionContext);
//share actionContext implicitly
BusinessActionContextUtil.setContext(businessActionContext);
Object ret;
boolean result;
// add idempotent and anti hanging
Expand Down Expand Up @@ -146,6 +148,9 @@ public BranchStatus branchCommit(BranchType branchType, String xid, long branchI
String msg = String.format("commit TCC resource error, resourceId: %s, xid: %s.", resourceId, xid);
LOGGER.error(msg, ExceptionUtil.unwrap(t));
return BranchStatus.PhaseTwo_CommitFailed_Retryable;
} finally {
// clear the action context
BusinessActionContextUtil.clear();
}
}

Expand Down Expand Up @@ -177,6 +182,8 @@ public BranchStatus branchRollback(BranchType branchType, String xid, long branc
BusinessActionContext businessActionContext = BusinessActionContextUtil.getBusinessActionContext(xid, branchId, resourceId,
applicationData);
Object[] args = this.getTwoPhaseRollbackArgs(tccResource, businessActionContext);
//share actionContext implicitly
BusinessActionContextUtil.setContext(businessActionContext);
Object ret;
boolean result;
// add idempotent and anti hanging
Expand Down Expand Up @@ -205,6 +212,9 @@ public BranchStatus branchRollback(BranchType branchType, String xid, long branc
String msg = String.format("rollback TCC resource error, resourceId: %s, xid: %s.", resourceId, xid);
LOGGER.error(msg, ExceptionUtil.unwrap(t));
return BranchStatus.PhaseTwo_RollbackFailed_Retryable;
} finally {
// clear the action context
BusinessActionContextUtil.clear();
}
}

Expand Down

0 comments on commit ef83bc3

Please sign in to comment.