This is an automated email from the ASF dual-hosted git repository. jianbin pushed a commit to branch 2.x in repository https://gitbox.apache.org/repos/asf/incubator-seata.git
The following commit(s) were added to refs/heads/2.x by this push: new 5601c36217 bugfix: fix tcc dead lock (#6769) 5601c36217 is described below commit 5601c36217e3f5dd0a84bd66f61bd7c0d6b5076d Author: iAmClever <158091...@qq.com> AuthorDate: Thu Aug 22 17:44:30 2024 +0800 bugfix: fix tcc dead lock (#6769) --- changes/en-us/2.x.md | 1 + changes/zh-cn/2.x.md | 2 +- .../java/org/apache/seata/common/Constants.java | 10 ++++ .../apache/seata/rm/fence/SpringFenceHandler.java | 53 ++++++++++++++++++++-- .../apache/seata/rm/tcc/TCCResourceManager.java | 10 ++++ 5 files changed, 72 insertions(+), 4 deletions(-) diff --git a/changes/en-us/2.x.md b/changes/en-us/2.x.md index bbc5cb504a..899fc78356 100644 --- a/changes/en-us/2.x.md +++ b/changes/en-us/2.x.md @@ -24,6 +24,7 @@ Add changes here for all PR submitted to the 2.x branch. - [[#6714](https://github.com/apache/incubator-seata/pull/6714)] fix dameng delete undo fail - [[#6701](https://github.com/apache/incubator-seata/pull/6728)] fix support serialization for dm.jdbc.driver.DmdbTimestamp - [[#6757](https://github.com/apache/incubator-seata/pull/6757)] the bug where multiple nodes cannot be retrieved from the naming server +- [[#6769](https://github.com/apache/incubator-seata/pull/6769)] fix tcc fence deadLock ### optimize: diff --git a/changes/zh-cn/2.x.md b/changes/zh-cn/2.x.md index 0b98b0fd8a..0371543ade 100644 --- a/changes/zh-cn/2.x.md +++ b/changes/zh-cn/2.x.md @@ -25,7 +25,7 @@ - [[#6714](https://github.com/apache/incubator-seata/pull/6714)] 修复达梦数据库的delete sql回滚失败的问题 - [[#6701](https://github.com/apache/incubator-seata/pull/6728)] 修复达梦数据库的对dm.jdbc.driver.DmdbTimestamp的支持 - [[#6757](https://github.com/apache/incubator-seata/pull/6757)] 修复client通过namingserver只能获取到一个tc节点的bug - +- [[#6769](https://github.com/apache/incubator-seata/pull/6769)] 修复tcc fence死锁 ### optimize: - [[#6499](https://github.com/apache/incubator-seata/pull/6499)] 拆分 committing 和 rollbacking 状态的任务线程池 diff --git a/common/src/main/java/org/apache/seata/common/Constants.java b/common/src/main/java/org/apache/seata/common/Constants.java index d3a0ab512d..43da1827e0 100644 --- a/common/src/main/java/org/apache/seata/common/Constants.java +++ b/common/src/main/java/org/apache/seata/common/Constants.java @@ -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; + } diff --git a/spring/src/main/java/org/apache/seata/rm/fence/SpringFenceHandler.java b/spring/src/main/java/org/apache/seata/rm/fence/SpringFenceHandler.java index 95ada3dc09..88003f6f4f 100644 --- a/spring/src/main/java/org/apache/seata/rm/fence/SpringFenceHandler.java +++ b/spring/src/main/java/org/apache/seata/rm/fence/SpringFenceHandler.java @@ -18,8 +18,12 @@ package org.apache.seata.rm.fence; 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; @@ -28,6 +32,7 @@ import java.util.concurrent.TimeUnit; 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; @@ -41,10 +46,14 @@ import org.apache.seata.integration.tx.api.fence.store.CommonFenceDO; 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; /** @@ -108,7 +117,8 @@ public class SpringFenceHandler implements FenceHandler { */ @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); @@ -146,7 +156,8 @@ public class SpringFenceHandler implements FenceHandler { @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); @@ -188,7 +199,8 @@ public class SpringFenceHandler implements FenceHandler { @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); @@ -218,6 +230,16 @@ public class SpringFenceHandler implements FenceHandler { 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); } }); @@ -353,6 +375,31 @@ public class SpringFenceHandler implements FenceHandler { } } + /** + * 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. * diff --git a/tcc/src/main/java/org/apache/seata/rm/tcc/TCCResourceManager.java b/tcc/src/main/java/org/apache/seata/rm/tcc/TCCResourceManager.java index eaaa76494c..2ad3c2b373 100644 --- a/tcc/src/main/java/org/apache/seata/rm/tcc/TCCResourceManager.java +++ b/tcc/src/main/java/org/apache/seata/rm/tcc/TCCResourceManager.java @@ -119,6 +119,8 @@ public class TCCResourceManager extends AbstractResourceManager { applicationData); Object[] args = this.getTwoPhaseCommitArgs(tccResource, businessActionContext); + //share actionContext implicitly + BusinessActionContextUtil.setContext(businessActionContext); Object ret; boolean result; // add idempotent and anti hanging @@ -146,6 +148,9 @@ public class TCCResourceManager extends AbstractResourceManager { 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(); } } @@ -177,6 +182,8 @@ public class TCCResourceManager extends AbstractResourceManager { 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 @@ -205,6 +212,9 @@ public class TCCResourceManager extends AbstractResourceManager { 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(); } } --------------------------------------------------------------------- To unsubscribe, e-mail: notifications-unsubscr...@seata.apache.org For additional commands, e-mail: notifications-h...@seata.apache.org