funky-eyes commented on code in PR #6706: URL: https://github.com/apache/incubator-seata/pull/6706#discussion_r1694448684
########## core/src/main/java/org/apache/seata/core/context/RootContext.java: ########## @@ -60,6 +61,26 @@ private RootContext() { */ public static final String KEY_TIMEOUT = "TX_TIMEOUT"; + /** + * The constant KEY_RESOURCE_ID. + */ + public static final String KEY_RESOURCE_ID = "TX_RESOURCE_ID"; Review Comment: Rootcontext不应该用来存放这些数据 Rootcontext should not be used to store this data ########## spring/src/main/java/org/apache/seata/rm/fence/SpringFenceHandler.java: ########## @@ -41,12 +31,32 @@ 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.fence.interceptor.TccFenceCommitInterceptor; +import org.apache.seata.rm.fence.interceptor.TccFencePrepareInterceptor; +import org.apache.seata.rm.fence.interceptor.TccFenceRollbackInterceptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.aop.Advisor; +import org.springframework.aop.framework.Advised; import org.springframework.jdbc.datasource.DataSourceUtils; import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.interceptor.TransactionAttributeSource; +import org.springframework.transaction.interceptor.TransactionInterceptor; +import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.transaction.support.TransactionTemplate; +import javax.sql.DataSource; Review Comment: 同上 ditto ########## spring/src/main/java/org/apache/seata/rm/fence/SpringFenceHandler.java: ########## @@ -187,40 +235,111 @@ 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 -> { + String xid, Long branchId, Object[] args, String actionName) { + if (hasTransactionAspectBeforeInterceptor(rollbackMethod, targetTCCBean, TccFenceRollbackInterceptor.class)) { try { - Connection conn = DataSourceUtils.getConnection(dataSource); - CommonFenceDO commonFenceDO = COMMON_FENCE_DAO.queryCommonFenceDO(conn, xid, branchId); - // non_rollback - if (commonFenceDO == null) { - boolean result = insertCommonFenceLog(conn, xid, branchId, actionName, CommonFenceConstant.STATUS_SUSPENDED); - LOGGER.info("Insert common fence record result: {}. xid: {}, branchId: {}", result, xid, branchId); - if (!result) { - throw new CommonFenceException(String.format("Insert common fence record error, rollback fence method failed. xid= %s, branchId= %s", xid, branchId), - FrameworkErrorCode.InsertRecordError); - } - return true; - } else { - if (CommonFenceConstant.STATUS_ROLLBACKED == commonFenceDO.getStatus() || CommonFenceConstant.STATUS_SUSPENDED == commonFenceDO.getStatus()) { - LOGGER.info("Branch transaction had already rollbacked before, idempotency rejected. xid: {}, branchId: {}, status: {}", xid, branchId, commonFenceDO.getStatus()); + // The tcc transaction is activated on the service side. + RootContext.bindTccLocalTxActive(TccLocalTxActive.ACTIVE); + RootContext.bindTccRollbackResult(false); + rollbackMethod.invoke(targetTCCBean, args); + Boolean result = RootContext.getTccRollbackResult(); + return result; + } catch (Exception e) { + throw new SkipCallbackWrapperException(ExceptionUtil.unwrap(e)); + } finally { + RootContext.unbindTccLocalTxActive(); + RootContext.unbindTccRollbackResult(); + } + } else { + return transactionTemplate.execute(status -> { + try { + // The tcc transaction is not activated on the service side. + RootContext.bindTccLocalTxActive(TccLocalTxActive.UN_ACTIVE); + Connection conn = DataSourceUtils.getConnection(dataSource); + CommonFenceDO commonFenceDO = COMMON_FENCE_DAO.queryCommonFenceDO(conn, xid, branchId); + // non_rollback + if (commonFenceDO == null) { + boolean result = insertCommonFenceLog(conn, xid, branchId, actionName, CommonFenceConstant.STATUS_SUSPENDED); + LOGGER.info("Insert common fence record result: {}. xid: {}, branchId: {}", result, xid, branchId); + if (!result) { + throw new CommonFenceException(String.format("Insert common fence record error, rollback fence method failed. xid= %s, branchId= %s", xid, branchId), + FrameworkErrorCode.InsertRecordError); + } return true; + } else { + if (CommonFenceConstant.STATUS_ROLLBACKED == commonFenceDO.getStatus() || CommonFenceConstant.STATUS_SUSPENDED == commonFenceDO.getStatus()) { + LOGGER.info("Branch transaction had already rollbacked before, idempotency rejected. xid: {}, branchId: {}, status: {}", xid, branchId, commonFenceDO.getStatus()); + return true; + } + if (CommonFenceConstant.STATUS_COMMITTED == commonFenceDO.getStatus()) { + if (LOGGER.isWarnEnabled()) { + LOGGER.warn("Branch transaction status is unexpected. xid: {}, branchId: {}, status: {}", xid, branchId, commonFenceDO.getStatus()); + } + return false; + } } - if (CommonFenceConstant.STATUS_COMMITTED == commonFenceDO.getStatus()) { - if (LOGGER.isWarnEnabled()) { - LOGGER.warn("Branch transaction status is unexpected. xid: {}, branchId: {}, status: {}", xid, branchId, commonFenceDO.getStatus()); + boolean result = updateStatusAndInvokeTargetMethod(conn, rollbackMethod, targetTCCBean, xid, branchId, CommonFenceConstant.STATUS_ROLLBACKED, status, args); + LOGGER.info("Common fence rollback result: {}. xid: {}, branchId: {}", result, xid, branchId); + 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 ("40001".equals(sqlState) && errorCode == 1213) { + // 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); } - return false; } + throw new SkipCallbackWrapperException(t); + } finally { + RootContext.unbindTccLocalTxActive(); + } + }); + } + } + + /** + * Check whether the transaction has been activated + * @return boolean + */ + public static boolean isTransactionActive() { + return TransactionSynchronizationManager.isActualTransactionActive(); + } + + /** + * Check that the business method's transaction interceptor precedes the target interceptor + * @param method business method + * @param proxy proxy + * @param targetInterceptorClass the target interceptor class + * @return boolean + */ + public static boolean hasTransactionAspectBeforeInterceptor(Method method, Object proxy, Class<?> targetInterceptorClass) { Review Comment: 为什么不在代理bean的时候就初始化好一切信息? Why not initialize everything in the proxy bean? ########## integration-tx-api/src/main/java/org/apache/seata/integration/tx/api/interceptor/ActionInterceptorHandler.java: ########## @@ -42,6 +34,14 @@ import org.slf4j.LoggerFactory; import org.slf4j.MDC; +import javax.annotation.Nonnull; Review Comment: java的包应该在顶层 Java packages should be at the top level -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: notifications-unsubscr...@seata.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org --------------------------------------------------------------------- To unsubscribe, e-mail: notifications-unsubscr...@seata.apache.org For additional commands, e-mail: notifications-h...@seata.apache.org