diff --git a/docker/skywalking/agent/plugins/apm-seata-skywalking-plugin-1.7.0.jar b/docker/skywalking/agent/plugins/apm-seata-skywalking-plugin-1.7.0.jar new file mode 100644 index 00000000..2e944fa4 Binary files /dev/null and b/docker/skywalking/agent/plugins/apm-seata-skywalking-plugin-1.7.0.jar differ diff --git a/ruoyi-common/ruoyi-common-alibaba-bom/pom.xml b/ruoyi-common/ruoyi-common-alibaba-bom/pom.xml index 94dc52c2..9ebb121a 100644 --- a/ruoyi-common/ruoyi-common-alibaba-bom/pom.xml +++ b/ruoyi-common/ruoyi-common-alibaba-bom/pom.xml @@ -17,7 +17,7 @@ 2.1.0-SNAPSHOT 2022.0.0.0-RC2 1.8.6 - 1.6.1 + 1.7.0 2.2.1 3.2.4 1.0.11 diff --git a/ruoyi-common/ruoyi-common-seata/src/main/java/io/seata/spring/util/SpringProxyUtils.java b/ruoyi-common/ruoyi-common-seata/src/main/java/io/seata/spring/util/SpringProxyUtils.java deleted file mode 100644 index 483d7c83..00000000 --- a/ruoyi-common/ruoyi-common-seata/src/main/java/io/seata/spring/util/SpringProxyUtils.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright 1999-2019 Seata.io Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.seata.spring.util; - -import io.seata.common.util.CollectionUtils; -import io.seata.rm.tcc.remoting.parser.DubboUtil; -import org.springframework.aop.TargetSource; -import org.springframework.aop.framework.Advised; -import org.springframework.aop.framework.AdvisedSupport; -import org.springframework.aop.support.AopUtils; - -import java.lang.reflect.Field; -import java.lang.reflect.Proxy; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -/** - * Proxy tools base on spring - * - * 临时修复 seata 适配 jdk17 反射bug - * - * @author zhangsen - */ -public class SpringProxyUtils { - private SpringProxyUtils() { - } - - /** - * Find target class class. - * - * @param proxy the proxy - * @return the class - * @throws Exception the exception - */ - public static Class findTargetClass(Object proxy) throws Exception { - if (proxy == null) { - return null; - } - if (AopUtils.isAopProxy(proxy) && proxy instanceof Advised) { - // #issue 3709 - final TargetSource targetSource = ((Advised) proxy).getTargetSource(); - if (!targetSource.isStatic()) { - return targetSource.getTargetClass(); - } - return findTargetClass(targetSource.getTarget()); - } - return proxy.getClass(); - } - - public static Class[] findInterfaces(Object proxy) throws Exception { - if (AopUtils.isJdkDynamicProxy(proxy)) { - AdvisedSupport advised = getAdvisedSupport(proxy); - return getInterfacesByAdvised(advised); - } else { - return new Class[]{}; - } - } - - private static Class[] getInterfacesByAdvised(AdvisedSupport advised) { - Class[] interfaces = advised.getProxiedInterfaces(); - if (interfaces.length > 0) { - return interfaces; - } else { - throw new IllegalStateException("Find the jdk dynamic proxy class that does not implement the interface"); - } - } - - /** - * Gets advised support. - * - * @param proxy the proxy - * @return the advised support - * @throws Exception the exception - */ - public static AdvisedSupport getAdvisedSupport(Object proxy) throws Exception { - Object dynamicAdvisedInterceptor; - if (AopUtils.isJdkDynamicProxy(proxy)) { - dynamicAdvisedInterceptor = Proxy.getInvocationHandler(proxy); - } else { - Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0"); - h.setAccessible(true); - dynamicAdvisedInterceptor = h.get(proxy); - } - Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised"); - advised.setAccessible(true); - return (AdvisedSupport)advised.get(dynamicAdvisedInterceptor); - } - - /** - * Is proxy boolean. - * - * @param bean the bean - * @return the boolean - */ - public static boolean isProxy(Object bean) { - if (bean == null) { - return false; - } - //check dubbo proxy ? - return DubboUtil.isDubboProxyName(bean.getClass().getName()) || (Proxy.class.isAssignableFrom(bean.getClass()) - || AopUtils.isAopProxy(bean)); - } - - /** - * Get the target class , get the interface of its agent if it is a Proxy - * - * @param proxy the proxy - * @return target interface - * @throws Exception the exception - */ - public static Class getTargetInterface(Object proxy) throws Exception { - if (proxy == null) { - throw new java.lang.IllegalArgumentException("proxy can not be null"); - } - - //jdk proxy - if (Proxy.class.isAssignableFrom(proxy.getClass())) { - Proxy p = (Proxy)proxy; - return p.getClass().getInterfaces()[0]; - } - - return getTargetClass(proxy); - } - - /** - * Get the class type of the proxy target object, if hadn't a target object, return the interface of the proxy - * - * @param proxy the proxy - * @return target interface - * @throws Exception the exception - */ - protected static Class getTargetClass(Object proxy) throws Exception { - if (proxy == null) { - throw new java.lang.IllegalArgumentException("proxy can not be null"); - } - //not proxy - if (!AopUtils.isAopProxy(proxy)) { - return proxy.getClass(); - } - AdvisedSupport advisedSupport = getAdvisedSupport(proxy); - Object target = advisedSupport.getTargetSource().getTarget(); - /* - * the Proxy of sofa:reference has no target - */ - if (target == null) { - if (CollectionUtils.isNotEmpty(advisedSupport.getProxiedInterfaces())) { - return advisedSupport.getProxiedInterfaces()[0]; - } else { - return proxy.getClass(); - } - } else { - return getTargetClass(target); - } - } - - /** - * get the all interfaces of bean, if the bean is null, then return empty array - * @param bean the bean - * @return target interface - */ - public static Class[] getAllInterfaces(Object bean) { - Set> interfaces = new HashSet<>(); - if (bean != null) { - Class clazz = bean.getClass(); - while (!Object.class.getName().equalsIgnoreCase(clazz.getName())) { - Class[] clazzInterfaces = clazz.getInterfaces(); - interfaces.addAll(Arrays.asList(clazzInterfaces)); - clazz = clazz.getSuperclass(); - } - } - return interfaces.toArray(new Class[0]); - } - -} diff --git a/ruoyi-visual/ruoyi-seata-server/pom.xml b/ruoyi-visual/ruoyi-seata-server/pom.xml index 7fce826b..4ae55cd9 100644 --- a/ruoyi-visual/ruoyi-seata-server/pom.xml +++ b/ruoyi-visual/ruoyi-seata-server/pom.xml @@ -27,10 +27,11 @@ jar - 1.6.1 - 1.72 + 1.7.0 + 1.82 1.2.12 - 2.7.12 + 2.7.14 + 0.9.20 @@ -196,7 +197,10 @@ net.logstash.logback logstash-logback-encoder - + + org.codehaus.janino + janino + diff --git a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/SeataServerApplication.java b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/SeataServerApplication.java index b8046178..729e32e7 100644 --- a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/SeataServerApplication.java +++ b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/SeataServerApplication.java @@ -15,18 +15,35 @@ */ package io.seata.server; +import io.seata.common.aot.NativeUtils; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import java.io.IOException; - /** * @author spilledyear@outlook.com */ @SpringBootApplication(scanBasePackages = {"io.seata"}) public class SeataServerApplication { - public static void main(String[] args) throws IOException { - // run the spring-boot application - SpringApplication.run(SeataServerApplication.class, args); + + public static void main(String[] args) throws Throwable { + try { + // run the spring-boot application + SpringApplication.run(SeataServerApplication.class, args); + } catch (Throwable t) { + // This exception is used to end `spring-boot-maven-plugin:process-aot`, so ignore it. + if ("org.springframework.boot.SpringApplication$AbandonedRunException".equals(t.getClass().getName())) { + throw t; + } + + // In the `native-image`, if an exception occurs prematurely during the startup process, the exception log will not be recorded, + // so here we sleep for 20 seconds to observe the exception information. + if (NativeUtils.inNativeImage()) { + t.printStackTrace(); + Thread.sleep(20000); + } + + throw t; + } } + } diff --git a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/ServerRunner.java b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/ServerRunner.java index d19233a0..30343405 100644 --- a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/ServerRunner.java +++ b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/ServerRunner.java @@ -19,7 +19,12 @@ import io.seata.core.rpc.Disposable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.web.context.WebServerInitializedEvent; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.core.Ordered; import org.springframework.stereotype.Component; import java.util.List; @@ -30,12 +35,18 @@ import java.util.concurrent.CopyOnWriteArrayList; * @author spilledyear@outlook.com */ @Component -public class ServerRunner implements CommandLineRunner, DisposableBean { +public class ServerRunner implements CommandLineRunner, DisposableBean, + ApplicationListener, Ordered { private static final Logger LOGGER = LoggerFactory.getLogger(ServerRunner.class); private boolean started = Boolean.FALSE; + private int port; + + @Value("${logging.file.path}") + private String logPath; + private static final List DISPOSABLE_LIST = new CopyOnWriteArrayList<>(); public static void addDisposable(Disposable disposable) { @@ -50,6 +61,7 @@ public class ServerRunner implements CommandLineRunner, DisposableBean { started = true; long cost = System.currentTimeMillis() - start; + LOGGER.info("\r\n you can visit seata console UI on http://127.0.0.1:{}. \r\n log path: {}.", this.port, this.logPath); LOGGER.info("seata server started in {} millSeconds", cost); } catch (Throwable e) { started = Boolean.FALSE; @@ -78,4 +90,16 @@ public class ServerRunner implements CommandLineRunner, DisposableBean { LOGGER.debug("destoryAll finish"); } } + + @Override + public void onApplicationEvent(ApplicationEvent event) { + if (event instanceof WebServerInitializedEvent) { + this.port = ((WebServerInitializedEvent)event).getWebServer().getPort(); + } + } + + @Override + public int getOrder() { + return Ordered.LOWEST_PRECEDENCE; + } } diff --git a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/console/controller/BranchSessionController.java b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/console/controller/BranchSessionController.java index f5e7a89d..f3ebf3f6 100644 --- a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/console/controller/BranchSessionController.java +++ b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/console/controller/BranchSessionController.java @@ -23,6 +23,7 @@ import javax.annotation.Resource; /** * Branch Session Controller + * * @author zhongxiang.wang */ @RestController @@ -32,5 +33,4 @@ public class BranchSessionController { @Resource(type = BranchSessionService.class) private BranchSessionService branchSessionService; - } diff --git a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/console/controller/GlobalLockController.java b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/console/controller/GlobalLockController.java index d8ea221c..e5fc3d01 100644 --- a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/console/controller/GlobalLockController.java +++ b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/console/controller/GlobalLockController.java @@ -26,9 +26,9 @@ import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; - /** * Global Lock Controller + * * @author zhongxiang.wang */ @RestController @@ -40,6 +40,7 @@ public class GlobalLockController { /** * Query locks by param + * * @param param the param * @return the list of GlobalLockVO */ diff --git a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/console/controller/GlobalSessionController.java b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/console/controller/GlobalSessionController.java index db34ace8..14335479 100644 --- a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/console/controller/GlobalSessionController.java +++ b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/console/controller/GlobalSessionController.java @@ -28,6 +28,7 @@ import javax.annotation.Resource; /** * Global Session Controller + * * @author zhongxiang.wang */ @RestController @@ -39,8 +40,9 @@ public class GlobalSessionController { /** * Query all globalSession + * * @param param param for query globalSession - * @return the list of GlobalSessionVO + * @return the list of GlobalSessionVO */ @GetMapping("query") public PageResult query(@ModelAttribute GlobalSessionParam param) { diff --git a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/console/impl/file/GlobalLockFileServiceImpl.java b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/console/impl/file/GlobalLockFileServiceImpl.java index 59d91540..d3c3f18b 100644 --- a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/console/impl/file/GlobalLockFileServiceImpl.java +++ b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/console/impl/file/GlobalLockFileServiceImpl.java @@ -79,6 +79,9 @@ public class GlobalLockFileServiceImpl implements GlobalLockService { * @return the RowLock list */ private Stream filterAndMap(GlobalLockParam param, BranchSession branchSession) { + if (CollectionUtils.isEmpty(branchSession.getLockHolder())) { + return Stream.empty(); + } final String tableName = param.getTableName(); diff --git a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/coordinator/AbstractCore.java b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/coordinator/AbstractCore.java index e5d0b3b8..70477c33 100644 --- a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/coordinator/AbstractCore.java +++ b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/coordinator/AbstractCore.java @@ -167,8 +167,9 @@ public abstract class AbstractCore implements Core { protected BranchStatus branchCommitSend(BranchCommitRequest request, GlobalSession globalSession, BranchSession branchSession) throws IOException, TimeoutException { + BranchCommitResponse response = (BranchCommitResponse) remotingServer.sendSyncRequest( - branchSession.getResourceId(), branchSession.getClientId(), request); + branchSession.getResourceId(), branchSession.getClientId(), request, branchSession.isAT()); return response.getBranchStatus(); } @@ -191,8 +192,9 @@ public abstract class AbstractCore implements Core { protected BranchStatus branchRollbackSend(BranchRollbackRequest request, GlobalSession globalSession, BranchSession branchSession) throws IOException, TimeoutException { + BranchRollbackResponse response = (BranchRollbackResponse) remotingServer.sendSyncRequest( - branchSession.getResourceId(), branchSession.getClientId(), request); + branchSession.getResourceId(), branchSession.getClientId(), request, branchSession.isAT()); return response.getBranchStatus(); } diff --git a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/coordinator/DefaultCoordinator.java b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/coordinator/DefaultCoordinator.java index 01223e82..910af84e 100644 --- a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/coordinator/DefaultCoordinator.java +++ b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/coordinator/DefaultCoordinator.java @@ -18,7 +18,6 @@ package io.seata.server.coordinator; import io.netty.channel.Channel; import io.seata.common.thread.NamedThreadFactory; import io.seata.common.util.CollectionUtils; -import io.seata.common.util.DurationUtil; import io.seata.config.ConfigurationFactory; import io.seata.core.constants.ConfigurationKeys; import io.seata.core.context.RootContext; @@ -36,11 +35,12 @@ import io.seata.core.rpc.netty.NettyRemotingServer; import io.seata.server.AbstractTCInboundHandler; import io.seata.server.metrics.MetricsPublisher; import io.seata.server.session.*; +import io.seata.server.store.StoreConfig; +import org.apache.commons.lang.time.DateFormatUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; -import java.time.Duration; import java.util.Collection; import java.util.Map; import java.util.concurrent.ArrayBlockingQueue; @@ -105,13 +105,13 @@ public class DefaultCoordinator extends AbstractTCInboundHandler implements Tran /** * the pool size of branch asynchronous remove thread pool */ - private static final int BRANCH_ASYNC_POOL_SIZE = Runtime.getRuntime().availableProcessors(); + private static final int BRANCH_ASYNC_POOL_SIZE = Runtime.getRuntime().availableProcessors() * 2; - private static final Duration MAX_COMMIT_RETRY_TIMEOUT = ConfigurationFactory.getInstance().getDuration( - ConfigurationKeys.MAX_COMMIT_RETRY_TIMEOUT, DurationUtil.DEFAULT_DURATION, DEFAULT_MAX_COMMIT_RETRY_TIMEOUT); + private static final long MAX_COMMIT_RETRY_TIMEOUT = ConfigurationFactory.getInstance().getLong( + ConfigurationKeys.MAX_COMMIT_RETRY_TIMEOUT, DEFAULT_MAX_COMMIT_RETRY_TIMEOUT); - private static final Duration MAX_ROLLBACK_RETRY_TIMEOUT = ConfigurationFactory.getInstance().getDuration( - ConfigurationKeys.MAX_ROLLBACK_RETRY_TIMEOUT, DurationUtil.DEFAULT_DURATION, DEFAULT_MAX_ROLLBACK_RETRY_TIMEOUT); + private static final long MAX_ROLLBACK_RETRY_TIMEOUT = ConfigurationFactory.getInstance().getLong( + ConfigurationKeys.MAX_ROLLBACK_RETRY_TIMEOUT, DEFAULT_MAX_ROLLBACK_RETRY_TIMEOUT); private static final boolean ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE = ConfigurationFactory.getInstance().getBoolean( ConfigurationKeys.ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE, DEFAULT_ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE); @@ -134,15 +134,9 @@ public class DefaultCoordinator extends AbstractTCInboundHandler implements Tran private final GlobalStatus[] rollbackingStatuses = new GlobalStatus[] {GlobalStatus.TimeoutRollbacking, GlobalStatus.TimeoutRollbackRetrying, GlobalStatus.RollbackRetrying, GlobalStatus.Rollbacking}; - private final GlobalStatus[] retryCommittingStatuses = - new GlobalStatus[] {GlobalStatus.Committing, GlobalStatus.CommitRetrying}; + private final GlobalStatus[] retryCommittingStatuses = new GlobalStatus[] {GlobalStatus.Committing, GlobalStatus.CommitRetrying, GlobalStatus.Committed}; - private final ThreadPoolExecutor branchRemoveExecutor = new ThreadPoolExecutor(BRANCH_ASYNC_POOL_SIZE, BRANCH_ASYNC_POOL_SIZE, - Integer.MAX_VALUE, TimeUnit.MILLISECONDS, - new ArrayBlockingQueue<>( - CONFIG.getInt(ConfigurationKeys.SESSION_BRANCH_ASYNC_QUEUE_SIZE, DEFAULT_BRANCH_ASYNC_QUEUE_SIZE) - ), new NamedThreadFactory("branchSessionRemove", BRANCH_ASYNC_POOL_SIZE), - new ThreadPoolExecutor.CallerRunsPolicy()); + private final ThreadPoolExecutor branchRemoveExecutor; private RemotingServer remotingServer; @@ -161,6 +155,19 @@ public class DefaultCoordinator extends AbstractTCInboundHandler implements Tran } this.remotingServer = remotingServer; this.core = new DefaultCore(remotingServer); + boolean enableBranchAsyncRemove = CONFIG.getBoolean( + ConfigurationKeys.ENABLE_BRANCH_ASYNC_REMOVE, DEFAULT_ENABLE_BRANCH_ASYNC_REMOVE); + // create branchRemoveExecutor + if (enableBranchAsyncRemove && StoreConfig.getSessionMode() != StoreConfig.SessionMode.FILE) { + branchRemoveExecutor = new ThreadPoolExecutor(BRANCH_ASYNC_POOL_SIZE, BRANCH_ASYNC_POOL_SIZE, + Integer.MAX_VALUE, TimeUnit.MILLISECONDS, + new ArrayBlockingQueue<>( + CONFIG.getInt(ConfigurationKeys.SESSION_BRANCH_ASYNC_QUEUE_SIZE, DEFAULT_BRANCH_ASYNC_QUEUE_SIZE) + ), new NamedThreadFactory("branchSessionRemove", BRANCH_ASYNC_POOL_SIZE), + new ThreadPoolExecutor.CallerRunsPolicy()); + } else { + branchRemoveExecutor = null; + } } public static DefaultCoordinator getInstance(RemotingServer remotingServer) { @@ -296,7 +303,8 @@ public class DefaultCoordinator extends AbstractTCInboundHandler implements Tran return false; } - LOGGER.info("Global transaction[{}] is timeout and will be rollback.", globalSession.getXid()); + LOGGER.warn("Global transaction[{}] is timeout and will be rollback,transaction begin time:{} and now:{}", globalSession.getXid(), + DateFormatUtils.ISO_DATE_FORMAT.format(globalSession.getBeginTime()), DateFormatUtils.ISO_DATE_FORMAT.format(System.currentTimeMillis())); globalSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); globalSession.close(); @@ -338,13 +346,10 @@ public class DefaultCoordinator extends AbstractTCInboundHandler implements Tran // The function of this 'return' is 'continue'. return; } - if (isRetryTimeout(now, MAX_ROLLBACK_RETRY_TIMEOUT.toMillis(), rollbackingSession.getBeginTime())) { + if (isRetryTimeout(now, MAX_ROLLBACK_RETRY_TIMEOUT, rollbackingSession.getBeginTime())) { if (ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE) { rollbackingSession.clean(); } - // Prevent thread safety issues - SessionHolder.getRetryRollbackingSessionManager().removeGlobalSession(rollbackingSession); - LOGGER.error("Global transaction rollback retry timeout and has removed [{}]", rollbackingSession.getXid()); SessionHelper.endRollbackFailed(rollbackingSession, true, true); @@ -354,7 +359,7 @@ public class DefaultCoordinator extends AbstractTCInboundHandler implements Tran rollbackingSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); core.doGlobalRollback(rollbackingSession, true); } catch (TransactionException ex) { - LOGGER.info("Failed to retry rollbacking [{}] {} {}", rollbackingSession.getXid(), ex.getCode(), ex.getMessage()); + LOGGER.error("Failed to retry rollbacking [{}] {} {}", rollbackingSession.getXid(), ex.getCode(), ex.getMessage()); } }); } @@ -374,15 +379,11 @@ public class DefaultCoordinator extends AbstractTCInboundHandler implements Tran SessionHelper.forEach(committingSessions, committingSession -> { try { // prevent repeated commit - if (committingSession.getStatus() == GlobalStatus.Committing - && !committingSession.isDeadSession()) { + if (GlobalStatus.Committing.equals(committingSession.getStatus()) && !committingSession.isDeadSession()) { // The function of this 'return' is 'continue'. return; } - if (isRetryTimeout(now, MAX_COMMIT_RETRY_TIMEOUT.toMillis(), committingSession.getBeginTime())) { - // Prevent thread safety issues - SessionHolder.getRetryCommittingSessionManager().removeGlobalSession(committingSession); - LOGGER.error("Global transaction commit retry timeout and has removed [{}]", committingSession.getXid()); + if (isRetryTimeout(now, MAX_COMMIT_RETRY_TIMEOUT, committingSession.getBeginTime())) { // commit retry timeout event SessionHelper.endCommitFailed(committingSession, true, true); @@ -390,10 +391,14 @@ public class DefaultCoordinator extends AbstractTCInboundHandler implements Tran //The function of this 'return' is 'continue'. return; } + if (GlobalStatus.Committed.equals(committingSession.getStatus()) + && committingSession.getBranchSessions().isEmpty()) { + SessionHelper.endCommitted(committingSession,true); + } committingSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); core.doGlobalCommit(committingSession, true); } catch (TransactionException ex) { - LOGGER.info("Failed to retry committing [{}] {} {}", committingSession.getXid(), ex.getCode(), ex.getMessage()); + LOGGER.error("Failed to retry committing [{}] {} {}", committingSession.getXid(), ex.getCode(), ex.getMessage()); } }); } @@ -500,14 +505,18 @@ public class DefaultCoordinator extends AbstractTCInboundHandler implements Tran asyncCommitting.shutdown(); timeoutCheck.shutdown(); undoLogDelete.shutdown(); - branchRemoveExecutor.shutdown(); + if (branchRemoveExecutor != null) { + branchRemoveExecutor.shutdown(); + } try { retryRollbacking.awaitTermination(TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS, TimeUnit.MILLISECONDS); retryCommitting.awaitTermination(TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS, TimeUnit.MILLISECONDS); asyncCommitting.awaitTermination(TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS, TimeUnit.MILLISECONDS); timeoutCheck.awaitTermination(TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS, TimeUnit.MILLISECONDS); undoLogDelete.awaitTermination(TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS, TimeUnit.MILLISECONDS); - branchRemoveExecutor.awaitTermination(TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS, TimeUnit.MILLISECONDS); + if (branchRemoveExecutor != null) { + branchRemoveExecutor.awaitTermination(TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS, TimeUnit.MILLISECONDS); + } } catch (InterruptedException ignore) { } @@ -549,6 +558,9 @@ public class DefaultCoordinator extends AbstractTCInboundHandler implements Tran */ public BranchRemoveTask(GlobalSession globalSession, BranchSession branchSession) { this.globalSession = globalSession; + if (branchSession == null) { + throw new IllegalArgumentException("BranchSession can`t be null!"); + } this.branchSession = branchSession; } diff --git a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/coordinator/DefaultCore.java b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/coordinator/DefaultCore.java index 62fb0873..bfd52711 100644 --- a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/coordinator/DefaultCore.java +++ b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/coordinator/DefaultCore.java @@ -216,6 +216,7 @@ public class DefaultCore implements Core { switch (branchStatus) { case PhaseTwo_Committed: SessionHelper.removeBranch(globalSession, branchSession, !retrying); + LOGGER.info("Commit branch transaction successfully, xid = {} branchId = {}", globalSession.getXid(), branchSession.getBranchId()); return CONTINUE; case PhaseTwo_CommitFailed_Unretryable: //not at branch @@ -324,10 +325,10 @@ public class DefaultCore implements Core { return CONTINUE; case PhaseTwo_RollbackFailed_Unretryable: SessionHelper.endRollbackFailed(globalSession, retrying); - LOGGER.info("Rollback branch transaction fail and stop retry, xid = {} branchId = {}", globalSession.getXid(), branchSession.getBranchId()); + LOGGER.error("Rollback branch transaction fail and stop retry, xid = {} branchId = {}", globalSession.getXid(), branchSession.getBranchId()); return false; default: - LOGGER.info("Rollback branch transaction fail and will retry, xid = {} branchId = {}", globalSession.getXid(), branchSession.getBranchId()); + LOGGER.error("Rollback branch transaction fail and will retry, xid = {} branchId = {}", globalSession.getXid(), branchSession.getBranchId()); if (!retrying) { globalSession.queueToRetryRollback(); } diff --git a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/lock/LockerManagerFactory.java b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/lock/LockerManagerFactory.java index cec4f0da..b4aef847 100644 --- a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/lock/LockerManagerFactory.java +++ b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/lock/LockerManagerFactory.java @@ -20,6 +20,8 @@ import io.seata.config.Configuration; import io.seata.config.ConfigurationFactory; import io.seata.server.store.StoreConfig; import io.seata.server.store.StoreConfig.LockMode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * The type Lock manager factory. @@ -28,6 +30,7 @@ import io.seata.server.store.StoreConfig.LockMode; */ public class LockerManagerFactory { + private static final Logger LOGGER = LoggerFactory.getLogger(LockerManagerFactory.class); private static final Configuration CONFIG = ConfigurationFactory.getInstance(); /** @@ -58,6 +61,7 @@ public class LockerManagerFactory { if (null == lockMode) { lockMode = StoreConfig.getLockMode(); } + LOGGER.info("use lock store mode: {}", lockMode.getName()); //if not exist the lock mode, throw exception if (null != StoreConfig.StoreMode.get(lockMode.name())) { LOCK_MANAGER = EnhancedServiceLoader.load(LockManager.class, lockMode.getName()); diff --git a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/session/BranchSession.java b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/session/BranchSession.java index 4df48e0f..daaf2304 100644 --- a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/session/BranchSession.java +++ b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/session/BranchSession.java @@ -297,6 +297,10 @@ public class BranchSession implements Lockable, Comparable, Sessi return true; } + public boolean isAT() { + return this.getBranchType() == BranchType.AT; + } + public LockStatus getLockStatus() { return lockStatus; } diff --git a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/session/GlobalSession.java b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/session/GlobalSession.java index 6019477b..8f6c39ce 100644 --- a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/session/GlobalSession.java +++ b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/session/GlobalSession.java @@ -226,7 +226,7 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { @Override public void end() throws TransactionException { - if (isSuccessEnd()) { + if (GlobalStatus.isTwoPhaseSuccess(status)) { // Clean locks first clean(); for (SessionLifecycleListener lifecycleListener : lifecycleListeners) { @@ -239,14 +239,6 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { } } - public boolean isSuccessEnd() { - if (status == GlobalStatus.Committed || status == GlobalStatus.Rollbacked - || status == GlobalStatus.TimeoutRollbacked) { - return true; - } - return false; - } - public void clean() throws TransactionException { if (!LockerManagerFactory.getLockManager().releaseGlobalSessionLock(this)) { throw new TransactionException("UnLock globalSession error, xid = " + this.xid); @@ -305,7 +297,7 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { } @Override - public void removeBranch(BranchSession branchSession) throws TransactionException { + public void unlockBranch(BranchSession branchSession) throws TransactionException { // do not unlock if global status in (Committing, CommitRetrying, AsyncCommitting), // because it's already unlocked in 'DefaultCore.commit()' if (status != Committing && status != CommitRetrying && status != AsyncCommitting) { @@ -313,12 +305,22 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { throw new TransactionException("Unlock branch lock failed, xid = " + this.xid + ", branchId = " + branchSession.getBranchId()); } } + } + + @Override + public void removeBranch(BranchSession branchSession) throws TransactionException { for (SessionLifecycleListener lifecycleListener : lifecycleListeners) { lifecycleListener.onRemoveBranch(this, branchSession); } remove(branchSession); } + @Override + public void removeAndUnlockBranch(BranchSession branchSession) throws TransactionException { + unlockBranch(branchSession); + removeBranch(branchSession); + } + /** * Gets branch. * diff --git a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/session/SessionHelper.java b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/session/SessionHelper.java index c8370f1f..12e5f25e 100644 --- a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/session/SessionHelper.java +++ b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/session/SessionHelper.java @@ -134,6 +134,10 @@ public class SessionHelper { MetricsPublisher.postSessionDoneEvent(globalSession, IdConstants.STATUS_VALUE_AFTER_COMMITTED_KEY, true, beginTime, retryBranch); } else { + if (globalSession.isSaga()) { + globalSession.setStatus(GlobalStatus.Committed); + globalSession.end(); + } MetricsPublisher.postSessionDoneEvent(globalSession, false, false); } } @@ -152,8 +156,8 @@ public class SessionHelper { /** * End commit failed. * - * @param globalSession the global session - * @param retryGlobal the retry global + * @param globalSession the global session + * @param retryGlobal the retry global * @param isRetryTimeout is retry timeout * @throws TransactionException the transaction exception */ @@ -201,6 +205,10 @@ public class SessionHelper { MetricsPublisher.postSessionDoneEvent(globalSession, IdConstants.STATUS_VALUE_AFTER_ROLLBACKED_KEY, true, beginTime, retryBranch); } else { + if (globalSession.isSaga()) { + globalSession.setStatus(GlobalStatus.Rollbacked); + globalSession.end(); + } MetricsPublisher.postSessionDoneEvent(globalSession, GlobalStatus.Rollbacked, false, false); } } @@ -294,7 +302,8 @@ public class SessionHelper { */ public static void removeBranch(GlobalSession globalSession, BranchSession branchSession, boolean isAsync) throws TransactionException { - if (Objects.equals(Boolean.TRUE, ENABLE_BRANCH_ASYNC_REMOVE) && isAsync) { + globalSession.unlockBranch(branchSession); + if (isEnableBranchRemoveAsync() && isAsync) { COORDINATOR.doBranchRemoveAsync(globalSession, branchSession); } else { globalSession.removeBranch(branchSession); @@ -312,12 +321,26 @@ public class SessionHelper { if (branchSessions == null || branchSessions.isEmpty()) { return; } - if (Objects.equals(Boolean.TRUE, ENABLE_BRANCH_ASYNC_REMOVE) && isAsync) { - COORDINATOR.doBranchRemoveAllAsync(globalSession); - } else { - for (BranchSession branchSession : branchSessions) { - globalSession.removeBranch(branchSession); + boolean isAsyncRemove = isEnableBranchRemoveAsync() && isAsync; + for (BranchSession branchSession : branchSessions) { + if (isAsyncRemove) { + globalSession.unlockBranch(branchSession); + } else { + globalSession.removeAndUnlockBranch(branchSession); } } + if (isAsyncRemove) { + COORDINATOR.doBranchRemoveAllAsync(globalSession); + } + } + + /** + * if true, enable delete the branch asynchronously + * + * @return the boolean + */ + private static boolean isEnableBranchRemoveAsync() { + return Objects.equals(Boolean.TRUE, DELAY_HANDLE_SESSION) + && Objects.equals(Boolean.TRUE, ENABLE_BRANCH_ASYNC_REMOVE); } } diff --git a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/session/SessionHolder.java b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/session/SessionHolder.java index 849a7896..a05bda43 100644 --- a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/session/SessionHolder.java +++ b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/session/SessionHolder.java @@ -102,6 +102,7 @@ public class SessionHolder { if (null == sessionMode) { sessionMode = StoreConfig.getSessionMode(); } + LOGGER.info("use session store mode: {}", sessionMode.getName()); if (SessionMode.DB.equals(sessionMode)) { ROOT_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, SessionMode.DB.getName()); ASYNC_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, SessionMode.DB.getName(), @@ -399,7 +400,7 @@ public class SessionHolder { func.call(); } } catch (Exception e) { - LOGGER.info("Exception running function with key = {}", key, e); + LOGGER.error("Exception running function with key = {}", key, e); } finally { if (lock) { try { diff --git a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/session/SessionLifecycle.java b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/session/SessionLifecycle.java index 05cd87a3..eb9bfcdd 100644 --- a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/session/SessionLifecycle.java +++ b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/session/SessionLifecycle.java @@ -58,6 +58,14 @@ public interface SessionLifecycle { */ void addBranch(BranchSession branchSession) throws TransactionException; + /** + * Release the lock of branch. + * + * @param branchSession the branch session + * @throws TransactionException the transaction exception + */ + void unlockBranch(BranchSession branchSession) throws TransactionException; + /** * Remove branch. * @@ -66,6 +74,14 @@ public interface SessionLifecycle { */ void removeBranch(BranchSession branchSession) throws TransactionException; + /** + * Remove branch and release the lock of branch. + * + * @param branchSession the branchSession + * @throws TransactionException the TransactionException + */ + void removeAndUnlockBranch(BranchSession branchSession) throws TransactionException; + /** * Is active boolean. * diff --git a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/spring/listener/SeataPropertiesLoader.java b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/spring/listener/SeataPropertiesLoader.java new file mode 100644 index 00000000..3a913c93 --- /dev/null +++ b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/spring/listener/SeataPropertiesLoader.java @@ -0,0 +1,70 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.spring.listener; + +import io.seata.common.util.CollectionUtils; +import io.seata.common.util.StringUtils; +import io.seata.config.ConfigurationFactory; +import io.seata.config.FileConfiguration; +import io.seata.config.file.FileConfig; +import io.seata.server.store.StoreConfig; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.PropertiesPropertySource; + +import java.util.*; + +import static io.seata.common.ConfigurationKeys.*; + +@Order(Ordered.HIGHEST_PRECEDENCE) +public class SeataPropertiesLoader implements ApplicationContextInitializer { + + List prefixList = Arrays.asList(FILE_ROOT_PREFIX_CONFIG, FILE_ROOT_PREFIX_REGISTRY, SERVER_PREFIX, + STORE_PREFIX, METRICS_PREFIX, TRANSPORT_PREFIX); + + @Override + public void initialize(ConfigurableApplicationContext applicationContext) { + ConfigurableEnvironment environment = applicationContext.getEnvironment(); + FileConfiguration configuration = ConfigurationFactory.getOriginFileInstanceRegistry(); + FileConfig fileConfig = configuration.getFileConfig(); + Map configs = fileConfig.getAllConfig(); + if (CollectionUtils.isNotEmpty(configs)) { + Optional originFileInstance = ConfigurationFactory.getOriginFileInstance(); + originFileInstance + .ifPresent(fileConfiguration -> configs.putAll(fileConfiguration.getFileConfig().getAllConfig())); + Properties properties = new Properties(); + configs.forEach((k, v) -> { + if (v instanceof String) { + if (StringUtils.isEmpty((String)v)) { + return; + } + } + // Convert the configuration name to the configuration name under Spring Boot + if (prefixList.stream().anyMatch(k::startsWith)) { + properties.put(SEATA_FILE_PREFIX_ROOT_CONFIG + k, v); + } + }); + environment.getPropertySources().addLast(new PropertiesPropertySource("seataOldConfig", properties)); + } + // Load by priority + System.setProperty("sessionMode", StoreConfig.getSessionMode().getName()); + System.setProperty("lockMode", StoreConfig.getLockMode().getName()); + } + +} diff --git a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/ServerApplicationListener.java b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/spring/listener/ServerApplicationListener.java similarity index 95% rename from ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/ServerApplicationListener.java rename to ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/spring/listener/ServerApplicationListener.java index e26301d7..5d9e0b16 100644 --- a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/ServerApplicationListener.java +++ b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/spring/listener/ServerApplicationListener.java @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.server; +package io.seata.server.spring.listener; import io.seata.common.holder.ObjectHolder; import io.seata.common.util.StringUtils; -import io.seata.server.store.StoreConfig; import io.seata.spring.boot.autoconfigure.SeataCoreEnvironmentPostProcessor; import io.seata.spring.boot.autoconfigure.SeataServerEnvironmentPostProcessor; import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; @@ -56,9 +55,6 @@ public class ServerApplicationListener implements GenericApplicationListener { ObjectHolder.INSTANCE.setObject(OBJECT_KEY_SPRING_CONFIGURABLE_ENVIRONMENT, environment); SeataCoreEnvironmentPostProcessor.init(); SeataServerEnvironmentPostProcessor.init(); - // Load by priority - System.setProperty("sessionMode", StoreConfig.getSessionMode().getName()); - System.setProperty("lockMode", StoreConfig.getLockMode().getName()); String[] args = environmentPreparedEvent.getArgs(); diff --git a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/storage/db/lock/DataBaseDistributedLocker.java b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/storage/db/lock/DataBaseDistributedLocker.java index 1b68600d..f55668f2 100644 --- a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/storage/db/lock/DataBaseDistributedLocker.java +++ b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/storage/db/lock/DataBaseDistributedLocker.java @@ -15,7 +15,6 @@ */ package io.seata.server.storage.db.lock; - import io.seata.common.exception.ShouldNeverHappenException; import io.seata.common.loader.EnhancedServiceLoader; import io.seata.common.loader.LoadLevel; @@ -37,7 +36,9 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.HashSet; import java.util.Objects; +import java.util.Set; import static io.seata.core.constants.ConfigurationKeys.DISTRIBUTED_LOCK_DB_TABLE; @@ -56,6 +57,19 @@ public class DataBaseDistributedLocker implements DistributedLocker { private DataSource distributedLockDataSource; + private static final String LOCK_WAIT_TIMEOUT_MYSQL_MESSAGE = "try restarting transaction"; + + private static final int LOCK_WAIT_TIMEOUT_MYSQL_CODE = 1205; + + private static final Set IGNORE_MYSQL_CODE = new HashSet<>(); + + private static final Set IGNORE_MYSQL_MESSAGE = new HashSet<>(); + + static { + IGNORE_MYSQL_CODE.add(LOCK_WAIT_TIMEOUT_MYSQL_CODE); + IGNORE_MYSQL_MESSAGE.add(LOCK_WAIT_TIMEOUT_MYSQL_MESSAGE); + } + /** * whether the distribute lock demotion * using for 1.5.0 only and will remove in 1.6.0 @@ -109,16 +123,16 @@ public class DataBaseDistributedLocker implements DistributedLocker { originalAutoCommit = connection.getAutoCommit(); connection.setAutoCommit(false); - DistributedLockDO distributedLockDOFromDB = getDistributedLockDO(connection, distributedLockDO.getLockKey()); - if (null == distributedLockDOFromDB) { + DistributedLockDO lockFromDB = getDistributedLockDO(connection, distributedLockDO.getLockKey()); + if (null == lockFromDB) { boolean ret = insertDistribute(connection, distributedLockDO); connection.commit(); return ret; } - if (distributedLockDOFromDB.getExpireTime() >= System.currentTimeMillis()) { + if (lockFromDB.getExpireTime() >= System.currentTimeMillis()) { LOGGER.debug("the distribute lock for key :{} is holding by :{}, acquire lock failure.", - distributedLockDO.getLockKey(), distributedLockDOFromDB.getLockValue()); + distributedLockDO.getLockKey(), lockFromDB.getLockValue()); connection.commit(); return false; } @@ -128,7 +142,11 @@ public class DataBaseDistributedLocker implements DistributedLocker { return ret; } catch (SQLException ex) { - LOGGER.error("execute acquire lock failure, key is: {}", distributedLockDO.getLockKey(), ex); + // ignore "Lock wait timeout exceeded; try restarting transaction" + // TODO: need nowait adaptation + if (!ignoreSQLException(ex)) { + LOGGER.error("execute acquire lock failure, key is: {}", distributedLockDO.getLockKey(), ex); + } try { if (connection != null) { connection.rollback(); @@ -167,8 +185,10 @@ public class DataBaseDistributedLocker implements DistributedLocker { if (distributedLockDOFromDB.getExpireTime() >= System.currentTimeMillis() && !Objects.equals(distributedLockDOFromDB.getLockValue(), distributedLockDO.getLockValue())) { - LOGGER.debug("the distribute lock for key :{} is holding by :{}, skip the release lock.", + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("the distribute lock for key :{} is holding by :{}, skip the release lock.", distributedLockDO.getLockKey(), distributedLockDOFromDB.getLockValue()); + } connection.commit(); return true; } @@ -180,7 +200,9 @@ public class DataBaseDistributedLocker implements DistributedLocker { connection.commit(); return ret; } catch (SQLException ex) { - LOGGER.error("execute release lock failure, key is: {}", distributedLockDO.getLockKey(), ex); + if (!ignoreSQLException(ex)) { + LOGGER.error("execute release lock failure, key is: {}", distributedLockDO.getLockKey(), ex); + } try { if (connection != null) { @@ -248,4 +270,14 @@ public class DataBaseDistributedLocker implements DistributedLocker { this.distributedLockDataSource = EnhancedServiceLoader.load(DataSourceProvider.class, datasourceType).provide(); } + private boolean ignoreSQLException(SQLException exception) { + if (IGNORE_MYSQL_CODE.contains(exception.getErrorCode())) { + return true; + } + if (StringUtils.isNotBlank(exception.getMessage())) { + return IGNORE_MYSQL_MESSAGE.stream().anyMatch(message -> exception.getMessage().contains(message)); + } + return false; + } + } diff --git a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/storage/file/session/FileSessionManager.java b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/storage/file/session/FileSessionManager.java index 504ec495..9eeca2f3 100644 --- a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/storage/file/session/FileSessionManager.java +++ b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/storage/file/session/FileSessionManager.java @@ -220,6 +220,7 @@ public class FileSessionManager extends AbstractSessionManager implements Reload case RollbackFailed: case TimeoutRollbacked: case TimeoutRollbackFailed: + case RollbackRetryTimeout: case Finished: return false; default: diff --git a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/store/DbcpDataSourceProvider.java b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/store/DbcpDataSourceProvider.java index 8880943b..3d6fc5c9 100644 --- a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/store/DbcpDataSourceProvider.java +++ b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/store/DbcpDataSourceProvider.java @@ -20,6 +20,7 @@ import io.seata.core.store.db.AbstractDataSourceProvider; import org.apache.commons.dbcp2.BasicDataSource; import javax.sql.DataSource; +import java.sql.Connection; /** * The dbcp datasource provider @@ -51,6 +52,7 @@ public class DbcpDataSourceProvider extends AbstractDataSourceProvider { ds.setTestWhileIdle(true); ds.setValidationQuery(getValidationQuery(getDBType())); ds.setConnectionProperties("useUnicode=yes;characterEncoding=utf8;socketTimeout=5000;connectTimeout=500"); + ds.setDefaultTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); return ds; } } diff --git a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/store/DruidDataSourceProvider.java b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/store/DruidDataSourceProvider.java index b18a8bbd..ad09895b 100644 --- a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/store/DruidDataSourceProvider.java +++ b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/store/DruidDataSourceProvider.java @@ -20,6 +20,7 @@ import io.seata.common.loader.LoadLevel; import io.seata.core.store.db.AbstractDataSourceProvider; import javax.sql.DataSource; +import java.sql.Connection; /** * The druid datasource provider @@ -52,6 +53,7 @@ public class DruidDataSourceProvider extends AbstractDataSourceProvider { ds.setDefaultAutoCommit(true); // fix issue 5030 ds.setUseOracleImplicitCache(false); + ds.setDefaultTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); return ds; } } diff --git a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/store/HikariDataSourceProvider.java b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/store/HikariDataSourceProvider.java index 8adb354f..60eb0d43 100644 --- a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/store/HikariDataSourceProvider.java +++ b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/store/HikariDataSourceProvider.java @@ -17,6 +17,7 @@ package io.seata.server.store; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; +import com.zaxxer.hikari.util.IsolationLevel; import io.seata.common.loader.LoadLevel; import io.seata.core.store.db.AbstractDataSourceProvider; @@ -55,6 +56,7 @@ public class HikariDataSourceProvider extends AbstractDataSourceProvider { config.setAutoCommit(true); config.setConnectionTimeout(getMaxWait()); config.setInitializationFailTimeout(-1); + config.setTransactionIsolation(IsolationLevel.TRANSACTION_READ_COMMITTED.name()); return new HikariDataSource(config); } } diff --git a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/transaction/at/ATCore.java b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/transaction/at/ATCore.java index f4713527..da318240 100644 --- a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/transaction/at/ATCore.java +++ b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/transaction/at/ATCore.java @@ -80,8 +80,9 @@ public class ATCore extends AbstractCore { branchSession.getBranchId())); } } catch (StoreException e) { - if (e.getCause() instanceof BranchTransactionException) { - throw new BranchTransactionException(((BranchTransactionException)e.getCause()).getCode(), + Throwable cause = e.getCause(); + if (cause instanceof BranchTransactionException) { + throw new BranchTransactionException(((BranchTransactionException)cause).getCode(), String.format("Global lock acquire failed xid = %s branchId = %s", globalSession.getXid(), branchSession.getBranchId())); } diff --git a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/transaction/saga/SagaCore.java b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/transaction/saga/SagaCore.java index fb59c795..f57cd6a5 100644 --- a/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/transaction/saga/SagaCore.java +++ b/ruoyi-visual/ruoyi-seata-server/src/main/java/io/seata/server/transaction/saga/SagaCore.java @@ -195,7 +195,7 @@ public class SagaCore extends AbstractCore { public void doGlobalReport(GlobalSession globalSession, String xid, GlobalStatus globalStatus) throws TransactionException { if (GlobalStatus.Committed.equals(globalStatus)) { SessionHelper.removeAllBranch(globalSession, false); - SessionHelper.endCommitted(globalSession,false); + SessionHelper.endCommitted(globalSession, false); LOGGER.info("Global[{}] committed", globalSession.getXid()); } else if (GlobalStatus.Rollbacked.equals(globalStatus) || GlobalStatus.Finished.equals(globalStatus)) { diff --git a/ruoyi-visual/ruoyi-seata-server/src/main/resources/META-INF/native-image/io.seata/server/reflect-config.json b/ruoyi-visual/ruoyi-seata-server/src/main/resources/META-INF/native-image/io.seata/server/reflect-config.json new file mode 100644 index 00000000..9d762dfb --- /dev/null +++ b/ruoyi-visual/ruoyi-seata-server/src/main/resources/META-INF/native-image/io.seata/server/reflect-config.json @@ -0,0 +1,362 @@ +[ + { + "condition": { + "typeReachable": "io.seata.core.rpc.RegisterCheckAuthHandler" + }, + "name": "io.seata.server.auth.DefaultCheckAuthHandler", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "condition": { + "typeReachable": "io.seata.core.store.db.DataSourceProvider" + }, + "name": "io.seata.server.store.DbcpDataSourceProvider", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "condition": { + "typeReachable": "io.seata.core.store.db.DataSourceProvider" + }, + "name": "io.seata.server.store.DruidDataSourceProvider", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "condition": { + "typeReachable": "io.seata.core.store.db.DataSourceProvider" + }, + "name": "io.seata.server.store.HikariDataSourceProvider", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "condition": { + "typeReachable": "io.seata.core.store.DistributedLocker" + }, + "name": "io.seata.server.storage.redis.lock.RedisDistributedLocker", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "condition": { + "typeReachable": "io.seata.core.store.DistributedLocker" + }, + "name": "io.seata.server.storage.db.lock.DataBaseDistributedLocker", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "condition": { + "typeReachable": "io.seata.server.coordinator.AbstractCore" + }, + "name": "io.seata.server.transaction.at.ATCore", + "methods": [ + { + "name": "", + "parameterTypes": [ + "io.seata.core.rpc.RemotingServer" + ] + } + ] + }, + { + "condition": { + "typeReachable": "io.seata.server.coordinator.AbstractCore" + }, + "name": "io.seata.server.transaction.tcc.TccCore", + "methods": [ + { + "name": "", + "parameterTypes": [ + "io.seata.core.rpc.RemotingServer" + ] + } + ] + }, + { + "condition": { + "typeReachable": "io.seata.server.coordinator.AbstractCore" + }, + "name": "io.seata.server.transaction.saga.SagaCore", + "methods": [ + { + "name": "", + "parameterTypes": [ + "io.seata.core.rpc.RemotingServer" + ] + } + ] + }, + { + "condition": { + "typeReachable": "io.seata.server.coordinator.AbstractCore" + }, + "name": "io.seata.server.transaction.xa.XACore", + "methods": [ + { + "name": "", + "parameterTypes": [ + "io.seata.core.rpc.RemotingServer" + ] + } + ] + }, + { + "condition": { + "typeReachable": "io.seata.server.lock.LockManager" + }, + "name": "io.seata.server.storage.db.lock.DataBaseLockManager", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "condition": { + "typeReachable": "io.seata.server.lock.LockManager" + }, + "name": "io.seata.server.storage.file.lock.FileLockManager", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "condition": { + "typeReachable": "io.seata.server.lock.LockManager" + }, + "name": "io.seata.server.storage.redis.lock.RedisLockManager", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] + }, + { + "condition": { + "typeReachable": "io.seata.server.session.SessionManager" + }, + "name": "io.seata.server.storage.file.session.FileSessionManager", + "methods": [ + { + "name": "", + "parameterTypes": [ + "java.lang.String", + "java.lang.String" + ] + } + ] + }, + { + "condition": { + "typeReachable": "io.seata.server.session.SessionManager" + }, + "name": "io.seata.server.storage.db.session.DataBaseSessionManager", + "methods": [ + { + "name": "", + "parameterTypes": [] + }, + { + "name": "", + "parameterTypes": [ + "java.lang.String" + ] + } + ] + }, + { + "condition": { + "typeReachable": "io.seata.server.session.SessionManager" + }, + "name": "io.seata.server.storage.redis.session.RedisSessionManager", + "methods": [ + { + "name": "", + "parameterTypes": [] + }, + { + "name": "", + "parameterTypes": [ + "java.lang.String" + ] + } + ] + }, + { + "condition": { + "typeReachable": "com.google.inject.internal.TypeConverterBindingProcessor" + }, + "name": "java.lang.Integer", + "methods": [ + { + "name": "parseInteger", + "parameterTypes": [ + "java.lang.String" + ] + } + ] + }, + { + "condition": { + "typeReachable": "com.google.inject.internal.TypeConverterBindingProcessor" + }, + "name": "java.lang.Long", + "methods": [ + { + "name": "parseLong", + "parameterTypes": [ + "java.lang.String" + ] + } + ] + }, + { + "condition": { + "typeReachable": "com.google.inject.internal.TypeConverterBindingProcessor" + }, + "name": "java.lang.Boolean", + "methods": [ + { + "name": "parseBoolean", + "parameterTypes": [ + "java.lang.String" + ] + } + ] + }, + { + "condition": { + "typeReachable": "com.google.inject.internal.TypeConverterBindingProcessor" + }, + "name": "java.lang.Byte", + "methods": [ + { + "name": "parseByte", + "parameterTypes": [ + "java.lang.String" + ] + } + ] + }, + { + "condition": { + "typeReachable": "com.google.inject.internal.TypeConverterBindingProcessor" + }, + "name": "java.lang.Short", + "methods": [ + { + "name": "parseShort", + "parameterTypes": [ + "java.lang.String" + ] + } + ] + }, + { + "condition": { + "typeReachable": "com.google.inject.internal.TypeConverterBindingProcessor" + }, + "name": "java.lang.Float", + "methods": [ + { + "name": "parseFloat", + "parameterTypes": [ + "java.lang.String" + ] + } + ] + }, + { + "condition": { + "typeReachable": "com.google.inject.internal.TypeConverterBindingProcessor" + }, + "name": "java.lang.Double", + "methods": [ + { + "name": "parseDouble", + "parameterTypes": [ + "java.lang.String" + ] + } + ] + }, + { + "condition": { + "typeReachable": "io.netty.channel.socket.nio.SelectorProviderUtil" + }, + "name": "java.nio.channels.spi.SelectorProvider", + "methods": [ + { + "name": "openServerSocketChannel", + "parameterTypes": [ + "java.net.ProtocolFamily" + ] + } + ] + }, + { + "condition": { + "typeReachable": "io.netty.channel.DefaultChannelConfig" + }, + "name": "io.netty.buffer.ByteBufAllocator" + }, + { + "condition": { + "typeReachable": "io.netty.channel.DefaultChannelConfig" + }, + "name": "io.netty.buffer.ByteBufUtil" + }, + { + "condition": { + "typeReachable": "io.netty.util.ResourceLeakDetector" + }, + "name": "io.netty.buffer.AbstractByteBufAllocator", + "allDeclaredMethods": true + }, + { + "condition": { + "typeReachable": "io.netty.util.ResourceLeakDetector" + }, + "name": "io.netty.buffer.AdvancedLeakAwareByteBuf", + "allDeclaredMethods": true + }, + { + "condition": { + "typeReachable": "io.netty.util.ResourceLeakDetector" + }, + "name": "io.netty.util.ReferenceCountUtil", + "allDeclaredMethods": true + } +] \ No newline at end of file diff --git a/ruoyi-visual/ruoyi-seata-server/src/main/resources/META-INF/native-image/io.seata/server/resource-config.json b/ruoyi-visual/ruoyi-seata-server/src/main/resources/META-INF/native-image/io.seata/server/resource-config.json new file mode 100644 index 00000000..73b72ff3 --- /dev/null +++ b/ruoyi-visual/ruoyi-seata-server/src/main/resources/META-INF/native-image/io.seata/server/resource-config.json @@ -0,0 +1,21 @@ +{ + "resources": { + "includes": [ + { + "pattern": "\\Qlogback\/\\E.*" + }, + { + "pattern": "\\Qlua\/redislocker\/redislock.lua\\E" + }, + { + "pattern": "\\Qapplication.yml\\E" + }, + { + "pattern": "\\Qbanner.txt\\E" + }, + { + "pattern": "\\Qlogback-spring.xml\\E" + } + ] + } +} \ No newline at end of file diff --git a/ruoyi-visual/ruoyi-seata-server/src/main/resources/META-INF/spring.factories b/ruoyi-visual/ruoyi-seata-server/src/main/resources/META-INF/spring.factories index 915bf1b7..3384008b 100644 --- a/ruoyi-visual/ruoyi-seata-server/src/main/resources/META-INF/spring.factories +++ b/ruoyi-visual/ruoyi-seata-server/src/main/resources/META-INF/spring.factories @@ -1,2 +1,4 @@ org.springframework.context.ApplicationListener=\ -io.seata.server.ServerApplicationListener \ No newline at end of file +io.seata.server.spring.listener.ServerApplicationListener +org.springframework.context.ApplicationContextInitializer=\ +io.seata.server.spring.listener.SeataPropertiesLoader \ No newline at end of file diff --git a/ruoyi-visual/ruoyi-seata-server/src/main/resources/application.yml b/ruoyi-visual/ruoyi-seata-server/src/main/resources/application.yml index 8df8aebf..5c12ec5a 100644 --- a/ruoyi-visual/ruoyi-seata-server/src/main/resources/application.yml +++ b/ruoyi-visual/ruoyi-seata-server/src/main/resources/application.yml @@ -56,4 +56,4 @@ seata: secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017 tokenValidityInMilliseconds: 1800000 ignore: - urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/api/v1/auth/login + urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.jpeg,/**/*.ico,/api/v1/auth/login