diff --git a/pigx-visual/pigx-seata-server/pom.xml b/pigx-visual/pigx-seata-server/pom.xml index 26d7f66dc..9ca5fbb5d 100644 --- a/pigx-visual/pigx-seata-server/pom.xml +++ b/pigx-visual/pigx-seata-server/pom.xml @@ -28,111 +28,171 @@ pigx-seata-server - 1.4.2 + 1.6.1 6.5 0.2.0-RC2 1.72 27.0.1-jre + + + + + io.seata + seata-dependencies + ${seata.version} + pom + import + + + + + - - io.seata - seata-core - ${seata.version} - - - io.seata - seata-config-all - ${seata.version} - - - io.seata - seata-discovery-all - ${seata.version} - - - io.seata - seata-serializer-all - ${seata.version} - - - io.seata - seata-compressor-all - ${seata.version} - + + io.seata + seata-spring-autoconfigure-server + ${seata.version} + - - io.seata - seata-metrics-all - ${seata.version} - + + io.seata + seata-core + ${seata.version} + + + io.seata + seata-config-all + ${seata.version} + + + log4j + log4j + + + + + io.seata + seata-discovery-all + ${seata.version} + + + io.seata + seata-serializer-all + ${seata.version} + + + io.seata + seata-compressor-all + ${seata.version} + - - - com.alibaba - druid - - - org.apache.commons - commons-dbcp2 - - - com.zaxxer - HikariCP - - - com.h2database - h2 - - - mysql - mysql-connector-java - - - org.postgresql - postgresql - - - com.beust - jcommander - ${jcommander.version} - + + io.seata + seata-metrics-all + ${seata.version} + - - - com.google.guava - guava - ${guava.version} - + + io.seata + seata-console + ${seata.version} + - - - ch.qos.logback - logback-classic - + + + com.alibaba + druid + + + org.apache.commons + commons-dbcp2 + + + com.zaxxer + HikariCP + + + com.h2database + h2 + + + + mysql + mysql-connector-java + + + org.postgresql + postgresql + + - - - redis.clients - jedis - + + com.beust + jcommander + + + + + com.google.guava + guava + + + + + redis.clients + jedis + + + + com.github.microwww + redis-server + test + + + + com.alibaba + fastjson + + + + + ch.qos.logback + logback-classic + + + ch.qos.logback + logback-core + + + + net.logstash.logback + logstash-logback-encoder + + + com.github.danielwegener + logback-kafka-appender + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-undertow + - - com.alibaba - fastjson - - - net.logstash.logback - logstash-logback-encoder - ${logstash-logback-encoder.version} - - - com.github.danielwegener - logback-kafka-appender - ${kafka-appender.version} - diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/AbstractTCInboundHandler.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/AbstractTCInboundHandler.java index 4ba44b6af..c824abf66 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/AbstractTCInboundHandler.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/AbstractTCInboundHandler.java @@ -20,25 +20,7 @@ import io.seata.core.exception.AbstractExceptionHandler; import io.seata.core.exception.TransactionException; import io.seata.core.exception.TransactionExceptionCode; import io.seata.core.model.GlobalStatus; -import io.seata.core.protocol.transaction.AbstractGlobalEndRequest; -import io.seata.core.protocol.transaction.AbstractGlobalEndResponse; -import io.seata.core.protocol.transaction.BranchRegisterRequest; -import io.seata.core.protocol.transaction.BranchRegisterResponse; -import io.seata.core.protocol.transaction.BranchReportRequest; -import io.seata.core.protocol.transaction.BranchReportResponse; -import io.seata.core.protocol.transaction.GlobalBeginRequest; -import io.seata.core.protocol.transaction.GlobalBeginResponse; -import io.seata.core.protocol.transaction.GlobalCommitRequest; -import io.seata.core.protocol.transaction.GlobalCommitResponse; -import io.seata.core.protocol.transaction.GlobalLockQueryRequest; -import io.seata.core.protocol.transaction.GlobalLockQueryResponse; -import io.seata.core.protocol.transaction.GlobalReportRequest; -import io.seata.core.protocol.transaction.GlobalReportResponse; -import io.seata.core.protocol.transaction.GlobalRollbackRequest; -import io.seata.core.protocol.transaction.GlobalRollbackResponse; -import io.seata.core.protocol.transaction.GlobalStatusRequest; -import io.seata.core.protocol.transaction.GlobalStatusResponse; -import io.seata.core.protocol.transaction.TCInboundHandler; +import io.seata.core.protocol.transaction.*; import io.seata.core.rpc.RpcContext; import io.seata.server.session.GlobalSession; import io.seata.server.session.SessionHolder; diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/ParameterParser.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/ParameterParser.java index e3a533e70..4c9d21f22 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/ParameterParser.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/ParameterParser.java @@ -19,12 +19,11 @@ import com.beust.jcommander.JCommander; import com.beust.jcommander.Parameter; import com.beust.jcommander.ParameterException; import io.seata.common.util.StringUtils; +import io.seata.config.Configuration; import io.seata.config.ConfigurationFactory; -import io.seata.core.constants.ConfigurationKeys; import io.seata.server.env.ContainerHelper; +import io.seata.server.store.StoreConfig; -import static io.seata.common.DefaultValues.SERVER_DEFAULT_PORT; -import static io.seata.common.DefaultValues.SERVER_DEFAULT_STORE_MODE; import static io.seata.config.ConfigurationFactory.ENV_PROPERTY_KEY; /** @@ -36,6 +35,8 @@ public class ParameterParser { private static final String PROGRAM_NAME = "sh seata-server.sh(for linux and mac) or cmd seata-server.bat(for windows)"; + private static final Configuration CONFIG = ConfigurationFactory.getInstance(); + @Parameter(names = "--help", help = true) private boolean help; @@ -43,9 +44,9 @@ public class ParameterParser { private String host; @Parameter(names = { "--port", "-p" }, description = "The port to listen.", order = 2) - private int port = SERVER_DEFAULT_PORT; + private int port; - @Parameter(names = { "--storeMode", "-m" }, description = "log store mode : file, db", order = 3) + @Parameter(names = { "--storeMode", "-m" }, description = "log store mode : file, db, redis", order = 3) private String storeMode; @Parameter(names = { "--serverNode", "-n" }, @@ -57,39 +58,33 @@ public class ParameterParser { order = 5) private String seataEnv; + @Parameter(names = { "--sessionStoreMode", "-ssm" }, description = "session log store mode : file, db, redis", + order = 6) + private String sessionStoreMode; + + @Parameter(names = { "--lockStoreMode", "-lsm" }, description = "lock log store mode : file, db, redis", order = 7) + private String lockStoreMode; + /** * Instantiates a new Parameter parser. * @param args the args */ - public ParameterParser(String[] args) { + public ParameterParser(String... args) { this.init(args); } + /** + * startup args > docker env + * @param args + */ private void init(String[] args) { try { - if (ContainerHelper.isRunningInContainer()) { - this.seataEnv = ContainerHelper.getEnv(); - this.host = ContainerHelper.getHost(); - this.port = ContainerHelper.getPort(); - this.serverNode = ContainerHelper.getServerNode(); - this.storeMode = ContainerHelper.getStoreMode(); - } - else { - JCommander jCommander = JCommander.newBuilder().addObject(this).build(); - jCommander.parse(args); - if (help) { - jCommander.setProgramName(PROGRAM_NAME); - jCommander.usage(); - System.exit(0); - } - } + getCommandParameters(args); + getEnvParameters(); if (StringUtils.isNotBlank(seataEnv)) { System.setProperty(ENV_PROPERTY_KEY, seataEnv); } - if (StringUtils.isBlank(storeMode)) { - storeMode = ConfigurationFactory.getInstance().getConfig(ConfigurationKeys.STORE_MODE, - SERVER_DEFAULT_STORE_MODE); - } + StoreConfig.setStartupParameter(storeMode, sessionStoreMode, lockStoreMode); } catch (ParameterException e) { printError(e); @@ -97,6 +92,31 @@ public class ParameterParser { } + private void getCommandParameters(String[] args) { + JCommander jCommander = JCommander.newBuilder().addObject(this).build(); + jCommander.parse(args); + if (help) { + jCommander.setProgramName(PROGRAM_NAME); + jCommander.usage(); + System.exit(0); + } + } + + private void getEnvParameters() { + if (StringUtils.isBlank(seataEnv)) { + seataEnv = ContainerHelper.getEnv(); + } + if (StringUtils.isBlank(host)) { + host = ContainerHelper.getHost(); + } + if (port == 0) { + port = ContainerHelper.getPort(); + } + if (serverNode == null) { + serverNode = ContainerHelper.getServerNode(); + } + } + private void printError(ParameterException e) { System.err.println("Option error " + e.getMessage()); e.getJCommander().setProgramName(PROGRAM_NAME); @@ -128,6 +148,22 @@ public class ParameterParser { return storeMode; } + /** + * Gets lock store mode. + * @return the store mode + */ + public String getLockStoreMode() { + return lockStoreMode; + } + + /** + * Gets session store mode. + * @return the store mode + */ + public String getSessionStoreMode() { + return sessionStoreMode; + } + /** * Is help boolean. * @return the boolean diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/PigxSeataServer.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/Server.java similarity index 63% rename from pigx-visual/pigx-seata-server/src/main/java/io/seata/server/PigxSeataServer.java rename to pigx-visual/pigx-seata-server/src/main/java/io/seata/server/Server.java index 665175b6f..e366d5fda 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/PigxSeataServer.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/Server.java @@ -18,46 +18,34 @@ package io.seata.server; import io.seata.common.XID; import io.seata.common.thread.NamedThreadFactory; import io.seata.common.util.NetUtil; -import io.seata.core.constants.ConfigurationKeys; -import io.seata.core.rpc.ShutdownHook; +import io.seata.common.util.StringUtils; +import io.seata.config.ConfigurationFactory; import io.seata.core.rpc.netty.NettyRemotingServer; import io.seata.core.rpc.netty.NettyServerConfig; import io.seata.server.coordinator.DefaultCoordinator; -import io.seata.server.env.ContainerHelper; -import io.seata.server.env.PortHelper; +import io.seata.server.lock.LockerManagerFactory; import io.seata.server.metrics.MetricsManager; import io.seata.server.session.SessionHolder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import java.io.IOException; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import static io.seata.spring.boot.autoconfigure.StarterConstants.REGEX_SPLIT_CHAR; +import static io.seata.spring.boot.autoconfigure.StarterConstants.REGISTRY_PREFERED_NETWORKS; + /** * The type Server. * * @author slievrly */ -public class PigxSeataServer { +public class Server { /** * The entry point of application. * @param args the input arguments - * @throws IOException the io exception */ - public static void main(String[] args) throws IOException { - // get port first, use to logback.xml - int port = PortHelper.getPort(args); - System.setProperty(ConfigurationKeys.SERVER_PORT, Integer.toString(port)); - - // create logger - final Logger logger = LoggerFactory.getLogger(PigxSeataServer.class); - if (ContainerHelper.isRunningInContainer()) { - logger.info("The server is running in container."); - } - + public static void start(String[] args) { // initialize the parameter parser // Note that the parameter parser should always be the first line to execute. // Because, here we need to parse the parameters needed for startup. @@ -66,46 +54,41 @@ public class PigxSeataServer { // initialize the metrics MetricsManager.get().init(); - System.setProperty(ConfigurationKeys.STORE_MODE, parameterParser.getStoreMode()); - ThreadPoolExecutor workingThreads = new ThreadPoolExecutor(NettyServerConfig.getMinServerPoolSize(), NettyServerConfig.getMaxServerPoolSize(), NettyServerConfig.getKeepAliveTime(), TimeUnit.SECONDS, new LinkedBlockingQueue<>(NettyServerConfig.getMaxTaskQueueSize()), new NamedThreadFactory("ServerHandlerThread", NettyServerConfig.getMaxServerPoolSize()), new ThreadPoolExecutor.CallerRunsPolicy()); - NettyRemotingServer nettyRemotingServer = new NettyRemotingServer(workingThreads); - // server port - nettyRemotingServer.setListenPort(parameterParser.getPort()); - UUIDGenerator.init(parameterParser.getServerNode()); - // log store mode : file, db, redis - SessionHolder.init(parameterParser.getStoreMode()); - - DefaultCoordinator coordinator = new DefaultCoordinator(nettyRemotingServer); - coordinator.init(); - nettyRemotingServer.setHandler(coordinator); - // register ShutdownHook - ShutdownHook.getInstance().addDisposable(coordinator); - ShutdownHook.getInstance().addDisposable(nettyRemotingServer); - // 127.0.0.1 and 0.0.0.0 are not valid here. if (NetUtil.isValidIp(parameterParser.getHost(), false)) { XID.setIpAddress(parameterParser.getHost()); } else { - XID.setIpAddress(NetUtil.getLocalIp()); + String preferredNetworks = ConfigurationFactory.getInstance().getConfig(REGISTRY_PREFERED_NETWORKS); + if (StringUtils.isNotBlank(preferredNetworks)) { + XID.setIpAddress(NetUtil.getLocalIp(preferredNetworks.split(REGEX_SPLIT_CHAR))); + } + else { + XID.setIpAddress(NetUtil.getLocalIp()); + } } + + NettyRemotingServer nettyRemotingServer = new NettyRemotingServer(workingThreads); XID.setPort(nettyRemotingServer.getListenPort()); + UUIDGenerator.init(parameterParser.getServerNode()); + // log store mode : file, db, redis + SessionHolder.init(); + LockerManagerFactory.init(); + DefaultCoordinator coordinator = DefaultCoordinator.getInstance(nettyRemotingServer); + coordinator.init(); + nettyRemotingServer.setHandler(coordinator); - try { - nettyRemotingServer.init(); - } - catch (Throwable e) { - logger.error("nettyServer init error:{}", e.getMessage(), e); - System.exit(-1); - } + // let ServerRunner do destroy instead ShutdownHook, see + // https://github.com/seata/seata/issues/4028 + ServerRunner.addDisposable(coordinator); - System.exit(0); + nettyRemotingServer.init(); } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/ServerApplication.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/ServerApplication.java new file mode 100644 index 000000000..536bc3407 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/ServerApplication.java @@ -0,0 +1,34 @@ +/* + * 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; + +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 ServerApplication { + + public static void main(String[] args) throws IOException { + // run the spring-boot application + SpringApplication.run(ServerApplication.class, args); + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/ServerApplicationListener.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/ServerApplicationListener.java new file mode 100644 index 000000000..3c3ddb817 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/ServerApplicationListener.java @@ -0,0 +1,129 @@ +/* + * 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; + +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; +import org.springframework.boot.context.logging.LoggingApplicationListener; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.GenericApplicationListener; +import org.springframework.core.ResolvableType; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.PropertiesPropertySource; + +import java.util.Properties; + +import static io.seata.common.Constants.OBJECT_KEY_SPRING_CONFIGURABLE_ENVIRONMENT; +import static io.seata.common.DefaultValues.SERVICE_OFFSET_SPRING_BOOT; +import static io.seata.core.constants.ConfigurationKeys.*; + +/** + * @author slievrly + * @author funkye + */ +public class ServerApplicationListener implements GenericApplicationListener { + + @Override + public boolean supportsEventType(ResolvableType eventType) { + return eventType.getRawClass() != null + && ApplicationEnvironmentPreparedEvent.class.isAssignableFrom(eventType.getRawClass()); + } + + @Override + public void onApplicationEvent(ApplicationEvent event) { + if (!(event instanceof ApplicationEnvironmentPreparedEvent)) { + return; + } + ApplicationEnvironmentPreparedEvent environmentPreparedEvent = (ApplicationEnvironmentPreparedEvent) event; + ConfigurableEnvironment environment = environmentPreparedEvent.getEnvironment(); + 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(); + + // port: -p > -D > env > yml > default + + // -p 8091 + if (args != null && args.length >= 2) { + for (int i = 0; i < args.length; ++i) { + if ("-p".equalsIgnoreCase(args[i]) && i < args.length - 1) { + setTargetPort(environment, args[i + 1], true); + return; + } + } + } + + // -Dserver.servicePort=8091 + String dPort = environment.getProperty(SERVER_SERVICE_PORT_CAMEL, String.class); + if (StringUtils.isNotBlank(dPort)) { + setTargetPort(environment, dPort, true); + return; + } + + // docker -e SEATA_PORT=8091 + String envPort = environment.getProperty(ENV_SEATA_PORT_KEY, String.class); + if (StringUtils.isNotBlank(envPort)) { + setTargetPort(environment, envPort, true); + return; + } + + // yml properties server.service-port=8091 + String configPort = environment.getProperty(SERVER_SERVICE_PORT_CONFIG, String.class); + if (StringUtils.isNotBlank(configPort)) { + setTargetPort(environment, configPort, false); + return; + } + + // server.port=7091 + String serverPort = environment.getProperty("server.port", String.class); + if (StringUtils.isBlank(serverPort)) { + serverPort = "8080"; + } + String servicePort = String.valueOf(Integer.parseInt(serverPort) + SERVICE_OFFSET_SPRING_BOOT); + setTargetPort(environment, servicePort, true); + } + + private void setTargetPort(ConfigurableEnvironment environment, String port, boolean needAddPropertySource) { + // get rpc port first, use to logback-spring.xml, @see the class named + // `SystemPropertyLoggerContextListener` + System.setProperty(SERVER_SERVICE_PORT_CAMEL, port); + + if (needAddPropertySource) { + // add property source to the first position + Properties pro = new Properties(); + pro.setProperty(SERVER_SERVICE_PORT_CONFIG, port); + environment.getPropertySources().addFirst(new PropertiesPropertySource("serverProperties", pro)); + } + } + + /** + * higher than LoggingApplicationListener + * @return the order + */ + @Override + public int getOrder() { + return LoggingApplicationListener.DEFAULT_ORDER - 1; + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/ServerRunner.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/ServerRunner.java new file mode 100644 index 000000000..4129b307a --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/ServerRunner.java @@ -0,0 +1,81 @@ +/* + * 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; + +import io.seata.core.rpc.Disposable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * @author spilledyear@outlook.com + */ +@Component +public class ServerRunner implements CommandLineRunner, DisposableBean { + + private static final Logger LOGGER = LoggerFactory.getLogger(ServerRunner.class); + + private boolean started = Boolean.FALSE; + + private static final List DISPOSABLE_LIST = new CopyOnWriteArrayList<>(); + + public static void addDisposable(Disposable disposable) { + DISPOSABLE_LIST.add(disposable); + } + + @Override + public void run(String... args) { + try { + long start = System.currentTimeMillis(); + Server.start(args); + started = true; + + long cost = System.currentTimeMillis() - start; + LOGGER.info("seata server started in {} millSeconds", cost); + } + catch (Throwable e) { + started = Boolean.FALSE; + LOGGER.error("seata server start error: {} ", e.getMessage(), e); + System.exit(-1); + } + } + + public boolean started() { + return started; + } + + @Override + public void destroy() throws Exception { + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("destoryAll starting"); + } + + for (Disposable disposable : DISPOSABLE_LIST) { + disposable.destroy(); + } + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("destoryAll finish"); + } + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/auth/AbstractCheckAuthHandler.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/auth/AbstractCheckAuthHandler.java index bf0d531b3..ca297602f 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/auth/AbstractCheckAuthHandler.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/auth/AbstractCheckAuthHandler.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package io.seata.server.auth; import io.seata.config.ConfigurationFactory; diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/auth/DefaultCheckAuthHandler.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/auth/DefaultCheckAuthHandler.java index e98da6338..8fe75e898 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/auth/DefaultCheckAuthHandler.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/auth/DefaultCheckAuthHandler.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package io.seata.server.auth; import io.seata.common.loader.LoadLevel; diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/controller/BranchSessionController.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/controller/BranchSessionController.java new file mode 100644 index 000000000..849343131 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/controller/BranchSessionController.java @@ -0,0 +1,36 @@ +/* + * 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.console.controller; + +import io.seata.server.console.service.BranchSessionService; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +/** + * Branch Session Controller + * + * @author zhongxiang.wang + */ +@RestController +@RequestMapping("console/branchSession") +public class BranchSessionController { + + @Resource(type = BranchSessionService.class) + private BranchSessionService branchSessionService; + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/controller/GlobalLockController.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/controller/GlobalLockController.java new file mode 100644 index 000000000..1c5034eb7 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/controller/GlobalLockController.java @@ -0,0 +1,51 @@ +/* + * 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.console.controller; + +import io.seata.console.result.PageResult; +import io.seata.server.console.param.GlobalLockParam; +import io.seata.server.console.service.GlobalLockService; +import io.seata.server.console.vo.GlobalLockVO; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +/** + * Global Lock Controller + * + * @author zhongxiang.wang + */ +@RestController +@RequestMapping("/api/v1/console/globalLock") +public class GlobalLockController { + + @Resource(type = GlobalLockService.class) + private GlobalLockService globalLockService; + + /** + * Query locks by param + * @param param the param + * @return the list of GlobalLockVO + */ + @GetMapping("query") + public PageResult query(@ModelAttribute GlobalLockParam param) { + return globalLockService.query(param); + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/controller/GlobalSessionController.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/controller/GlobalSessionController.java new file mode 100644 index 000000000..0d1bdc95f --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/controller/GlobalSessionController.java @@ -0,0 +1,51 @@ +/* + * 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.console.controller; + +import io.seata.console.result.PageResult; +import io.seata.server.console.param.GlobalSessionParam; +import io.seata.server.console.service.GlobalSessionService; +import io.seata.server.console.vo.GlobalSessionVO; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +/** + * Global Session Controller + * + * @author zhongxiang.wang + */ +@RestController +@RequestMapping("/api/v1/console/globalSession") +public class GlobalSessionController { + + @Resource(type = GlobalSessionService.class) + private GlobalSessionService globalSessionService; + + /** + * Query all globalSession + * @param param param for query globalSession + * @return the list of GlobalSessionVO + */ + @GetMapping("query") + public PageResult query(@ModelAttribute GlobalSessionParam param) { + return globalSessionService.query(param); + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/db/BranchSessionDBServiceImpl.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/db/BranchSessionDBServiceImpl.java new file mode 100644 index 000000000..a92864bfb --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/db/BranchSessionDBServiceImpl.java @@ -0,0 +1,104 @@ +/* + * 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.console.impl.db; + +import io.seata.common.ConfigurationKeys; +import io.seata.common.exception.StoreException; +import io.seata.common.loader.EnhancedServiceLoader; +import io.seata.common.util.IOUtil; +import io.seata.common.util.StringUtils; +import io.seata.config.Configuration; +import io.seata.config.ConfigurationFactory; +import io.seata.console.result.PageResult; +import io.seata.core.store.db.DataSourceProvider; +import io.seata.core.store.db.sql.log.LogStoreSqlsFactory; +import io.seata.server.console.service.BranchSessionService; +import io.seata.server.console.vo.BranchSessionVO; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import static io.seata.common.DefaultValues.DEFAULT_STORE_DB_BRANCH_TABLE; + +/** + * Branch Session DataBase ServiceImpl + * + * @author zhongxiang.wang + * @author lvekee 734843455@qq.com + */ +@Component +@org.springframework.context.annotation.Configuration +@ConditionalOnExpression("#{'db'.equals('${sessionMode}')}") +public class BranchSessionDBServiceImpl implements BranchSessionService { + + private String branchTable; + + private String dbType; + + private DataSource dataSource; + + public BranchSessionDBServiceImpl() { + Configuration configuration = ConfigurationFactory.getInstance(); + branchTable = configuration.getConfig(ConfigurationKeys.STORE_DB_BRANCH_TABLE, DEFAULT_STORE_DB_BRANCH_TABLE); + dbType = configuration.getConfig(ConfigurationKeys.STORE_DB_TYPE); + if (StringUtils.isBlank(dbType)) { + throw new IllegalArgumentException(ConfigurationKeys.STORE_DB_TYPE + " should not be blank"); + } + String dbDataSource = configuration.getConfig(ConfigurationKeys.STORE_DB_DATASOURCE_TYPE); + if (StringUtils.isBlank(dbDataSource)) { + throw new IllegalArgumentException(ConfigurationKeys.STORE_DB_DATASOURCE_TYPE + " should not be blank"); + } + dataSource = EnhancedServiceLoader.load(DataSourceProvider.class, dbDataSource).provide(); + } + + @Override + public PageResult queryByXid(String xid) { + if (StringUtils.isBlank(xid)) { + throw new IllegalArgumentException("xid should not be blank"); + } + + String whereCondition = " where xid = ? "; + String branchSessionSQL = LogStoreSqlsFactory.getLogStoreSqls(dbType).getAllBranchSessionSQL(branchTable, + whereCondition); + + List list = new ArrayList<>(); + ResultSet rs = null; + + try (Connection conn = dataSource.getConnection(); + PreparedStatement ps = conn.prepareStatement(branchSessionSQL)) { + ps.setObject(1, xid); + rs = ps.executeQuery(); + while (rs.next()) { + list.add(BranchSessionVO.convert(rs)); + } + } + catch (SQLException e) { + throw new StoreException(e); + } + finally { + IOUtil.close(rs); + } + return PageResult.success(list, list.size(), 0, 0, 0); + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/db/GlobalLockDBServiceImpl.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/db/GlobalLockDBServiceImpl.java new file mode 100644 index 000000000..fdba6ef35 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/db/GlobalLockDBServiceImpl.java @@ -0,0 +1,146 @@ +/* + * 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.console.impl.db; + +import io.seata.common.ConfigurationKeys; +import io.seata.common.exception.StoreException; +import io.seata.common.loader.EnhancedServiceLoader; +import io.seata.common.util.IOUtil; +import io.seata.common.util.PageUtil; +import io.seata.common.util.StringUtils; +import io.seata.config.Configuration; +import io.seata.config.ConfigurationFactory; +import io.seata.console.result.PageResult; +import io.seata.core.store.db.DataSourceProvider; +import io.seata.core.store.db.sql.lock.LockStoreSqlFactory; +import io.seata.server.console.param.GlobalLockParam; +import io.seata.server.console.service.GlobalLockService; +import io.seata.server.console.vo.GlobalLockVO; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import static io.seata.common.DefaultValues.DEFAULT_LOCK_DB_TABLE; + +/** + * Global Lock DB ServiceImpl + * + * @author zhongxiang.wang + * @author lvekee 734843455@qq.com + */ +@Component +@org.springframework.context.annotation.Configuration +@ConditionalOnExpression("#{'db'.equals('${lockMode}')}") +public class GlobalLockDBServiceImpl implements GlobalLockService { + + private String lockTable; + + private String dbType; + + private DataSource dataSource; + + public GlobalLockDBServiceImpl() { + Configuration configuration = ConfigurationFactory.getInstance(); + lockTable = configuration.getConfig(ConfigurationKeys.LOCK_DB_TABLE, DEFAULT_LOCK_DB_TABLE); + dbType = configuration.getConfig(ConfigurationKeys.STORE_DB_TYPE); + if (StringUtils.isBlank(dbType)) { + throw new IllegalArgumentException(ConfigurationKeys.STORE_DB_TYPE + " should not be blank"); + } + String dbDataSource = configuration.getConfig(ConfigurationKeys.STORE_DB_DATASOURCE_TYPE); + if (StringUtils.isBlank(dbDataSource)) { + throw new IllegalArgumentException(ConfigurationKeys.STORE_DB_DATASOURCE_TYPE + " should not be blank"); + } + dataSource = EnhancedServiceLoader.load(DataSourceProvider.class, dbDataSource).provide(); + } + + @Override + public PageResult query(GlobalLockParam param) { + PageUtil.checkParam(param.getPageNum(), param.getPageSize()); + + List sqlParamList = new ArrayList<>(); + String whereCondition = this.getWhereConditionByParam(param, sqlParamList); + + String sourceSql = LockStoreSqlFactory.getLogStoreSql(dbType).getAllLockSql(lockTable, whereCondition); + String queryLockSql = PageUtil.pageSql(sourceSql, dbType, param.getPageNum(), param.getPageSize()); + String lockCountSql = PageUtil.countSql(sourceSql, dbType); + + List list = new ArrayList<>(); + int count = 0; + + ResultSet rs = null; + ResultSet countRs = null; + + try (Connection conn = dataSource.getConnection(); + PreparedStatement ps = conn.prepareStatement(queryLockSql); + PreparedStatement countPs = conn.prepareStatement(lockCountSql)) { + PageUtil.setObject(ps, sqlParamList); + rs = ps.executeQuery(); + while (rs.next()) { + list.add(GlobalLockVO.convert(rs)); + } + PageUtil.setObject(countPs, sqlParamList); + countRs = countPs.executeQuery(); + if (countRs.next()) { + count = countRs.getInt(1); + } + } + catch (SQLException e) { + throw new StoreException(e); + } + finally { + IOUtil.close(rs, countRs); + } + return PageResult.success(list, count, param.getPageNum(), param.getPageSize()); + } + + private String getWhereConditionByParam(GlobalLockParam param, List sqlParamList) { + StringBuilder whereConditionBuilder = new StringBuilder(); + if (StringUtils.isNotBlank(param.getXid())) { + whereConditionBuilder.append(" and xid = ? "); + sqlParamList.add(param.getXid()); + } + if (StringUtils.isNotBlank(param.getTableName())) { + whereConditionBuilder.append(" and table_name = ? "); + sqlParamList.add(param.getTableName()); + } + if (StringUtils.isNotBlank(param.getTransactionId())) { + whereConditionBuilder.append(" and transaction_id = ? "); + sqlParamList.add(param.getTransactionId()); + } + if (StringUtils.isNotBlank(param.getBranchId())) { + whereConditionBuilder.append(" and branch_id = ? "); + sqlParamList.add(param.getBranchId()); + } + if (param.getTimeStart() != null) { + whereConditionBuilder.append(" and gmt_create >= ? "); + sqlParamList.add(param.getTimeStart()); + } + if (param.getTimeEnd() != null) { + whereConditionBuilder.append(" and gmt_create <= ? "); + sqlParamList.add(param.getTimeEnd()); + } + String whereCondition = whereConditionBuilder.toString(); + return whereCondition.replaceFirst("and", "where"); + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/db/GlobalSessionDBServiceImpl.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/db/GlobalSessionDBServiceImpl.java new file mode 100644 index 000000000..64935d103 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/db/GlobalSessionDBServiceImpl.java @@ -0,0 +1,162 @@ +/* + * 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.console.impl.db; + +import io.seata.common.ConfigurationKeys; +import io.seata.common.exception.StoreException; +import io.seata.common.loader.EnhancedServiceLoader; +import io.seata.common.util.IOUtil; +import io.seata.common.util.PageUtil; +import io.seata.common.util.StringUtils; +import io.seata.config.Configuration; +import io.seata.config.ConfigurationFactory; +import io.seata.console.result.PageResult; +import io.seata.core.store.db.DataSourceProvider; +import io.seata.core.store.db.sql.log.LogStoreSqlsFactory; +import io.seata.server.console.param.GlobalSessionParam; +import io.seata.server.console.service.BranchSessionService; +import io.seata.server.console.service.GlobalSessionService; +import io.seata.server.console.vo.BranchSessionVO; +import io.seata.server.console.vo.GlobalSessionVO; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashSet; +import java.util.List; + +import static io.seata.common.DefaultValues.DEFAULT_STORE_DB_GLOBAL_TABLE; + +/** + * Global Session DataBase ServiceImpl + * + * @author zhongxiang.wang + * @author lvekee 734843455@qq.com + */ +@Component +@org.springframework.context.annotation.Configuration +@ConditionalOnExpression("#{'db'.equals('${sessionMode}')}") +public class GlobalSessionDBServiceImpl implements GlobalSessionService { + + private String globalTable; + + private String dbType; + + private DataSource dataSource; + + @Resource(type = BranchSessionService.class) + private BranchSessionService branchSessionService; + + public GlobalSessionDBServiceImpl() { + Configuration configuration = ConfigurationFactory.getInstance(); + globalTable = configuration.getConfig(ConfigurationKeys.STORE_DB_GLOBAL_TABLE, DEFAULT_STORE_DB_GLOBAL_TABLE); + dbType = configuration.getConfig(ConfigurationKeys.STORE_DB_TYPE); + if (StringUtils.isBlank(dbType)) { + throw new IllegalArgumentException(ConfigurationKeys.STORE_DB_TYPE + " should not be blank"); + } + String dbDataSource = configuration.getConfig(ConfigurationKeys.STORE_DB_DATASOURCE_TYPE); + if (StringUtils.isBlank(dbDataSource)) { + throw new IllegalArgumentException(ConfigurationKeys.STORE_DB_DATASOURCE_TYPE + " should not be blank"); + } + dataSource = EnhancedServiceLoader.load(DataSourceProvider.class, dbDataSource).provide(); + } + + @Override + public PageResult query(GlobalSessionParam param) { + PageUtil.checkParam(param.getPageNum(), param.getPageSize()); + + List sqlParamList = new ArrayList<>(); + String whereCondition = getWhereConditionByParam(param, sqlParamList); + + String sourceSql = LogStoreSqlsFactory.getLogStoreSqls(dbType).getAllGlobalSessionSql(globalTable, + whereCondition); + String querySessionSql = PageUtil.pageSql(sourceSql, dbType, param.getPageNum(), param.getPageSize()); + String sessionCountSql = PageUtil.countSql(sourceSql, dbType); + + List list = new ArrayList<>(); + int count = 0; + + ResultSet rs = null; + ResultSet countRs = null; + + try (Connection conn = dataSource.getConnection(); + PreparedStatement ps = conn.prepareStatement(querySessionSql); + PreparedStatement countPs = conn.prepareStatement(sessionCountSql)) { + PageUtil.setObject(ps, sqlParamList); + rs = ps.executeQuery(); + while (rs.next()) { + list.add(GlobalSessionVO.convert(rs)); + } + + PageUtil.setObject(countPs, sqlParamList); + countRs = countPs.executeQuery(); + if (countRs.next()) { + count = countRs.getInt(1); + } + if (param.isWithBranch()) { + for (GlobalSessionVO globalSessionVO : list) { + PageResult pageResp = branchSessionService.queryByXid(globalSessionVO.getXid()); + globalSessionVO.setBranchSessionVOs(new HashSet<>(pageResp.getData())); + } + } + } + catch (SQLException e) { + throw new StoreException(e); + } + finally { + IOUtil.close(rs, countRs); + } + return PageResult.success(list, count, param.getPageNum(), param.getPageSize()); + } + + private String getWhereConditionByParam(GlobalSessionParam param, List sqlParamList) { + StringBuilder whereConditionBuilder = new StringBuilder(); + if (StringUtils.isNotBlank(param.getXid())) { + whereConditionBuilder.append(" and xid = ? "); + sqlParamList.add(param.getXid()); + } + if (StringUtils.isNotBlank(param.getApplicationId())) { + whereConditionBuilder.append(" and application_id = ? "); + sqlParamList.add(param.getApplicationId()); + } + if (param.getStatus() != null) { + whereConditionBuilder.append(" and status = ? "); + sqlParamList.add(param.getStatus()); + } + if (StringUtils.isNotBlank(param.getTransactionName())) { + whereConditionBuilder.append(" and transaction_name = ? "); + sqlParamList.add(param.getTransactionName()); + } + if (param.getTimeStart() != null) { + whereConditionBuilder.append(" and gmt_create >= ? "); + sqlParamList.add(new Date(param.getTimeStart())); + } + if (param.getTimeEnd() != null) { + whereConditionBuilder.append(" and gmt_create <= ? "); + sqlParamList.add(new Date(param.getTimeEnd())); + } + String whereCondition = whereConditionBuilder.toString(); + return whereCondition.replaceFirst("and", "where"); + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/file/BranchSessionFileServiceImpl.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/file/BranchSessionFileServiceImpl.java new file mode 100644 index 000000000..1dad12400 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/file/BranchSessionFileServiceImpl.java @@ -0,0 +1,40 @@ +/* + * 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.console.impl.file; + +import io.seata.common.exception.NotSupportYetException; +import io.seata.console.result.PageResult; +import io.seata.server.console.service.BranchSessionService; +import io.seata.server.console.vo.BranchSessionVO; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; + +/** + * Branch Session File ServiceImpl + * + * @author zhongxiang.wang + */ +@Component +@org.springframework.context.annotation.Configuration +@ConditionalOnExpression("#{'file'.equals('${sessionMode}')}") +public class BranchSessionFileServiceImpl implements BranchSessionService { + + @Override + public PageResult queryByXid(String xid) { + throw new NotSupportYetException(); + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/file/GlobalLockFileServiceImpl.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/file/GlobalLockFileServiceImpl.java new file mode 100644 index 000000000..770d27a6d --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/file/GlobalLockFileServiceImpl.java @@ -0,0 +1,159 @@ +/* + * 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.console.impl.file; + +import io.seata.common.util.CollectionUtils; +import io.seata.common.util.StringUtils; +import io.seata.console.result.PageResult; +import io.seata.core.lock.RowLock; +import io.seata.server.console.param.GlobalLockParam; +import io.seata.server.console.service.GlobalLockService; +import io.seata.server.console.vo.GlobalLockVO; +import io.seata.server.lock.LockerManagerFactory; +import io.seata.server.session.BranchSession; +import io.seata.server.session.GlobalSession; +import io.seata.server.session.SessionHolder; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static io.seata.common.util.StringUtils.isBlank; +import static io.seata.server.console.vo.GlobalLockVO.convert; +import static java.util.Objects.isNull; + +/** + * Global Lock File ServiceImpl + * + * @author zhongxiang.wang + * @author miaoxueyu + */ +@Component +@org.springframework.context.annotation.Configuration +@ConditionalOnExpression("#{'file'.equals('${lockMode}')}") +public class GlobalLockFileServiceImpl implements GlobalLockService { + + @Override + public PageResult query(GlobalLockParam param) { + checkParam(param); + + final Collection allSessions = SessionHolder.getRootSessionManager().allSessions(); + + final AtomicInteger total = new AtomicInteger(); + List result = allSessions.parallelStream().filter(obtainGlobalSessionPredicate(param)) + .flatMap(globalSession -> globalSession.getBranchSessions().stream()) + .filter(obtainBranchSessionPredicate(param)) + .flatMap(branchSession -> filterAndMap(param, branchSession)) + .peek(globalSession -> total.incrementAndGet()).collect(Collectors.toList()); + + return PageResult.build(convert(result), param.getPageNum(), param.getPageSize()); + + } + + /** + * filter with tableName and generate RowLock + * @param param the query param + * @param branchSession the branch session + * @return the RowLock list + */ + private Stream filterAndMap(GlobalLockParam param, BranchSession branchSession) { + + final String tableName = param.getTableName(); + + // get rowLock from branchSession + final List rowLocks = LockerManagerFactory.getLockManager().collectRowLocks(branchSession); + + if (StringUtils.isNotBlank(tableName)) { + return rowLocks.parallelStream().filter(rowLock -> rowLock.getTableName().contains(param.getTableName())); + } + + return rowLocks.stream(); + } + + /** + * check the param + * @param param the param + */ + private void checkParam(GlobalLockParam param) { + if (param.getPageSize() <= 0 || param.getPageNum() <= 0) { + throw new IllegalArgumentException("wrong pageSize or pageNum"); + } + + // verification data type + try { + Long.parseLong(param.getTransactionId()); + } + catch (NumberFormatException e) { + param.setTransactionId(null); + } + try { + Long.parseLong(param.getBranchId()); + } + catch (NumberFormatException e) { + param.setBranchId(null); + } + + } + + /** + * obtain the branch session condition + * @param param condition for query branch session + * @return the filter condition + */ + private Predicate obtainBranchSessionPredicate(GlobalLockParam param) { + return branchSession -> { + // transactionId + return (isBlank(param.getTransactionId()) + || String.valueOf(branchSession.getTransactionId()).contains(param.getTransactionId())) + + && + // branch id + (isBlank(param.getBranchId()) || String.valueOf(branchSession.getBranchId()).contains(param.getBranchId())); + }; + } + + /** + * obtain the global session condition + * @param param condition for query global session + * @return the filter condition + */ + private Predicate obtainGlobalSessionPredicate(GlobalLockParam param) { + + return globalSession -> { + // first, there must be withBranchSession + return CollectionUtils.isNotEmpty(globalSession.getBranchSessions()) + + && + // The second is other conditions + // xid + (isBlank(param.getXid()) || globalSession.getXid().contains(param.getXid())) + + && + // timeStart + (isNull(param.getTimeStart()) || param.getTimeStart() <= globalSession.getBeginTime()) + + && + // timeEnd + (isNull(param.getTimeEnd()) || param.getTimeEnd() >= globalSession.getBeginTime()); + }; + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/file/GlobalSessionFileServiceImpl.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/file/GlobalSessionFileServiceImpl.java new file mode 100644 index 000000000..91e648044 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/file/GlobalSessionFileServiceImpl.java @@ -0,0 +1,98 @@ +/* + * 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.console.impl.file; + +import io.seata.console.result.PageResult; +import io.seata.server.console.param.GlobalSessionParam; +import io.seata.server.console.service.GlobalSessionService; +import io.seata.server.console.vo.GlobalSessionVO; +import io.seata.server.session.GlobalSession; +import io.seata.server.session.SessionHolder; +import io.seata.server.storage.SessionConverter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import static io.seata.common.util.StringUtils.isBlank; +import static java.util.Objects.isNull; + +/** + * Global Session File ServiceImpl + * + * @author zhongxiang.wang + * @author miaoxueyu + */ +@Component +@org.springframework.context.annotation.Configuration +@ConditionalOnExpression("#{'file'.equals('${sessionMode}')}") +public class GlobalSessionFileServiceImpl implements GlobalSessionService { + + @Override + public PageResult query(GlobalSessionParam param) { + if (param.getPageSize() <= 0 || param.getPageNum() <= 0) { + throw new IllegalArgumentException("wrong pageSize or pageNum"); + } + + final Collection allSessions = SessionHolder.getRootSessionManager().allSessions(); + + final List filteredSessions = allSessions.parallelStream().filter(obtainPredicate(param)) + .collect(Collectors.toList()); + + return PageResult.build(SessionConverter.convertGlobalSession(filteredSessions), param.getPageNum(), + param.getPageSize()); + } + + /** + * obtain the condition + * @param param condition for query global session + * @return the filter condition + */ + private Predicate obtainPredicate(GlobalSessionParam param) { + + return session -> { + return + // xid + (isBlank(param.getXid()) || session.getXid().contains(param.getXid())) + + && + // applicationId + (isBlank(param.getApplicationId()) || session.getApplicationId().contains(param.getApplicationId())) + + && + // status + (isNull(param.getStatus()) || Objects.equals(session.getStatus().getCode(), param.getStatus())) + + && + // transactionName + (isBlank(param.getTransactionName()) || session.getTransactionName().contains(param.getTransactionName())) + + && + // timeStart + (isNull(param.getTimeStart()) || param.getTimeStart() <= session.getBeginTime()) + + && + // timeEnd + (isNull(param.getTimeEnd()) || param.getTimeEnd() >= session.getBeginTime()); + + }; + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/redis/BranchSessionRedisServiceImpl.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/redis/BranchSessionRedisServiceImpl.java new file mode 100644 index 000000000..30b7a028e --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/redis/BranchSessionRedisServiceImpl.java @@ -0,0 +1,66 @@ +/* + * 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.console.impl.redis; + +import io.seata.common.util.CollectionUtils; +import io.seata.common.util.StringUtils; +import io.seata.console.result.PageResult; +import io.seata.core.store.BranchTransactionDO; +import io.seata.server.console.service.BranchSessionService; +import io.seata.server.console.vo.BranchSessionVO; +import io.seata.server.storage.redis.store.RedisTransactionStoreManager; +import org.springframework.beans.BeanUtils; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +/** + * Branch Session Redis ServiceImpl + * + * @author zhongxiang.wang + * @author doubleDimple + */ +@Component +@org.springframework.context.annotation.Configuration +@ConditionalOnExpression("#{'redis'.equals('${sessionMode}')}") +public class BranchSessionRedisServiceImpl implements BranchSessionService { + + @Override + public PageResult queryByXid(String xid) { + if (StringUtils.isBlank(xid)) { + return PageResult.success(); + } + + List branchSessionVos = new ArrayList<>(); + + RedisTransactionStoreManager instance = RedisTransactionStoreManager.getInstance(); + + List branchSessionDos = instance.findBranchSessionByXid(xid); + + if (CollectionUtils.isNotEmpty(branchSessionDos)) { + for (BranchTransactionDO branchSessionDo : branchSessionDos) { + BranchSessionVO branchSessionVO = new BranchSessionVO(); + BeanUtils.copyProperties(branchSessionDo, branchSessionVO); + branchSessionVos.add(branchSessionVO); + } + } + + return PageResult.success(branchSessionVos, branchSessionVos.size(), 0, branchSessionVos.size()); + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/redis/GlobalLockRedisServiceImpl.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/redis/GlobalLockRedisServiceImpl.java new file mode 100644 index 000000000..6624adc7b --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/redis/GlobalLockRedisServiceImpl.java @@ -0,0 +1,121 @@ +/* + * 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.console.impl.redis; + +import io.seata.common.util.BeanUtils; +import io.seata.common.util.CollectionUtils; +import io.seata.console.result.PageResult; +import io.seata.server.console.param.GlobalLockParam; +import io.seata.server.console.service.GlobalLockService; +import io.seata.server.console.vo.GlobalLockVO; +import io.seata.server.storage.redis.JedisPooledFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; +import redis.clients.jedis.Jedis; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import static io.seata.common.Constants.ROW_LOCK_KEY_SPLIT_CHAR; +import static io.seata.common.exception.FrameworkErrorCode.ParameterRequired; +import static io.seata.common.util.StringUtils.isNotBlank; +import static io.seata.console.result.PageResult.checkPage; +import static io.seata.core.constants.RedisKeyConstants.*; + +/** + * Global Lock Redis Service Impl + * + * @author zhongxiang.wang + * @author doubleDimple + */ +@Component +@org.springframework.context.annotation.Configuration +@ConditionalOnExpression("#{'redis'.equals('${lockMode}')}") +public class GlobalLockRedisServiceImpl implements GlobalLockService { + + @Override + public PageResult query(GlobalLockParam param) { + + int total = 0; + List globalLockVos; + checkPage(param); + if (isNotBlank(param.getXid())) { + globalLockVos = queryGlobalByXid(param.getXid()); + total = globalLockVos.size(); + return PageResult.success(globalLockVos, total, param.getPageNum(), param.getPageSize()); + } + else if (isNotBlank(param.getTableName()) && isNotBlank(param.getPk()) && isNotBlank(param.getResourceId())) { + // SEATA_ROW_LOCK_jdbc:mysql://116.62.62.26/seata-order^^^order^^^2188 + String tableName = param.getTableName(); + String pk = param.getPk(); + String resourceId = param.getResourceId(); + globalLockVos = queryGlobalLockByRowKey(buildRowKey(tableName, pk, resourceId)); + total = globalLockVos.size(); + return PageResult.success(globalLockVos, total, param.getPageNum(), param.getPageSize()); + } + else { + return PageResult.failure(ParameterRequired.getErrCode(), + "only three parameters of tableName,pk,resourceId or Xid are supported"); + } + } + + private List queryGlobalLockByRowKey(String buildRowKey) { + return readGlobalLockByRowKey(buildRowKey); + } + + private String buildRowKey(String tableName, String pk, String resourceId) { + return DEFAULT_REDIS_SEATA_ROW_LOCK_PREFIX + resourceId + SPLIT + tableName + SPLIT + pk; + } + + private List queryGlobalByXid(String xid) { + return readGlobalLockByXid(DEFAULT_REDIS_SEATA_GLOBAL_LOCK_PREFIX + xid); + } + + private List readGlobalLockByXid(String key) { + List vos = new ArrayList<>(); + try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { + Map mapGlobalKeys = jedis.hgetAll(key); + if (CollectionUtils.isNotEmpty(mapGlobalKeys)) { + List rowLockKeys = new ArrayList<>(); + mapGlobalKeys.forEach((k, v) -> rowLockKeys.addAll(Arrays.asList(v.split(ROW_LOCK_KEY_SPLIT_CHAR)))); + for (String rowLoclKey : rowLockKeys) { + Map mapRowLockKey = jedis.hgetAll(rowLoclKey); + GlobalLockVO vo = (GlobalLockVO) BeanUtils.mapToObject(mapRowLockKey, GlobalLockVO.class); + if (vo != null) { + vos.add(vo); + } + } + } + } + + return vos; + } + + private List readGlobalLockByRowKey(String key) { + List vos = new ArrayList<>(); + try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { + Map map = jedis.hgetAll(key); + GlobalLockVO vo = (GlobalLockVO) BeanUtils.mapToObject(map, GlobalLockVO.class); + if (vo != null) { + vos.add(vo); + } + } + return vos; + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/redis/GlobalSessionRedisServiceImpl.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/redis/GlobalSessionRedisServiceImpl.java new file mode 100644 index 000000000..3e6fef645 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/redis/GlobalSessionRedisServiceImpl.java @@ -0,0 +1,116 @@ +/* + * 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.console.impl.redis; + +import io.seata.common.util.CollectionUtils; +import io.seata.console.result.PageResult; +import io.seata.core.model.GlobalStatus; +import io.seata.server.console.param.GlobalSessionParam; +import io.seata.server.console.service.GlobalSessionService; +import io.seata.server.console.vo.GlobalSessionVO; +import io.seata.server.session.GlobalSession; +import io.seata.server.session.SessionCondition; +import io.seata.server.storage.redis.store.RedisTransactionStoreManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import static io.seata.common.exception.FrameworkErrorCode.ParameterRequired; +import static io.seata.common.util.StringUtils.isBlank; +import static io.seata.common.util.StringUtils.isNotBlank; +import static io.seata.console.result.PageResult.checkPage; +import static io.seata.server.storage.SessionConverter.convertToGlobalSessionVo; + +/** + * Global Session Redis ServiceImpl + * + * @author zhongxiang.wang + * @author doubleDimple + */ +@Component +@org.springframework.context.annotation.Configuration +@ConditionalOnExpression("#{'redis'.equals('${sessionMode}')}") +public class GlobalSessionRedisServiceImpl implements GlobalSessionService { + + private static final Logger LOGGER = LoggerFactory.getLogger(GlobalSessionRedisServiceImpl.class); + + @Override + public PageResult query(GlobalSessionParam param) { + List result = new ArrayList<>(); + Long total = 0L; + if (param.getTimeStart() != null || param.getTimeEnd() != null) { + // not support time range query + LOGGER.debug("not supported according to time range query"); + return PageResult.failure(ParameterRequired.getErrCode(), "not supported according to time range query"); + } + List globalSessions = new ArrayList<>(); + + RedisTransactionStoreManager instance = RedisTransactionStoreManager.getInstance(); + + checkPage(param); + + if (isBlank(param.getXid()) && param.getStatus() == null) { + total = instance.countByGlobalSessions(GlobalStatus.values()); + globalSessions = instance.findGlobalSessionByPage(param.getPageNum(), param.getPageSize(), + param.isWithBranch()); + } + else { + List globalSessionsNew = new ArrayList<>(); + if (isNotBlank(param.getXid())) { + SessionCondition sessionCondition = new SessionCondition(); + sessionCondition.setXid(param.getXid()); + sessionCondition.setLazyLoadBranch(!param.isWithBranch()); + globalSessions = instance.readSession(sessionCondition); + total = (long) globalSessions.size(); + } + + if (param.getStatus() != null && GlobalStatus.get(param.getStatus()) != null) { + if (CollectionUtils.isNotEmpty(globalSessions)) { + globalSessionsNew = globalSessions.stream() + .filter(globalSession -> globalSession.getStatus().getCode() == (param.getStatus())) + .collect(Collectors.toList()); + total = (long) globalSessionsNew.size(); + } + else { + total = instance.countByGlobalSessions(new GlobalStatus[] { GlobalStatus.get(param.getStatus()) }); + globalSessionsNew = instance.readSessionStatusByPage(param); + } + } + + if (LOGGER.isDebugEnabled()) { + if (isNotBlank(param.getApplicationId())) { + // not support + LOGGER.debug("not supported according to applicationId query"); + } + if (isNotBlank(param.getTransactionName())) { + // not support + LOGGER.debug("not supported according to transactionName query"); + } + } + globalSessions = globalSessionsNew.size() > 0 ? globalSessionsNew : globalSessions; + } + + convertToGlobalSessionVo(result, globalSessions); + + return PageResult.success(result, total.intValue(), param.getPageNum(), param.getPageSize()); + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/param/GlobalLockParam.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/param/GlobalLockParam.java new file mode 100644 index 000000000..d9b147849 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/param/GlobalLockParam.java @@ -0,0 +1,116 @@ +/* + * 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.console.param; + +import io.seata.console.param.BaseParam; + +import java.io.Serializable; + +/** + * Global lock param + * + * @author zhongxiang.wang + */ +public class GlobalLockParam extends BaseParam implements Serializable { + + private static final long serialVersionUID = 615412528070131284L; + + /** + * the xid + */ + private String xid; + + /** + * the table name + */ + private String tableName; + + /** + * the transaction id + */ + private String transactionId; + + /** + * the branch id + */ + private String branchId; + + /** + * the primary Key + */ + private String pk; + + /** + * the resourceId + */ + private String resourceId; + + public String getTransactionId() { + return transactionId; + } + + public void setTransactionId(String transactionId) { + this.transactionId = transactionId; + } + + public String getBranchId() { + return branchId; + } + + public void setBranchId(String branchId) { + this.branchId = branchId; + } + + public String getXid() { + return xid; + } + + public void setXid(String xid) { + this.xid = xid; + } + + public String getTableName() { + return tableName; + } + + public void setTableName(String tableName) { + this.tableName = tableName; + } + + public String getPk() { + return pk; + } + + public void setPk(String pk) { + this.pk = pk; + } + + public String getResourceId() { + return resourceId; + } + + public void setResourceId(String resourceId) { + this.resourceId = resourceId; + } + + @Override + public String toString() { + return "GlobalLockParam{" + "xid='" + xid + '\'' + ", tableName='" + tableName + '\'' + ", transactionId='" + + transactionId + '\'' + ", branchId='" + branchId + '\'' + ", pk='" + pk + '\'' + ", resourceId='" + + resourceId + '\'' + '}'; + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/param/GlobalSessionParam.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/param/GlobalSessionParam.java new file mode 100644 index 000000000..1ab332317 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/param/GlobalSessionParam.java @@ -0,0 +1,102 @@ +/* + * 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.console.param; + +import io.seata.console.param.BaseParam; + +import java.io.Serializable; + +/** + * Global session param + * + * @author zhongxiang.wang + */ +public class GlobalSessionParam extends BaseParam implements Serializable { + + private static final long serialVersionUID = 115488252809011284L; + + /** + * the xid + */ + private String xid; + + /** + * the application id + */ + private String applicationId; + + /** + * the global session status + */ + private Integer status; + + /** + * the transaction name + */ + private String transactionName; + + /** + * if with branch true: with branch session false: no branch session + */ + private boolean withBranch; + + public String getXid() { + return xid; + } + + public void setXid(String xid) { + this.xid = xid; + } + + public String getTransactionName() { + return transactionName; + } + + public void setTransactionName(String transactionName) { + this.transactionName = transactionName; + } + + public String getApplicationId() { + return applicationId; + } + + public void setApplicationId(String applicationId) { + this.applicationId = applicationId; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public boolean isWithBranch() { + return withBranch; + } + + public void setWithBranch(boolean withBranch) { + this.withBranch = withBranch; + } + + @Override + public String toString() { + return "GlobalSessionParam{" + "xid='" + xid + '\'' + ", applicationId='" + applicationId + '\'' + ", status=" + + status + ", transactionName='" + transactionName + '\'' + ", withBranch=" + withBranch + '}'; + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ansi/AnsiElement.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/service/BranchSessionService.java similarity index 63% rename from pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ansi/AnsiElement.java rename to pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/service/BranchSessionService.java index 8af1d13d4..7eaf40091 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ansi/AnsiElement.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/service/BranchSessionService.java @@ -13,20 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.server.logging.logback.ansi; +package io.seata.server.console.service; + +import io.seata.console.result.PageResult; +import io.seata.server.console.vo.BranchSessionVO; /** - * An ANSI encodable element. + * Branch session service * - * @author Phillip Webb - * @origin Copied from spring-boot.jar by wang.liang + * @author wangzhongxiang */ -public interface AnsiElement { +public interface BranchSessionService { /** - * @return the ANSI escape code + * Query branch session by xid + * @param xid the xid + * @return the BranchSessionVO list */ - @Override - String toString(); + PageResult queryByXid(String xid); } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ansi/AnsiStyle.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/service/GlobalLockService.java similarity index 54% rename from pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ansi/AnsiStyle.java rename to pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/service/GlobalLockService.java index 1080d9fc7..de8b428aa 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ansi/AnsiStyle.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/service/GlobalLockService.java @@ -13,53 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.server.logging.logback.ansi; +package io.seata.server.console.service; + +import io.seata.console.result.PageResult; +import io.seata.server.console.param.GlobalLockParam; +import io.seata.server.console.vo.GlobalLockVO; /** - * {@link AnsiElement Ansi} styles. + * Global lock service * - * @author Phillip Webb - * @origin Copied from spring-boot.jar by wang.liang + * @author wangzhongxiang */ -public enum AnsiStyle implements AnsiElement { +public interface GlobalLockService { /** - * normal + * Query locks by param + * @param param the param + * @return the list of GlobalLockVO */ - NORMAL("0"), - - /** - * bold - */ - BOLD("1"), - - /** - * faint - */ - FAINT("2"), - - /** - * italic - */ - ITALIC("3"), - - /** - * underline - */ - UNDERLINE("4"); - - /** - * code of style - */ - private final String code; - - AnsiStyle(String code) { - this.code = code; - } - - @Override - public String toString() { - return this.code; - } + PageResult query(GlobalLockParam param); } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/service/GlobalSessionService.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/service/GlobalSessionService.java new file mode 100644 index 000000000..c9ef50ef0 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/service/GlobalSessionService.java @@ -0,0 +1,36 @@ +/* + * 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.console.service; + +import io.seata.console.result.PageResult; +import io.seata.server.console.param.GlobalSessionParam; +import io.seata.server.console.vo.GlobalSessionVO; + +/** + * Global session service + * + * @author wangzhongxiang + */ +public interface GlobalSessionService { + + /** + * Query global session + * @param param the param + * @return the GlobalSessionVO list + */ + PageResult query(GlobalSessionParam param); + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/vo/BranchSessionVO.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/vo/BranchSessionVO.java new file mode 100644 index 000000000..7400176fd --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/vo/BranchSessionVO.java @@ -0,0 +1,213 @@ +/* + * 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.console.vo; + +import io.seata.core.constants.ServerTableColumnsName; + +import java.sql.Date; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Objects; + +/** + * BranchSessionVO + * + * @author zhongxiang.wang + */ +public class BranchSessionVO { + + private String xid; + + private String transactionId; + + private String branchId; + + private String resourceGroupId; + + private String resourceId; + + private String branchType; + + private Integer status; + + private String clientId; + + private String applicationData; + + private Long gmtCreate; + + private Long gmtModified; + + public BranchSessionVO() { + + } + + public BranchSessionVO(String xid, Long transactionId, Long branchId, String resourceGroupId, String resourceId, + String branchType, Integer status, String clientId, String applicationData) { + this.xid = xid; + this.transactionId = String.valueOf(transactionId); + this.branchId = String.valueOf(branchId); + this.resourceGroupId = resourceGroupId; + this.resourceId = resourceId; + this.branchType = branchType; + this.status = status; + this.clientId = clientId; + this.applicationData = applicationData; + } + + public String getXid() { + return xid; + } + + public void setXid(String xid) { + this.xid = xid; + } + + public String getTransactionId() { + return transactionId; + } + + public void setTransactionId(Long transactionId) { + this.transactionId = String.valueOf(transactionId); + } + + public String getBranchId() { + return branchId; + } + + public void setBranchId(Long branchId) { + this.branchId = String.valueOf(branchId); + } + + public String getResourceGroupId() { + return resourceGroupId; + } + + public void setResourceGroupId(String resourceGroupId) { + this.resourceGroupId = resourceGroupId; + } + + public String getResourceId() { + return resourceId; + } + + public void setResourceId(String resourceId) { + this.resourceId = resourceId; + } + + public String getBranchType() { + return branchType; + } + + public void setBranchType(String branchType) { + this.branchType = branchType; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public String getApplicationData() { + return applicationData; + } + + public void setApplicationData(String applicationData) { + this.applicationData = applicationData; + } + + public Long getGmtCreate() { + return gmtCreate; + } + + public void setGmtCreate(Long gmtCreate) { + this.gmtCreate = gmtCreate; + } + + public Long getGmtModified() { + return gmtModified; + } + + public void setGmtModified(Long gmtModified) { + this.gmtModified = gmtModified; + } + + public static BranchSessionVO convert(ResultSet rs) throws SQLException { + BranchSessionVO branchSessionVO = new BranchSessionVO(); + branchSessionVO.setXid(rs.getString(ServerTableColumnsName.BRANCH_TABLE_XID)); + branchSessionVO.setTransactionId(rs.getLong(ServerTableColumnsName.BRANCH_TABLE_TRANSACTION_ID)); + branchSessionVO.setBranchId(rs.getLong(ServerTableColumnsName.BRANCH_TABLE_BRANCH_ID)); + branchSessionVO.setResourceGroupId(rs.getString(ServerTableColumnsName.BRANCH_TABLE_RESOURCE_GROUP_ID)); + branchSessionVO.setResourceId(rs.getString(ServerTableColumnsName.BRANCH_TABLE_RESOURCE_ID)); + branchSessionVO.setBranchType(rs.getString(ServerTableColumnsName.BRANCH_TABLE_BRANCH_TYPE)); + branchSessionVO.setStatus(rs.getInt(ServerTableColumnsName.BRANCH_TABLE_STATUS)); + branchSessionVO.setClientId(rs.getString(ServerTableColumnsName.BRANCH_TABLE_CLIENT_ID)); + branchSessionVO.setApplicationData(rs.getString(ServerTableColumnsName.BRANCH_TABLE_APPLICATION_DATA)); + Date gmtCreateTimestamp = rs.getDate(ServerTableColumnsName.BRANCH_TABLE_GMT_CREATE); + if (gmtCreateTimestamp != null) { + branchSessionVO.setGmtCreate(gmtCreateTimestamp.getTime()); + } + Date gmtModifiedTimestamp = rs.getDate(ServerTableColumnsName.BRANCH_TABLE_GMT_MODIFIED); + if (gmtModifiedTimestamp != null) { + branchSessionVO.setGmtModified(gmtModifiedTimestamp.getTime()); + } + return branchSessionVO; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + BranchSessionVO that = (BranchSessionVO) o; + return Objects.equals(xid, that.xid) && Objects.equals(transactionId, that.transactionId) + && Objects.equals(branchId, that.branchId) && Objects.equals(resourceGroupId, that.resourceGroupId) + && Objects.equals(resourceId, that.resourceId) && Objects.equals(branchType, that.branchType) + && Objects.equals(status, that.status) && Objects.equals(clientId, that.clientId) + && Objects.equals(applicationData, that.applicationData) && Objects.equals(gmtCreate, that.gmtCreate) + && Objects.equals(gmtModified, that.gmtModified); + } + + @Override + public int hashCode() { + return Objects.hash(xid, transactionId, branchId, resourceGroupId, resourceId, branchType, status, clientId, + applicationData, gmtCreate, gmtModified); + } + + @Override + public String toString() { + return "BranchSessionVO{" + "xid='" + xid + '\'' + ", transactionId=" + transactionId + ", branchId=" + branchId + + ", resourceGroupId='" + resourceGroupId + '\'' + ", resourceId='" + resourceId + '\'' + + ", branchType='" + branchType + '\'' + ", status=" + status + ", clientId='" + clientId + '\'' + + ", applicationData='" + applicationData + '\'' + ", gmtCreate=" + gmtCreate + ", gmtModified=" + + gmtModified + '}'; + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/vo/GlobalLockVO.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/vo/GlobalLockVO.java new file mode 100644 index 000000000..f16ea70dd --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/vo/GlobalLockVO.java @@ -0,0 +1,188 @@ +/* + * 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.console.vo; + +import io.seata.common.util.CollectionUtils; +import io.seata.core.constants.ServerTableColumnsName; +import io.seata.core.lock.RowLock; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * GlobalLockVO + * + * @author zhongxiang.wang + * @author miaoxueyu + */ +public class GlobalLockVO { + + private String xid; + + private String transactionId; + + private String branchId; + + private String resourceId; + + private String tableName; + + private String pk; + + private String rowKey; + + private Long gmtCreate; + + private Long gmtModified; + + /** + * convert RowLock list to GlobalLockVO list + * @param rowLocks the RowLock list + * @return the GlobalLockVO list + */ + public static List convert(List rowLocks) { + if (CollectionUtils.isEmpty(rowLocks)) { + return Collections.emptyList(); + } + final List result = new ArrayList<>(rowLocks.size()); + for (RowLock rowLock : rowLocks) { + result.add(convert(rowLock)); + } + + return result; + } + + /** + * convert RowLock to GlobalLockVO + * @param rowLock the RowLock + * @return the GlobalLockVO + */ + public static GlobalLockVO convert(RowLock rowLock) { + final GlobalLockVO globalLockVO = new GlobalLockVO(); + globalLockVO.setXid(rowLock.getXid()); + globalLockVO.setTransactionId(rowLock.getTransactionId()); + globalLockVO.setBranchId(rowLock.getBranchId()); + globalLockVO.setResourceId(rowLock.getResourceId()); + globalLockVO.setTableName(rowLock.getTableName()); + globalLockVO.setPk(rowLock.getPk()); + globalLockVO.setRowKey(rowLock.getRowKey()); + return globalLockVO; + } + + public String getXid() { + return xid; + } + + public void setXid(String xid) { + this.xid = xid; + } + + public String getTransactionId() { + return transactionId; + } + + public void setTransactionId(Long transactionId) { + this.transactionId = String.valueOf(transactionId); + } + + public String getBranchId() { + return branchId; + } + + public void setBranchId(Long branchId) { + this.branchId = String.valueOf(branchId); + } + + public String getResourceId() { + return resourceId; + } + + public void setResourceId(String resourceId) { + this.resourceId = resourceId; + } + + public String getTableName() { + return tableName; + } + + public void setTableName(String tableName) { + this.tableName = tableName; + } + + public String getPk() { + return pk; + } + + public void setPk(String pk) { + this.pk = pk; + } + + public String getRowKey() { + return rowKey; + } + + public void setRowKey(String rowKey) { + this.rowKey = rowKey; + } + + public Long getGmtCreate() { + return gmtCreate; + } + + public void setGmtCreate(Long gmtCreate) { + this.gmtCreate = gmtCreate; + } + + public Long getGmtModified() { + return gmtModified; + } + + public void setGmtModified(Long gmtModified) { + this.gmtModified = gmtModified; + } + + public static GlobalLockVO convert(ResultSet rs) throws SQLException { + GlobalLockVO globalLockVO = new GlobalLockVO(); + globalLockVO.setRowKey(rs.getString(ServerTableColumnsName.LOCK_TABLE_ROW_KEY)); + globalLockVO.setXid(rs.getString(ServerTableColumnsName.LOCK_TABLE_XID)); + globalLockVO.setTransactionId(rs.getLong(ServerTableColumnsName.LOCK_TABLE_TRANSACTION_ID)); + globalLockVO.setBranchId(rs.getLong(ServerTableColumnsName.LOCK_TABLE_BRANCH_ID)); + globalLockVO.setResourceId(rs.getString(ServerTableColumnsName.LOCK_TABLE_RESOURCE_ID)); + globalLockVO.setTableName(rs.getString(ServerTableColumnsName.LOCK_TABLE_TABLE_NAME)); + globalLockVO.setPk(rs.getString(ServerTableColumnsName.LOCK_TABLE_PK)); + Timestamp gmtCreateTimestamp = rs.getTimestamp(ServerTableColumnsName.LOCK_TABLE_GMT_CREATE); + if (gmtCreateTimestamp != null) { + globalLockVO.setGmtCreate(gmtCreateTimestamp.getTime()); + } + Timestamp gmtModifiedTimestamp = rs.getTimestamp(ServerTableColumnsName.LOCK_TABLE_GMT_MODIFIED); + if (gmtModifiedTimestamp != null) { + globalLockVO.setGmtModified(gmtModifiedTimestamp.getTime()); + } + return globalLockVO; + } + + @Override + public String toString() { + return "GlobalLockVO{" + "xid='" + xid + '\'' + ", transactionId=" + transactionId + ", branchId=" + branchId + + ", resourceId='" + resourceId + '\'' + ", tableName='" + tableName + '\'' + ", pk='" + pk + '\'' + + ", rowKey='" + rowKey + '\'' + ", gmtCreate=" + gmtCreate + ", gmtModified=" + gmtModified + '}'; + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/vo/GlobalSessionVO.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/vo/GlobalSessionVO.java new file mode 100644 index 000000000..f1711f8fc --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/vo/GlobalSessionVO.java @@ -0,0 +1,203 @@ +/* + * 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.console.vo; + +import io.seata.core.constants.ServerTableColumnsName; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.Set; + +/** + * GlobalSessionVO + * + * @author zhongxiang.wang + */ +public class GlobalSessionVO { + + private String xid; + + private String transactionId; + + private Integer status; + + private String applicationId; + + private String transactionServiceGroup; + + private String transactionName; + + private Long timeout; + + private Long beginTime; + + private String applicationData; + + private Long gmtCreate; + + private Long gmtModified; + + private Set branchSessionVOs; + + public GlobalSessionVO() { + + } + + public GlobalSessionVO(String xid, Long transactionId, Integer status, String applicationId, + String transactionServiceGroup, String transactionName, Long timeout, Long beginTime, + String applicationData, Set branchSessionVOs) { + this.xid = xid; + this.transactionId = String.valueOf(transactionId); + this.status = status; + this.applicationId = applicationId; + this.transactionServiceGroup = transactionServiceGroup; + this.transactionName = transactionName; + this.timeout = timeout; + this.beginTime = beginTime; + this.applicationData = applicationData; + this.branchSessionVOs = branchSessionVOs; + } + + public String getXid() { + return xid; + } + + public void setXid(String xid) { + this.xid = xid; + } + + public String getTransactionId() { + return transactionId; + } + + public void setTransactionId(Long transactionId) { + this.transactionId = String.valueOf(transactionId); + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getApplicationId() { + return applicationId; + } + + public void setApplicationId(String applicationId) { + this.applicationId = applicationId; + } + + public String getTransactionServiceGroup() { + return transactionServiceGroup; + } + + public void setTransactionServiceGroup(String transactionServiceGroup) { + this.transactionServiceGroup = transactionServiceGroup; + } + + public String getTransactionName() { + return transactionName; + } + + public void setTransactionName(String transactionName) { + this.transactionName = transactionName; + } + + public Long getTimeout() { + return timeout; + } + + public void setTimeout(Long timeout) { + this.timeout = timeout; + } + + public Long getBeginTime() { + return beginTime; + } + + public void setBeginTime(Long beginTime) { + this.beginTime = beginTime; + } + + public String getApplicationData() { + return applicationData; + } + + public void setApplicationData(String applicationData) { + this.applicationData = applicationData; + } + + public Long getGmtCreate() { + return gmtCreate; + } + + public void setGmtCreate(Long gmtCreate) { + this.gmtCreate = gmtCreate; + } + + public Long getGmtModified() { + return gmtModified; + } + + public void setGmtModified(Long gmtModified) { + this.gmtModified = gmtModified; + } + + public Set getBranchSessionVOs() { + return branchSessionVOs; + } + + public void setBranchSessionVOs(Set branchSessionVOs) { + this.branchSessionVOs = branchSessionVOs; + } + + public static GlobalSessionVO convert(ResultSet rs) throws SQLException { + GlobalSessionVO globalSessionVO = new GlobalSessionVO(); + globalSessionVO.setXid(rs.getString(ServerTableColumnsName.GLOBAL_TABLE_XID)); + globalSessionVO.setTransactionId(rs.getLong(ServerTableColumnsName.GLOBAL_TABLE_TRANSACTION_ID)); + globalSessionVO.setStatus(rs.getInt(ServerTableColumnsName.GLOBAL_TABLE_STATUS)); + globalSessionVO.setApplicationId(rs.getString(ServerTableColumnsName.GLOBAL_TABLE_APPLICATION_ID)); + globalSessionVO.setTransactionServiceGroup( + rs.getString(ServerTableColumnsName.GLOBAL_TABLE_TRANSACTION_SERVICE_GROUP)); + globalSessionVO.setTransactionName(rs.getString(ServerTableColumnsName.GLOBAL_TABLE_TRANSACTION_NAME)); + globalSessionVO.setTimeout(rs.getLong(ServerTableColumnsName.GLOBAL_TABLE_TIMEOUT)); + globalSessionVO.setBeginTime(rs.getLong(ServerTableColumnsName.GLOBAL_TABLE_BEGIN_TIME)); + globalSessionVO.setApplicationData(rs.getString(ServerTableColumnsName.GLOBAL_TABLE_APPLICATION_DATA)); + Timestamp gmtCreateTimestamp = rs.getTimestamp(ServerTableColumnsName.GLOBAL_TABLE_GMT_CREATE); + if (gmtCreateTimestamp != null) { + globalSessionVO.setGmtCreate(gmtCreateTimestamp.getTime()); + } + Timestamp gmtModifiedTimestamp = rs.getTimestamp(ServerTableColumnsName.GLOBAL_TABLE_GMT_MODIFIED); + if (gmtModifiedTimestamp != null) { + globalSessionVO.setGmtModified(gmtModifiedTimestamp.getTime()); + } + return globalSessionVO; + } + + @Override + public String toString() { + return "GlobalSessionVO{" + "xid='" + xid + '\'' + ", transactionId=" + transactionId + ", status=" + status + + ", applicationId='" + applicationId + '\'' + ", transactionServiceGroup='" + transactionServiceGroup + + '\'' + ", transactionName='" + transactionName + '\'' + ", timeout=" + timeout + ", beginTime=" + + beginTime + ", applicationData='" + applicationData + '\'' + ", gmtCreate=" + gmtCreate + + ", gmtModified=" + gmtModified + ", branchSessionVOs=" + branchSessionVOs + '}'; + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/controller/HealthController.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/controller/HealthController.java new file mode 100644 index 000000000..9f89cdc99 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/controller/HealthController.java @@ -0,0 +1,44 @@ +/* + * 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.controller; + +import io.seata.server.ServerRunner; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +/** + * @author spilledyear@outlook.com + */ +@Controller +@RequestMapping +public class HealthController { + + private static final String OK = "ok"; + + private static final String NOT_OK = "not_ok"; + + @Autowired + private ServerRunner serverRunner; + + @RequestMapping("/health") + @ResponseBody + String healthCheck() { + return serverRunner.started() ? OK : NOT_OK; + } + +} \ No newline at end of file diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/coordinator/AbstractCore.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/coordinator/AbstractCore.java index 59247fc4b..73a541df4 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/coordinator/AbstractCore.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/coordinator/AbstractCore.java @@ -15,9 +15,6 @@ */ package io.seata.server.coordinator; -import java.io.IOException; -import java.util.concurrent.TimeoutException; - import io.seata.core.context.RootContext; import io.seata.core.exception.BranchTransactionException; import io.seata.core.exception.GlobalTransactionException; @@ -41,12 +38,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; -import static io.seata.core.exception.TransactionExceptionCode.BranchTransactionNotExist; -import static io.seata.core.exception.TransactionExceptionCode.FailedToAddBranch; -import static io.seata.core.exception.TransactionExceptionCode.GlobalTransactionNotActive; -import static io.seata.core.exception.TransactionExceptionCode.GlobalTransactionStatusInvalid; -import static io.seata.core.exception.TransactionExceptionCode.FailedToSendBranchCommitRequest; -import static io.seata.core.exception.TransactionExceptionCode.FailedToSendBranchRollbackRequest; +import java.io.IOException; +import java.util.concurrent.TimeoutException; + +import static io.seata.core.exception.TransactionExceptionCode.*; /** * The type abstract core. @@ -141,6 +136,7 @@ public abstract class AbstractCore implements Core { throw new BranchTransactionException(BranchTransactionNotExist, String.format("Could not found branch session xid = %s branchId = %s", xid, branchId)); } + branchSession.setApplicationData(applicationData); globalSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); globalSession.changeBranchStatus(branchSession, status); diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/coordinator/DefaultCoordinator.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/coordinator/DefaultCoordinator.java index e3930085c..a0eefcacd 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/coordinator/DefaultCoordinator.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/coordinator/DefaultCoordinator.java @@ -15,11 +15,6 @@ */ package io.seata.server.coordinator; -import java.time.Duration; -import java.util.Collection; -import java.util.Map; -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.TimeUnit; import io.netty.channel.Channel; import io.seata.common.thread.NamedThreadFactory; import io.seata.common.util.CollectionUtils; @@ -27,31 +22,11 @@ import io.seata.common.util.DurationUtil; import io.seata.config.ConfigurationFactory; import io.seata.core.constants.ConfigurationKeys; import io.seata.core.context.RootContext; -import io.seata.core.event.EventBus; -import io.seata.core.event.GlobalTransactionEvent; import io.seata.core.exception.TransactionException; import io.seata.core.model.GlobalStatus; import io.seata.core.protocol.AbstractMessage; import io.seata.core.protocol.AbstractResultMessage; -import io.seata.core.protocol.transaction.AbstractTransactionRequestToTC; -import io.seata.core.protocol.transaction.AbstractTransactionResponse; -import io.seata.core.protocol.transaction.BranchRegisterRequest; -import io.seata.core.protocol.transaction.BranchRegisterResponse; -import io.seata.core.protocol.transaction.BranchReportRequest; -import io.seata.core.protocol.transaction.BranchReportResponse; -import io.seata.core.protocol.transaction.GlobalBeginRequest; -import io.seata.core.protocol.transaction.GlobalBeginResponse; -import io.seata.core.protocol.transaction.GlobalCommitRequest; -import io.seata.core.protocol.transaction.GlobalCommitResponse; -import io.seata.core.protocol.transaction.GlobalLockQueryRequest; -import io.seata.core.protocol.transaction.GlobalLockQueryResponse; -import io.seata.core.protocol.transaction.GlobalReportRequest; -import io.seata.core.protocol.transaction.GlobalReportResponse; -import io.seata.core.protocol.transaction.GlobalRollbackRequest; -import io.seata.core.protocol.transaction.GlobalRollbackResponse; -import io.seata.core.protocol.transaction.GlobalStatusRequest; -import io.seata.core.protocol.transaction.GlobalStatusResponse; -import io.seata.core.protocol.transaction.UndoLogDeleteRequest; +import io.seata.core.protocol.transaction.*; import io.seata.core.rpc.Disposable; import io.seata.core.rpc.RemotingServer; import io.seata.core.rpc.RpcContext; @@ -59,14 +34,23 @@ import io.seata.core.rpc.TransactionMessageHandler; import io.seata.core.rpc.netty.ChannelManager; import io.seata.core.rpc.netty.NettyRemotingServer; import io.seata.server.AbstractTCInboundHandler; -import io.seata.server.event.EventBusManager; -import io.seata.server.session.GlobalSession; -import io.seata.server.session.SessionHelper; -import io.seata.server.session.SessionHolder; +import io.seata.server.metrics.MetricsPublisher; +import io.seata.server.session.*; 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; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import static io.seata.common.Constants.*; +import static io.seata.common.DefaultValues.*; + /** * The type Default coordinator. */ @@ -80,30 +64,31 @@ public class DefaultCoordinator extends AbstractTCInboundHandler implements Tran * The constant COMMITTING_RETRY_PERIOD. */ protected static final long COMMITTING_RETRY_PERIOD = CONFIG.getLong(ConfigurationKeys.COMMITING_RETRY_PERIOD, - 1000L); + DEFAULT_COMMITING_RETRY_PERIOD); /** * The constant ASYNC_COMMITTING_RETRY_PERIOD. */ protected static final long ASYNC_COMMITTING_RETRY_PERIOD = CONFIG - .getLong(ConfigurationKeys.ASYN_COMMITING_RETRY_PERIOD, 1000L); + .getLong(ConfigurationKeys.ASYNC_COMMITING_RETRY_PERIOD, DEFAULT_ASYNC_COMMITTING_RETRY_PERIOD); /** * The constant ROLLBACKING_RETRY_PERIOD. */ protected static final long ROLLBACKING_RETRY_PERIOD = CONFIG.getLong(ConfigurationKeys.ROLLBACKING_RETRY_PERIOD, - 1000L); + DEFAULT_ROLLBACKING_RETRY_PERIOD); /** * The constant TIMEOUT_RETRY_PERIOD. */ - protected static final long TIMEOUT_RETRY_PERIOD = CONFIG.getLong(ConfigurationKeys.TIMEOUT_RETRY_PERIOD, 1000L); + protected static final long TIMEOUT_RETRY_PERIOD = CONFIG.getLong(ConfigurationKeys.TIMEOUT_RETRY_PERIOD, + DEFAULT_TIMEOUT_RETRY_PERIOD); /** * The Transaction undo log delete period. */ protected static final long UNDO_LOG_DELETE_PERIOD = CONFIG - .getLong(ConfigurationKeys.TRANSACTION_UNDO_LOG_DELETE_PERIOD, 24 * 60 * 60 * 1000); + .getLong(ConfigurationKeys.TRANSACTION_UNDO_LOG_DELETE_PERIOD, DEFAULT_UNDO_LOG_DELETE_PERIOD); /** * The Transaction undo log delay delete period @@ -112,45 +97,114 @@ public class DefaultCoordinator extends AbstractTCInboundHandler implements Tran private static final int ALWAYS_RETRY_BOUNDARY = 0; - private static final Duration MAX_COMMIT_RETRY_TIMEOUT = ConfigurationFactory.getInstance() - .getDuration(ConfigurationKeys.MAX_COMMIT_RETRY_TIMEOUT, DurationUtil.DEFAULT_DURATION, 100); + /** + * default branch async queue size + */ + private static final int DEFAULT_BRANCH_ASYNC_QUEUE_SIZE = 5000; - private static final Duration MAX_ROLLBACK_RETRY_TIMEOUT = ConfigurationFactory.getInstance() - .getDuration(ConfigurationKeys.MAX_ROLLBACK_RETRY_TIMEOUT, DurationUtil.DEFAULT_DURATION, 100); + /** + * the pool size of branch asynchronous remove thread pool + */ + private static final int BRANCH_ASYNC_POOL_SIZE = Runtime.getRuntime().availableProcessors(); - private static final boolean ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE = ConfigurationFactory.getInstance() - .getBoolean(ConfigurationKeys.ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE, false); + 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 ScheduledThreadPoolExecutor retryRollbacking = new ScheduledThreadPoolExecutor(1, - new NamedThreadFactory("RetryRollbacking", 1)); + 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 ScheduledThreadPoolExecutor retryCommitting = new ScheduledThreadPoolExecutor(1, - new NamedThreadFactory("RetryCommitting", 1)); + private static final boolean ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE = ConfigurationFactory.getInstance().getBoolean( + ConfigurationKeys.ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE, DEFAULT_ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE); - private ScheduledThreadPoolExecutor asyncCommitting = new ScheduledThreadPoolExecutor(1, - new NamedThreadFactory("AsyncCommitting", 1)); + private final ScheduledThreadPoolExecutor retryRollbacking = new ScheduledThreadPoolExecutor(1, + new NamedThreadFactory(RETRY_ROLLBACKING, 1)); - private ScheduledThreadPoolExecutor timeoutCheck = new ScheduledThreadPoolExecutor(1, - new NamedThreadFactory("TxTimeoutCheck", 1)); + private final ScheduledThreadPoolExecutor retryCommitting = new ScheduledThreadPoolExecutor(1, + new NamedThreadFactory(RETRY_COMMITTING, 1)); - private ScheduledThreadPoolExecutor undoLogDelete = new ScheduledThreadPoolExecutor(1, - new NamedThreadFactory("UndoLogDelete", 1)); + private final ScheduledThreadPoolExecutor asyncCommitting = new ScheduledThreadPoolExecutor(1, + new NamedThreadFactory(ASYNC_COMMITTING, 1)); + + private final ScheduledThreadPoolExecutor timeoutCheck = new ScheduledThreadPoolExecutor(1, + new NamedThreadFactory(TX_TIMEOUT_CHECK, 1)); + + private final ScheduledThreadPoolExecutor undoLogDelete = new ScheduledThreadPoolExecutor(1, + new NamedThreadFactory(UNDOLOG_DELETE, 1)); + + 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 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 RemotingServer remotingServer; - private DefaultCore core; + private final DefaultCore core; - private EventBus eventBus = EventBusManager.get(); + private static volatile DefaultCoordinator instance; /** * Instantiates a new Default coordinator. * @param remotingServer the remoting server */ - public DefaultCoordinator(RemotingServer remotingServer) { + private DefaultCoordinator(RemotingServer remotingServer) { + if (remotingServer == null) { + throw new IllegalArgumentException("RemotingServer not allowed be null."); + } this.remotingServer = remotingServer; this.core = new DefaultCore(remotingServer); } + public static DefaultCoordinator getInstance(RemotingServer remotingServer) { + if (null == instance) { + synchronized (DefaultCoordinator.class) { + if (null == instance) { + instance = new DefaultCoordinator(remotingServer); + } + } + } + return instance; + } + + public static DefaultCoordinator getInstance() { + if (null == instance) { + throw new IllegalArgumentException("The instance has not been created."); + } + return instance; + } + + /** + * Asynchronous remove branch + * @param globalSession the globalSession + * @param branchSession the branchSession + */ + public void doBranchRemoveAsync(GlobalSession globalSession, BranchSession branchSession) { + if (globalSession == null) { + return; + } + branchRemoveExecutor.execute(new BranchRemoveTask(globalSession, branchSession)); + } + + /** + * Asynchronous remove all branch + * @param globalSession the globalSession + */ + public void doBranchRemoveAllAsync(GlobalSession globalSession) { + if (globalSession == null) { + return; + } + branchRemoveExecutor.execute(new BranchRemoveTask(globalSession)); + } + @Override protected void doGlobalBegin(GlobalBeginRequest request, GlobalBeginResponse response, RpcContext rpcContext) throws TransactionException { @@ -219,47 +273,45 @@ public class DefaultCoordinator extends AbstractTCInboundHandler implements Tran /** * Timeout check. - * @throws TransactionException the transaction exception */ - protected void timeoutCheck() throws TransactionException { - Collection allSessions = SessionHolder.getRootSessionManager().allSessions(); - if (CollectionUtils.isEmpty(allSessions)) { + protected void timeoutCheck() { + SessionCondition sessionCondition = new SessionCondition(GlobalStatus.Begin); + sessionCondition.setLazyLoadBranch(true); + Collection beginGlobalsessions = SessionHolder.getRootSessionManager() + .findGlobalSessions(sessionCondition); + if (CollectionUtils.isEmpty(beginGlobalsessions)) { return; } - if (allSessions.size() > 0 && LOGGER.isDebugEnabled()) { - LOGGER.debug("Global transaction timeout check begin, size: {}", allSessions.size()); + if (!beginGlobalsessions.isEmpty() && LOGGER.isDebugEnabled()) { + LOGGER.debug("Global transaction timeout check begin, size: {}", beginGlobalsessions.size()); } - SessionHelper.forEach(allSessions, globalSession -> { + SessionHelper.forEach(beginGlobalsessions, globalSession -> { if (LOGGER.isDebugEnabled()) { LOGGER.debug(globalSession.getXid() + " " + globalSession.getStatus() + " " + globalSession.getBeginTime() + " " + globalSession.getTimeout()); } - boolean shouldTimeout = SessionHolder.lockAndExecute(globalSession, () -> { + SessionHolder.lockAndExecute(globalSession, () -> { if (globalSession.getStatus() != GlobalStatus.Begin || !globalSession.isTimeout()) { return false; } + + LOGGER.info("Global transaction[{}] is timeout and will be rollback.", globalSession.getXid()); + globalSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); globalSession.close(); - globalSession.changeStatus(GlobalStatus.TimeoutRollbacking); + globalSession.setStatus(GlobalStatus.TimeoutRollbacking); + + globalSession.addSessionLifecycleListener(SessionHolder.getRetryRollbackingSessionManager()); + SessionHolder.getRetryRollbackingSessionManager().addGlobalSession(globalSession); // transaction timeout and start rollbacking event - eventBus.post(new GlobalTransactionEvent(globalSession.getTransactionId(), - GlobalTransactionEvent.ROLE_TC, globalSession.getTransactionName(), - globalSession.getApplicationId(), globalSession.getTransactionServiceGroup(), - globalSession.getBeginTime(), null, globalSession.getStatus())); + MetricsPublisher.postSessionDoingEvent(globalSession, GlobalStatus.TimeoutRollbacking.name(), false, + false); return true; }); - if (!shouldTimeout) { - // The function of this 'return' is 'continue'. - return; - } - LOGGER.info("Global transaction[{}] is timeout and will be rollback.", globalSession.getXid()); - - globalSession.addSessionLifecycleListener(SessionHolder.getRetryRollbackingSessionManager()); - SessionHolder.getRetryRollbackingSessionManager().addGlobalSession(globalSession); }); - if (allSessions.size() > 0 && LOGGER.isDebugEnabled()) { + if (!beginGlobalsessions.isEmpty() && LOGGER.isDebugEnabled()) { LOGGER.debug("Global transaction timeout check end. "); } @@ -269,7 +321,10 @@ public class DefaultCoordinator extends AbstractTCInboundHandler implements Tran * Handle retry rollbacking. */ protected void handleRetryRollbacking() { - Collection rollbackingSessions = SessionHolder.getRetryRollbackingSessionManager().allSessions(); + SessionCondition sessionCondition = new SessionCondition(rollbackingStatuses); + sessionCondition.setLazyLoadBranch(true); + Collection rollbackingSessions = SessionHolder.getRetryRollbackingSessionManager() + .findGlobalSessions(sessionCondition); if (CollectionUtils.isEmpty(rollbackingSessions)) { return; } @@ -277,8 +332,7 @@ public class DefaultCoordinator extends AbstractTCInboundHandler implements Tran SessionHelper.forEach(rollbackingSessions, rollbackingSession -> { try { // prevent repeated rollback - if (rollbackingSession.getStatus().equals(GlobalStatus.Rollbacking) - && !rollbackingSession.isDeadSession()) { + if (rollbackingSession.getStatus() == GlobalStatus.Rollbacking && !rollbackingSession.isDeadSession()) { // The function of this 'return' is 'continue'. return; } @@ -286,12 +340,13 @@ public class DefaultCoordinator extends AbstractTCInboundHandler implements Tran if (ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE) { rollbackingSession.clean(); } - /** - * Prevent thread safety issues - */ + // Prevent thread safety issues SessionHolder.getRetryRollbackingSessionManager().removeGlobalSession(rollbackingSession); - LOGGER.info("Global transaction rollback retry timeout and has removed [{}]", + LOGGER.error("Global transaction rollback retry timeout and has removed [{}]", rollbackingSession.getXid()); + + SessionHelper.endRollbackFailed(rollbackingSession, true, true); + // The function of this 'return' is 'continue'. return; } @@ -309,7 +364,10 @@ public class DefaultCoordinator extends AbstractTCInboundHandler implements Tran * Handle retry committing. */ protected void handleRetryCommitting() { - Collection committingSessions = SessionHolder.getRetryCommittingSessionManager().allSessions(); + SessionCondition retryCommittingSessionCondition = new SessionCondition(retryCommittingStatuses); + retryCommittingSessionCondition.setLazyLoadBranch(true); + Collection committingSessions = SessionHolder.getRetryCommittingSessionManager() + .findGlobalSessions(retryCommittingSessionCondition); if (CollectionUtils.isEmpty(committingSessions)) { return; } @@ -317,18 +375,19 @@ public class DefaultCoordinator extends AbstractTCInboundHandler implements Tran SessionHelper.forEach(committingSessions, committingSession -> { try { // prevent repeated commit - if (committingSession.getStatus().equals(GlobalStatus.Committing) - && !committingSession.isDeadSession()) { + if (committingSession.getStatus() == GlobalStatus.Committing && !committingSession.isDeadSession()) { // The function of this 'return' is 'continue'. return; } if (isRetryTimeout(now, MAX_COMMIT_RETRY_TIMEOUT.toMillis(), committingSession.getBeginTime())) { - /** - * Prevent thread safety issues - */ + // Prevent thread safety issues SessionHolder.getRetryCommittingSessionManager().removeGlobalSession(committingSession); LOGGER.error("Global transaction commit retry timeout and has removed [{}]", committingSession.getXid()); + + // commit retry timeout event + SessionHelper.endCommitFailed(committingSession, true, true); + // The function of this 'return' is 'continue'. return; } @@ -342,27 +401,18 @@ public class DefaultCoordinator extends AbstractTCInboundHandler implements Tran }); } - private boolean isRetryTimeout(long now, long timeout, long beginTime) { - return timeout >= ALWAYS_RETRY_BOUNDARY && now - beginTime > timeout; - } - /** * Handle async committing. */ protected void handleAsyncCommitting() { + SessionCondition sessionCondition = new SessionCondition(GlobalStatus.AsyncCommitting); Collection asyncCommittingSessions = SessionHolder.getAsyncCommittingSessionManager() - .allSessions(); + .findGlobalSessions(sessionCondition); if (CollectionUtils.isEmpty(asyncCommittingSessions)) { return; } SessionHelper.forEach(asyncCommittingSessions, asyncCommittingSession -> { try { - // Instruction reordering in DefaultCore#asyncCommit may cause this - // situation - if (GlobalStatus.AsyncCommitting != asyncCommittingSession.getStatus()) { - // The function of this 'return' is 'continue'. - return; - } asyncCommittingSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); core.doGlobalCommit(asyncCommittingSession, true); } @@ -401,84 +451,33 @@ public class DefaultCoordinator extends AbstractTCInboundHandler implements Tran } } + private boolean isRetryTimeout(long now, long timeout, long beginTime) { + return timeout >= ALWAYS_RETRY_BOUNDARY && now - beginTime > timeout; + } + /** * Init. */ public void init() { - retryRollbacking.scheduleAtFixedRate(() -> { - boolean lock = SessionHolder.retryRollbackingLock(); - if (lock) { - try { - handleRetryRollbacking(); - } - catch (Exception e) { - LOGGER.info("Exception retry rollbacking ... ", e); - } - finally { - SessionHolder.unRetryRollbackingLock(); - } - } - }, 0, ROLLBACKING_RETRY_PERIOD, TimeUnit.MILLISECONDS); + retryRollbacking.scheduleAtFixedRate( + () -> SessionHolder.distributedLockAndExecute(RETRY_ROLLBACKING, this::handleRetryRollbacking), 0, + ROLLBACKING_RETRY_PERIOD, TimeUnit.MILLISECONDS); - retryCommitting.scheduleAtFixedRate(() -> { - boolean lock = SessionHolder.retryCommittingLock(); - if (lock) { - try { - handleRetryCommitting(); - } - catch (Exception e) { - LOGGER.info("Exception retry committing ... ", e); - } - finally { - SessionHolder.unRetryCommittingLock(); - } - } - }, 0, COMMITTING_RETRY_PERIOD, TimeUnit.MILLISECONDS); + retryCommitting.scheduleAtFixedRate( + () -> SessionHolder.distributedLockAndExecute(RETRY_COMMITTING, this::handleRetryCommitting), 0, + COMMITTING_RETRY_PERIOD, TimeUnit.MILLISECONDS); - asyncCommitting.scheduleAtFixedRate(() -> { - boolean lock = SessionHolder.asyncCommittingLock(); - if (lock) { - try { - handleAsyncCommitting(); - } - catch (Exception e) { - LOGGER.info("Exception async committing ... ", e); - } - finally { - SessionHolder.unAsyncCommittingLock(); - } - } - }, 0, ASYNC_COMMITTING_RETRY_PERIOD, TimeUnit.MILLISECONDS); + asyncCommitting.scheduleAtFixedRate( + () -> SessionHolder.distributedLockAndExecute(ASYNC_COMMITTING, this::handleAsyncCommitting), 0, + ASYNC_COMMITTING_RETRY_PERIOD, TimeUnit.MILLISECONDS); - timeoutCheck.scheduleAtFixedRate(() -> { - boolean lock = SessionHolder.txTimeoutCheckLock(); - if (lock) { - try { - timeoutCheck(); - } - catch (Exception e) { - LOGGER.info("Exception timeout checking ... ", e); - } - finally { - SessionHolder.unTxTimeoutCheckLock(); - } - } - }, 0, TIMEOUT_RETRY_PERIOD, TimeUnit.MILLISECONDS); + timeoutCheck.scheduleAtFixedRate( + () -> SessionHolder.distributedLockAndExecute(TX_TIMEOUT_CHECK, this::timeoutCheck), 0, + TIMEOUT_RETRY_PERIOD, TimeUnit.MILLISECONDS); - undoLogDelete.scheduleAtFixedRate(() -> { - boolean lock = SessionHolder.undoLogDeleteLock(); - if (lock) { - try { - undoLogDelete(); - } - catch (Exception e) { - LOGGER.info("Exception undoLog deleting ... ", e); - } - finally { - SessionHolder.unUndoLogDeleteLock(); - } - } - }, UNDO_LOG_DELAY_DELETE_PERIOD, UNDO_LOG_DELETE_PERIOD, TimeUnit.MILLISECONDS); + undoLogDelete.scheduleAtFixedRate( + () -> SessionHolder.distributedLockAndExecute(UNDOLOG_DELETE, this::undoLogDelete), + UNDO_LOG_DELAY_DELETE_PERIOD, UNDO_LOG_DELETE_PERIOD, TimeUnit.MILLISECONDS); } @Override @@ -507,11 +506,15 @@ public class DefaultCoordinator extends AbstractTCInboundHandler implements Tran retryCommitting.shutdown(); asyncCommitting.shutdown(); timeoutCheck.shutdown(); + undoLogDelete.shutdown(); + 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); } catch (InterruptedException ignore) { @@ -520,8 +523,92 @@ public class DefaultCoordinator extends AbstractTCInboundHandler implements Tran if (remotingServer instanceof NettyRemotingServer) { ((NettyRemotingServer) remotingServer).destroy(); } - // 3. last destroy SessionHolder + // 3. third destroy SessionHolder SessionHolder.destroy(); + instance = null; + } + + /** + * only used for mock test + * @param remotingServer + */ + public void setRemotingServer(RemotingServer remotingServer) { + this.remotingServer = remotingServer; + } + + /** + * the task to remove branchSession + */ + static class BranchRemoveTask implements Runnable { + + /** + * the globalSession + */ + private final GlobalSession globalSession; + + /** + * the branchSession + */ + private final BranchSession branchSession; + + /** + * If you use this construct, the task will remove the branchSession provided by + * the parameter + * @param globalSession the globalSession + */ + public BranchRemoveTask(GlobalSession globalSession, BranchSession branchSession) { + this.globalSession = globalSession; + this.branchSession = branchSession; + } + + /** + * If you use this construct, the task will remove all branchSession + * @param globalSession the globalSession + */ + public BranchRemoveTask(GlobalSession globalSession) { + this.globalSession = globalSession; + this.branchSession = null; + } + + @Override + public void run() { + if (globalSession == null) { + return; + } + try { + MDC.put(RootContext.MDC_KEY_XID, globalSession.getXid()); + if (branchSession != null) { + doRemove(branchSession); + } + else { + globalSession.getSortedBranches().forEach(this::doRemove); + } + } + catch (Exception unKnowException) { + LOGGER.error("Asynchronous delete branchSession error, xid = {}", globalSession.getXid(), + unKnowException); + } + finally { + MDC.remove(RootContext.MDC_KEY_XID); + } + } + + private void doRemove(BranchSession bt) { + try { + MDC.put(RootContext.MDC_KEY_BRANCH_ID, String.valueOf(bt.getBranchId())); + globalSession.removeBranch(bt); + LOGGER.info("Asynchronous delete branchSession successfully, xid = {}, branchId = {}", + globalSession.getXid(), bt.getBranchId()); + } + catch (TransactionException transactionException) { + LOGGER.error("Asynchronous delete branchSession error, xid = {}, branchId = {}", globalSession.getXid(), + bt.getBranchId(), transactionException); + } + finally { + MDC.remove(RootContext.MDC_KEY_BRANCH_ID); + } + } + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/coordinator/DefaultCore.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/coordinator/DefaultCore.java index 45492891e..b44bd5d79 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/coordinator/DefaultCore.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/coordinator/DefaultCore.java @@ -15,23 +15,19 @@ */ package io.seata.server.coordinator; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - +import io.seata.common.DefaultValues; import io.seata.common.exception.NotSupportYetException; import io.seata.common.loader.EnhancedServiceLoader; import io.seata.common.util.CollectionUtils; +import io.seata.config.ConfigurationFactory; import io.seata.core.context.RootContext; -import io.seata.core.event.EventBus; -import io.seata.core.event.GlobalTransactionEvent; import io.seata.core.exception.TransactionException; import io.seata.core.logger.StackTraceLogger; import io.seata.core.model.BranchStatus; import io.seata.core.model.BranchType; import io.seata.core.model.GlobalStatus; import io.seata.core.rpc.RemotingServer; -import io.seata.server.event.EventBusManager; +import io.seata.server.metrics.MetricsPublisher; import io.seata.server.session.BranchSession; import io.seata.server.session.GlobalSession; import io.seata.server.session.SessionHelper; @@ -40,6 +36,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import static io.seata.core.constants.ConfigurationKeys.XAER_NOTA_RETRY_TIMEOUT; import static io.seata.server.session.BranchSessionHandler.CONTINUE; /** @@ -51,7 +52,8 @@ public class DefaultCore implements Core { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultCore.class); - private EventBus eventBus = EventBusManager.get(); + private static final int RETRY_XAER_NOTA_TIMEOUT = ConfigurationFactory.getInstance() + .getInt(XAER_NOTA_RETRY_TIMEOUT, DefaultValues.DEFAULT_XAER_NOTA_RETRY_TIMEOUT); private static Map coreMap = new ConcurrentHashMap<>(); @@ -132,9 +134,7 @@ public class DefaultCore implements Core { session.begin(); // transaction start event - eventBus.post(new GlobalTransactionEvent(session.getTransactionId(), GlobalTransactionEvent.ROLE_TC, - session.getTransactionName(), applicationId, transactionServiceGroup, session.getBeginTime(), null, - session.getStatus())); + MetricsPublisher.postSessionDoingEvent(session, false); return session.getXid(); } @@ -145,20 +145,27 @@ public class DefaultCore implements Core { if (globalSession == null) { return GlobalStatus.Finished; } + + if (globalSession.isTimeout()) { + LOGGER.info("TC detected timeout, xid = {}", globalSession.getXid()); + return GlobalStatus.TimeoutRollbacking; + } + globalSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); // just lock changeStatus boolean shouldCommit = SessionHolder.lockAndExecute(globalSession, () -> { - // Highlight: Firstly, close the session, then no more branch can be - // registered. - globalSession.closeAndClean(); if (globalSession.getStatus() == GlobalStatus.Begin) { + // Highlight: Firstly, close the session, then no more branch can be + // registered. + globalSession.closeAndClean(); if (globalSession.canBeCommittedAsync()) { globalSession.asyncCommit(); + MetricsPublisher.postSessionDoneEvent(globalSession, GlobalStatus.Committed, false, false); return false; } else { - globalSession.changeStatus(GlobalStatus.Committing); + globalSession.changeGlobalStatus(GlobalStatus.Committing); return true; } } @@ -187,10 +194,7 @@ public class DefaultCore implements Core { public boolean doGlobalCommit(GlobalSession globalSession, boolean retrying) throws TransactionException { boolean success = true; // start committing event - eventBus.post(new GlobalTransactionEvent(globalSession.getTransactionId(), GlobalTransactionEvent.ROLE_TC, - globalSession.getTransactionName(), globalSession.getApplicationId(), - globalSession.getTransactionServiceGroup(), globalSession.getBeginTime(), null, - globalSession.getStatus())); + MetricsPublisher.postSessionDoingEvent(globalSession, retrying); if (globalSession.isSaga()) { success = getCore(BranchType.SAGA).doGlobalCommit(globalSession, retrying); @@ -204,31 +208,29 @@ public class DefaultCore implements Core { BranchStatus currentStatus = branchSession.getStatus(); if (currentStatus == BranchStatus.PhaseOne_Failed) { - globalSession.removeBranch(branchSession); + SessionHelper.removeBranch(globalSession, branchSession, !retrying); return CONTINUE; } try { BranchStatus branchStatus = getCore(branchSession.getBranchType()).branchCommit(globalSession, branchSession); - + if (isXaerNotaTimeout(globalSession, branchStatus)) { + LOGGER.info("Commit branch XAER_NOTA retry timeout, xid = {} branchId = {}", + globalSession.getXid(), branchSession.getBranchId()); + branchStatus = BranchStatus.PhaseTwo_Committed; + } switch (branchStatus) { case PhaseTwo_Committed: - globalSession.removeBranch(branchSession); + SessionHelper.removeBranch(globalSession, branchSession, !retrying); return CONTINUE; case PhaseTwo_CommitFailed_Unretryable: - if (globalSession.canBeCommittedAsync()) { - LOGGER.error( - "Committing branch transaction[{}], status: PhaseTwo_CommitFailed_Unretryable, please check the business log.", - branchSession.getBranchId()); - return CONTINUE; - } - else { - SessionHelper.endCommitFailed(globalSession); - LOGGER.error( - "Committing global transaction[{}] finally failed, caused by branch transaction[{}] commit failed.", - globalSession.getXid(), branchSession.getBranchId()); - return false; - } + // not at branch + SessionHelper.endCommitFailed(globalSession, retrying); + LOGGER.error( + "Committing global transaction[{}] finally failed, caused by branch transaction[{}] commit failed.", + globalSession.getXid(), branchSession.getBranchId()); + return false; + default: if (!retrying) { globalSession.queueToRetryCommit(); @@ -269,16 +271,15 @@ public class DefaultCore implements Core { return false; } } - // If success and there is no branch, end the global transaction. + // if it succeeds and there is no branch, retrying=true is the asynchronous state + // when retrying. EndCommitted is + // executed to improve concurrency performance, and the global transaction ends.. if (success && globalSession.getBranchSessions().isEmpty()) { - SessionHelper.endCommitted(globalSession); - - // committed event - eventBus.post(new GlobalTransactionEvent(globalSession.getTransactionId(), GlobalTransactionEvent.ROLE_TC, - globalSession.getTransactionName(), globalSession.getApplicationId(), - globalSession.getTransactionServiceGroup(), globalSession.getBeginTime(), - System.currentTimeMillis(), globalSession.getStatus())); - + if (!retrying) { + // contains not AT branch + globalSession.setStatus(GlobalStatus.Committed); + } + SessionHelper.endCommitted(globalSession, retrying); LOGGER.info("Committing global transaction is successfully done, xid = {}.", globalSession.getXid()); } return success; @@ -296,7 +297,7 @@ public class DefaultCore implements Core { globalSession.close(); // Highlight: Firstly, close the session, then no more // branch can be registered. if (globalSession.getStatus() == GlobalStatus.Begin) { - globalSession.changeStatus(GlobalStatus.Rollbacking); + globalSession.changeGlobalStatus(GlobalStatus.Rollbacking); return true; } return false; @@ -305,18 +306,15 @@ public class DefaultCore implements Core { return globalSession.getStatus(); } - doGlobalRollback(globalSession, false); - return globalSession.getStatus(); + boolean rollbackSuccess = doGlobalRollback(globalSession, false); + return rollbackSuccess ? GlobalStatus.Rollbacked : globalSession.getStatus(); } @Override public boolean doGlobalRollback(GlobalSession globalSession, boolean retrying) throws TransactionException { boolean success = true; // start rollback event - eventBus.post(new GlobalTransactionEvent(globalSession.getTransactionId(), GlobalTransactionEvent.ROLE_TC, - globalSession.getTransactionName(), globalSession.getApplicationId(), - globalSession.getTransactionServiceGroup(), globalSession.getBeginTime(), null, - globalSession.getStatus())); + MetricsPublisher.postSessionDoingEvent(globalSession, retrying); if (globalSession.isSaga()) { success = getCore(BranchType.SAGA).doGlobalRollback(globalSession, retrying); @@ -325,19 +323,24 @@ public class DefaultCore implements Core { Boolean result = SessionHelper.forEach(globalSession.getReverseSortedBranches(), branchSession -> { BranchStatus currentBranchStatus = branchSession.getStatus(); if (currentBranchStatus == BranchStatus.PhaseOne_Failed) { - globalSession.removeBranch(branchSession); + SessionHelper.removeBranch(globalSession, branchSession, !retrying); return CONTINUE; } try { BranchStatus branchStatus = branchRollback(globalSession, branchSession); + if (isXaerNotaTimeout(globalSession, branchStatus)) { + LOGGER.info("Rollback branch XAER_NOTA retry timeout, xid = {} branchId = {}", + globalSession.getXid(), branchSession.getBranchId()); + branchStatus = BranchStatus.PhaseTwo_Rollbacked; + } switch (branchStatus) { case PhaseTwo_Rollbacked: - globalSession.removeBranch(branchSession); + SessionHelper.removeBranch(globalSession, branchSession, !retrying); LOGGER.info("Rollback branch transaction successfully, xid = {} branchId = {}", globalSession.getXid(), branchSession.getBranchId()); return CONTINUE; case PhaseTwo_RollbackFailed_Unretryable: - SessionHelper.endRollbackFailed(globalSession); + SessionHelper.endRollbackFailed(globalSession, retrying); LOGGER.info("Rollback branch transaction fail and stop retry, xid = {} branchId = {}", globalSession.getXid(), branchSession.getBranchId()); return false; @@ -365,32 +368,13 @@ public class DefaultCore implements Core { if (result != null) { return result; } - - // In db mode, there is a problem of inconsistent data in multiple copies, - // resulting in new branch - // transaction registration when rolling back. - // 1. New branch transaction and rollback branch transaction have no data - // association - // 2. New branch transaction has data association with rollback branch - // transaction - // The second query can solve the first problem, and if it is the second - // problem, it may cause a rollback - // failure due to data changes. - GlobalSession globalSessionTwice = SessionHolder.findGlobalSession(globalSession.getXid()); - if (globalSessionTwice != null && globalSessionTwice.hasBranch()) { - LOGGER.info("Rollbacking global transaction is NOT done, xid = {}.", globalSession.getXid()); - return false; - } } + + // In db mode, lock and branch data residual problems may occur. + // Therefore, execution needs to be delayed here and cannot be executed + // synchronously. if (success) { - SessionHelper.endRollbacked(globalSession); - - // rollbacked event - eventBus.post(new GlobalTransactionEvent(globalSession.getTransactionId(), GlobalTransactionEvent.ROLE_TC, - globalSession.getTransactionName(), globalSession.getApplicationId(), - globalSession.getTransactionServiceGroup(), globalSession.getBeginTime(), - System.currentTimeMillis(), globalSession.getStatus())); - + SessionHelper.endRollbacked(globalSession, retrying); LOGGER.info("Rollback global transaction successfully, xid = {}.", globalSession.getXid()); } return success; @@ -426,4 +410,15 @@ public class DefaultCore implements Core { } } + private boolean isXaerNotaTimeout(GlobalSession globalSession, BranchStatus branchStatus) { + if (BranchStatus.PhaseTwo_CommitFailed_XAER_NOTA_Retryable.equals(branchStatus) + || BranchStatus.PhaseTwo_RollbackFailed_XAER_NOTA_Retryable.equals(branchStatus)) { + return System.currentTimeMillis() > globalSession.getBeginTime() + globalSession.getTimeout() + + Math.max(RETRY_XAER_NOTA_TIMEOUT, globalSession.getTimeout()); + } + else { + return false; + } + } + } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/env/ContainerHelper.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/env/ContainerHelper.java index b7dd20149..457d88e8f 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/env/ContainerHelper.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/env/ContainerHelper.java @@ -18,13 +18,7 @@ package io.seata.server.env; import io.seata.common.util.NumberUtils; import io.seata.common.util.StringUtils; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.stream.Stream; - -import static io.seata.common.DefaultValues.SERVER_DEFAULT_PORT; +import static io.seata.core.constants.ConfigurationKeys.ENV_SEATA_PORT_KEY; /** * @author xingfudeshi@gmail.com @@ -44,27 +38,11 @@ public class ContainerHelper { private static final String ENV_SERVER_NODE_KEY = "SERVER_NODE"; - private static final String ENV_SEATA_PORT_KEY = "SEATA_PORT"; - private static final String ENV_STORE_MODE_KEY = "STORE_MODE"; - /** - * Judge if application is run in container. - * @return If application is run in container - */ - public static boolean isRunningInContainer() { - Path path = Paths.get(C_GROUP_PATH); - if (Files.exists(path)) { - try (Stream stream = Files.lines(path)) { - return stream.anyMatch(line -> line.contains(DOCKER_PATH) || line.contains(KUBEPODS_PATH)); - } - catch (IOException e) { - System.err.println("Judge if running in container failed: " + e.getMessage()); - e.printStackTrace(); - } - } - return false; - } + private static final String ENV_LOCK_STORE_MODE_KEY = "LOCK_STORE_MODE"; + + private static final String ENV_SESSION_STORE_MODE_KEY = "SESSION_STORE_MODE"; /** * Gets env from container. @@ -87,7 +65,7 @@ public class ContainerHelper { * @return the env */ public static int getPort() { - return NumberUtils.toInt(System.getenv(ENV_SEATA_PORT_KEY), SERVER_DEFAULT_PORT); + return NumberUtils.toInt(System.getenv(ENV_SEATA_PORT_KEY), 0); } /** @@ -106,4 +84,20 @@ public class ContainerHelper { return StringUtils.trimToNull(System.getenv(ENV_STORE_MODE_KEY)); } + /** + * Gets session store mode from container. + * @return the env + */ + public static String getSessionStoreMode() { + return StringUtils.trimToNull(System.getenv(ENV_SESSION_STORE_MODE_KEY)); + } + + /** + * Gets lock store mode from container. + * @return the env + */ + public static String getLockStoreMode() { + return StringUtils.trimToNull(System.getenv(ENV_LOCK_STORE_MODE_KEY)); + } + } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/env/PortHelper.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/env/PortHelper.java index 58fc56a07..29bfe8806 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/env/PortHelper.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/env/PortHelper.java @@ -15,28 +15,120 @@ */ package io.seata.server.env; +import io.seata.common.util.CollectionUtils; +import io.seata.common.util.MapUtil; import io.seata.common.util.NumberUtils; +import io.seata.common.util.StringUtils; +import org.springframework.util.ResourceUtils; +import org.yaml.snakeyaml.Yaml; -import static io.seata.common.DefaultValues.SERVER_DEFAULT_PORT; +import java.io.*; +import java.util.Map; +import java.util.Properties; /** * @author wang.liang */ public class PortHelper { - public static int getPort(String[] args) { - if (ContainerHelper.isRunningInContainer()) { - return ContainerHelper.getPort(); - } - else if (args != null && args.length >= 2) { + public static int getPortFromEnvOrStartup(String[] args) { + int port = 0; + if (args != null && args.length >= 2) { for (int i = 0; i < args.length; ++i) { if ("-p".equalsIgnoreCase(args[i]) && i < args.length - 1) { - return NumberUtils.toInt(args[i + 1], SERVER_DEFAULT_PORT); + port = NumberUtils.toInt(args[i + 1], 0); } } } + if (port == 0) { + port = ContainerHelper.getPort(); + } + return port; + } + + /** + * get config from configFile -Dspring.config.location > + * classpath:application.properties > classpath:application.yml + * @return the port + * @throws IOException the io exception + */ + public static int getPortFromConfigFile() throws IOException { + + int port = 8080; + File configFile = null; + File startupConfigFile = getConfigFromStartup(); + if (null != startupConfigFile) { + configFile = startupConfigFile; + } + else { + try { + File propertiesFile = ResourceUtils.getFile("classpath:application.properties"); + configFile = propertiesFile; + } + catch (FileNotFoundException exx) { + File ymlFile = ResourceUtils.getFile("classpath:application.yml"); + configFile = ymlFile; + } + } + InputStream inputStream = null; + try { + inputStream = new FileInputStream(configFile); + String fileName = configFile.getName(); + String portNum = null; + if (fileName.endsWith("yml")) { + Map yamlMap = new Yaml().load(inputStream); + Map configMap = MapUtil.getFlattenedMap(yamlMap); + if (CollectionUtils.isNotEmpty(configMap)) { + Object serverPort = configMap.get("server.port"); + if (null != serverPort) { + portNum = serverPort.toString(); + } + } + } + else { + Properties properties = new Properties(); + properties.load(inputStream); + portNum = properties.getProperty("server.port"); + } + if (null != portNum) { + try { + port = Integer.parseInt(portNum); + } + catch (NumberFormatException exx) { + // ignore + } + } + } + finally { + if (null != inputStream) { + inputStream.close(); + } + } + return port; + + } + + private static File getConfigFromStartup() { + + String configLocation = System.getProperty("spring.config.location"); + if (StringUtils.isNotBlank(configLocation)) { + try { + File configFile = ResourceUtils.getFile(configLocation); + if (!configFile.isFile()) { + return null; + } + String fileName = configFile.getName(); + if (!(fileName.endsWith("yml") || fileName.endsWith("properties"))) { + return null; + } + return configFile; + } + catch (FileNotFoundException e) { + return null; + } + } + return null; - return SERVER_DEFAULT_PORT; } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/event/EventBusManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/event/EventBusManager.java index 3531c7b57..41750c1d8 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/event/EventBusManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/event/EventBusManager.java @@ -27,7 +27,7 @@ public class EventBusManager { private static class SingletonHolder { - private static EventBus INSTANCE = new GuavaEventBus("tc"); + private static EventBus INSTANCE = new GuavaEventBus("tc", true); } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/lock/AbstractLockManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/lock/AbstractLockManager.java index 002d7e61a..1a62ec2b4 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/lock/AbstractLockManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/lock/AbstractLockManager.java @@ -15,19 +15,21 @@ */ package io.seata.server.lock; -import java.util.ArrayList; -import java.util.List; - import io.seata.common.XID; import io.seata.common.util.CollectionUtils; import io.seata.common.util.StringUtils; import io.seata.core.exception.TransactionException; import io.seata.core.lock.Locker; import io.seata.core.lock.RowLock; +import io.seata.core.model.LockStatus; import io.seata.server.session.BranchSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + /** * The type Abstract lock manager. * @@ -42,6 +44,12 @@ public abstract class AbstractLockManager implements LockManager { @Override public boolean acquireLock(BranchSession branchSession) throws TransactionException { + return acquireLock(branchSession, true, false); + } + + @Override + public boolean acquireLock(BranchSession branchSession, boolean autoCommit, boolean skipCheckLock) + throws TransactionException { if (branchSession == null) { throw new IllegalArgumentException("branchSession can't be null for memory/file locker."); } @@ -56,7 +64,7 @@ public abstract class AbstractLockManager implements LockManager { // no lock return true; } - return getLocker(branchSession).acquireLock(locks); + return getLocker(branchSession).acquireLock(locks, autoCommit, skipCheckLock); } @Override @@ -110,23 +118,19 @@ public abstract class AbstractLockManager implements LockManager { */ protected abstract Locker getLocker(BranchSession branchSession); - /** - * Collect row locks list.` - * @param branchSession the branch session - * @return the list - */ - protected List collectRowLocks(BranchSession branchSession) { - List locks = new ArrayList<>(); + @Override + public List collectRowLocks(BranchSession branchSession) { if (branchSession == null || StringUtils.isBlank(branchSession.getLockKey())) { - return locks; + return Collections.emptyList(); } - String xid = branchSession.getXid(); - String resourceId = branchSession.getResourceId(); - long transactionId = branchSession.getTransactionId(); String lockKey = branchSession.getLockKey(); + String resourceId = branchSession.getResourceId(); + String xid = branchSession.getXid(); + long transactionId = branchSession.getTransactionId(); + long branchId = branchSession.getBranchId(); - return collectRowLocks(lockKey, resourceId, xid, transactionId, branchSession.getBranchId()); + return collectRowLocks(lockKey, resourceId, xid, transactionId, branchId); } /** @@ -151,7 +155,7 @@ public abstract class AbstractLockManager implements LockManager { */ protected List collectRowLocks(String lockKey, String resourceId, String xid, Long transactionId, Long branchID) { - List locks = new ArrayList(); + List locks = new ArrayList<>(); String[] tableGroupedLockKeys = lockKey.split(";"); for (String tableGroupedLockKey : tableGroupedLockKeys) { @@ -184,4 +188,9 @@ public abstract class AbstractLockManager implements LockManager { return locks; } + @Override + public void updateLockStatus(String xid, LockStatus lockStatus) { + this.getLocker().updateLockStatus(xid, lockStatus); + } + } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/lock/LockManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/lock/LockManager.java index 80669d829..2f63a5d6a 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/lock/LockManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/lock/LockManager.java @@ -16,9 +16,13 @@ package io.seata.server.lock; import io.seata.core.exception.TransactionException; +import io.seata.core.lock.RowLock; +import io.seata.core.model.LockStatus; import io.seata.server.session.BranchSession; import io.seata.server.session.GlobalSession; +import java.util.List; + /** * The interface Lock manager. * @@ -34,6 +38,17 @@ public interface LockManager { */ boolean acquireLock(BranchSession branchSession) throws TransactionException; + /** + * Acquire lock boolean. + * @param branchSession the branch session + * @param autoCommit the auto commit + * @param skipCheckLock whether skip check lock or not + * @return the boolean + * @throws TransactionException the transaction exception + */ + boolean acquireLock(BranchSession branchSession, boolean autoCommit, boolean skipCheckLock) + throws TransactionException; + /** * Un lock boolean. * @param branchSession the branch session @@ -66,4 +81,20 @@ public interface LockManager { */ void cleanAllLocks() throws TransactionException; + /** + * Collect row locks list.` + * @param branchSession the branch session + * @return the list + */ + List collectRowLocks(BranchSession branchSession); + + /** + * update lock status. + * @param xid the xid + * @param lockStatus the lock status + * @throws TransactionException the transaction exception + * + */ + void updateLockStatus(String xid, LockStatus lockStatus) throws TransactionException; + } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/lock/LockerManagerFactory.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/lock/LockerManagerFactory.java index b9e0e0b24..9751e4c76 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/lock/LockerManagerFactory.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/lock/LockerManagerFactory.java @@ -16,8 +16,10 @@ package io.seata.server.lock; import io.seata.common.loader.EnhancedServiceLoader; +import io.seata.config.Configuration; import io.seata.config.ConfigurationFactory; -import io.seata.core.constants.ConfigurationKeys; +import io.seata.server.store.StoreConfig; +import io.seata.server.store.StoreConfig.LockMode; /** * The type Lock manager factory. @@ -26,18 +28,42 @@ import io.seata.core.constants.ConfigurationKeys; */ public class LockerManagerFactory { + private static final Configuration CONFIG = ConfigurationFactory.getInstance(); + /** * the lock manager */ - private static final LockManager LOCK_MANAGER = EnhancedServiceLoader.load(LockManager.class, - ConfigurationFactory.getInstance().getConfig(ConfigurationKeys.STORE_MODE)); + private static volatile LockManager LOCK_MANAGER; /** * Get lock manager. * @return the lock manager */ public static LockManager getLockManager() { + if (LOCK_MANAGER == null) { + init(); + } return LOCK_MANAGER; } + public static void init() { + init(null); + } + + public static void init(LockMode lockMode) { + if (LOCK_MANAGER == null) { + synchronized (LockerManagerFactory.class) { + if (LOCK_MANAGER == null) { + if (null == lockMode) { + lockMode = StoreConfig.getLockMode(); + } + // 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/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/lock/distributed/DistributedLockerFactory.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/lock/distributed/DistributedLockerFactory.java new file mode 100644 index 000000000..367a9e3b8 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/lock/distributed/DistributedLockerFactory.java @@ -0,0 +1,71 @@ +/* + * 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.lock.distributed; + +import io.seata.common.loader.EnhancedServiceLoader; +import io.seata.common.loader.EnhancedServiceNotFoundException; +import io.seata.core.store.DefaultDistributedLocker; +import io.seata.core.store.DistributedLocker; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Distributed locker factory + * + * @author zhongxiang.wang + */ +public class DistributedLockerFactory { + + /** + * The constant LOGGER. + */ + private static final Logger LOGGER = LoggerFactory.getLogger(DistributedLockerFactory.class); + + private static volatile DistributedLocker DISTRIBUTED_LOCKER = null; + + /** + * Get the distributed locker by lockerType + * @param lockerType the locker type + * @return the distributed locker + */ + public static DistributedLocker getDistributedLocker(String lockerType) { + if (DISTRIBUTED_LOCKER == null) { + synchronized (DistributedLocker.class) { + if (DISTRIBUTED_LOCKER == null) { + DistributedLocker distributedLocker = null; + try { + if (!"file".equals(lockerType)) { + distributedLocker = EnhancedServiceLoader.load(DistributedLocker.class, lockerType); + } + } + catch (EnhancedServiceNotFoundException ex) { + LOGGER.error("Get distributed locker failed: {}", ex.getMessage(), ex); + } + if (distributedLocker == null) { + distributedLocker = new DefaultDistributedLocker(); + } + DISTRIBUTED_LOCKER = distributedLocker; + } + } + } + return DISTRIBUTED_LOCKER; + } + + public static void cleanLocker() { + DISTRIBUTED_LOCKER = null; + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/listener/SystemPropertyLoggerContextListener.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/listener/SystemPropertyLoggerContextListener.java index 4ed809cc6..62770cf29 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/listener/SystemPropertyLoggerContextListener.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/listener/SystemPropertyLoggerContextListener.java @@ -38,7 +38,7 @@ public class SystemPropertyLoggerContextListener extends ContextAwareBase implem } Context context = getContext(); - context.putProperty("PORT", System.getProperty(ConfigurationKeys.SERVER_PORT)); + context.putProperty("RPC_PORT", System.getProperty(ConfigurationKeys.SERVER_SERVICE_PORT_CAMEL)); started = true; } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ColorConverter.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ColorConverter.java deleted file mode 100644 index ddaf2ff82..000000000 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ColorConverter.java +++ /dev/null @@ -1,93 +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.server.logging.logback; - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.pattern.CompositeConverter; -import io.netty.util.internal.PlatformDependent; -import io.seata.server.logging.logback.ansi.AnsiColor; -import io.seata.server.logging.logback.ansi.AnsiElement; -import io.seata.server.logging.logback.ansi.AnsiOutput; -import io.seata.server.logging.logback.ansi.AnsiStyle; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -/** - * Logback {@link CompositeConverter} colors output using the {@link AnsiOutput} class. A - * single 'color' option can be provided to the converter, or if not specified color will - * be picked based on the logging level. - * - * @author Phillip Webb - * @origin Copied from spring-boot.jar by wang.liang - */ -public class ColorConverter extends CompositeConverter { - - private static final Map ELEMENTS; - - private static final String DISABLE_PROPERTY_NAME = "logback.color.disable-for-bat"; - - static { - Map ansiElements = new HashMap<>(); - ansiElements.put("faint", AnsiStyle.FAINT); - ansiElements.put("red", AnsiColor.RED); - ansiElements.put("green", AnsiColor.GREEN); - ansiElements.put("yellow", AnsiColor.YELLOW); - ansiElements.put("blue", AnsiColor.BLUE); - ansiElements.put("magenta", AnsiColor.MAGENTA); - ansiElements.put("cyan", AnsiColor.CYAN); - ELEMENTS = Collections.unmodifiableMap(ansiElements); - } - - private static final Map LEVELS; - - static { - Map ansiLevels = new HashMap<>(); - ansiLevels.put(Level.ERROR_INTEGER, AnsiColor.RED); - ansiLevels.put(Level.WARN_INTEGER, AnsiColor.YELLOW); - LEVELS = Collections.unmodifiableMap(ansiLevels); - } - - private final boolean disable; - - public ColorConverter() { - // If is windows and run by seata-server.bat, then disable the color log. - disable = PlatformDependent.isWindows() && Boolean.parseBoolean(System.getProperty(DISABLE_PROPERTY_NAME)); - } - - @Override - protected String transform(ILoggingEvent event, String in) { - if (disable) { - // return the original log - return in; - } - - AnsiElement element = ELEMENTS.get(getFirstOption()); - if (element == null) { - // Assume highlighting - element = LEVELS.get(event.getLevel().toInteger()); - element = (element != null) ? element : AnsiColor.GREEN; - } - return toAnsiString(in, element); - } - - protected String toAnsiString(String in, AnsiElement element) { - return AnsiOutput.toString(element, in); - } - -} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ExtendedWhitespaceThrowableProxyConverter.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ExtendedWhitespaceThrowableProxyConverter.java index a2a0ca489..9c79cea10 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ExtendedWhitespaceThrowableProxyConverter.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ExtendedWhitespaceThrowableProxyConverter.java @@ -21,10 +21,9 @@ import ch.qos.logback.core.CoreConstants; /** * {@link ExtendedThrowableProxyConverter} that adds some additional whitespace around the - * stack trace. + * stack trace. Copied from spring-boot-xxx.jar by wang.liang * * @author Phillip Webb - * @origin Copied from spring-boot.jar by wang.liang */ public class ExtendedWhitespaceThrowableProxyConverter extends ExtendedThrowableProxyConverter { diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/WhitespaceThrowableProxyConverter.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/WhitespaceThrowableProxyConverter.java deleted file mode 100644 index 97aa6eeb4..000000000 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/WhitespaceThrowableProxyConverter.java +++ /dev/null @@ -1,37 +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.server.logging.logback; - -import ch.qos.logback.classic.pattern.ThrowableProxyConverter; -import ch.qos.logback.classic.spi.IThrowableProxy; -import ch.qos.logback.core.CoreConstants; - -/** - * {@link ThrowableProxyConverter} that adds some additional whitespace around the - * stacktrace. - * - * @author Phillip Webb - * @origin Copied from spring-boot.jar by wang.liang - */ -public class WhitespaceThrowableProxyConverter extends ThrowableProxyConverter { - - @Override - protected String throwableProxyToString(IThrowableProxy tp) { - return CoreConstants.LINE_SEPARATOR + super.throwableProxyToString(tp) + CoreConstants.LINE_SEPARATOR - + CoreConstants.LINE_SEPARATOR; - } - -} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ansi/AnsiColor.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ansi/AnsiColor.java deleted file mode 100644 index 381c6111f..000000000 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ansi/AnsiColor.java +++ /dev/null @@ -1,126 +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.server.logging.logback.ansi; - -/** - * {@link AnsiElement Ansi} colors. - * - * @author Phillip Webb - * @author Geoffrey Chandler - * @origin Copied from spring-boot.jar by wang.liang - */ -public enum AnsiColor implements AnsiElement { - - /** - * default is light-grey - */ - DEFAULT("39"), - - /** - * black - */ - BLACK("30"), - - /** - * red - */ - RED("31"), - - /** - * green - */ - GREEN("32"), - - /** - * yellow - */ - YELLOW("33"), - - /** - * blue - */ - BLUE("34"), - - /** - * magenta - */ - MAGENTA("35"), - - /** - * cyan - */ - CYAN("36"), - - /** - * white - */ - WHITE("37"), - - /** - * bright black - */ - BRIGHT_BLACK("90"), - - /** - * bright red - */ - BRIGHT_RED("91"), - - /** - * bright green - */ - BRIGHT_GREEN("92"), - - /** - * bright yellow - */ - BRIGHT_YELLOW("93"), - - /** - * bright blue - */ - BRIGHT_BLUE("94"), - - /** - * bright magenta - */ - BRIGHT_MAGENTA("95"), - - /** - * bright cyan - */ - BRIGHT_CYAN("96"), - - /** - * bright white - */ - BRIGHT_WHITE("97"); - - /** - * code of color - */ - private final String code; - - AnsiColor(String code) { - this.code = code; - } - - @Override - public String toString() { - return this.code; - } - -} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ansi/AnsiOutput.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ansi/AnsiOutput.java deleted file mode 100644 index c4b9303ba..000000000 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ansi/AnsiOutput.java +++ /dev/null @@ -1,184 +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.server.logging.logback.ansi; - -import com.alipay.lookout.common.Assert; - -import java.util.Locale; - -/** - * Generates ANSI encoded output, automatically attempting to detect if the terminal - * supports ANSI. - * - * @author Phillip Webb - * @origin Copied from spring-boot.jar by wang.liang - */ -public class AnsiOutput { - - private static final String ENCODE_JOIN = ";"; - - private static Enabled enabled = Enabled.ALWAYS; - - private static Boolean consoleAvailable; - - private static Boolean ansiCapable; - - private static final String OPERATING_SYSTEM_NAME = System.getProperty("os.name").toLowerCase(Locale.ENGLISH); - - private static final String ENCODE_START = "\033["; - - private static final String ENCODE_END = "m"; - - private static final String RESET = "0;" + AnsiColor.DEFAULT; - - /** - * Sets if ANSI output is enabled. - * @param enabled if ANSI is enabled, disabled or detected - */ - public static void setEnabled(Enabled enabled) { - Assert.notNull(enabled, "Enabled must not be null"); - AnsiOutput.enabled = enabled; - } - - /** - * Sets if the System.console() is known to be available. - * @param consoleAvailable if the console is known to be available or {@code null} to - * use standard detection logic. - */ - public static void setConsoleAvailable(Boolean consoleAvailable) { - AnsiOutput.consoleAvailable = consoleAvailable; - } - - static Enabled getEnabled() { - return AnsiOutput.enabled; - } - - /** - * Encode a single {@link AnsiElement} if output is enabled. - * @param element the element to encode - * @return the encoded element or an empty string - */ - public static String encode(AnsiElement element) { - if (isEnabled()) { - return ENCODE_START + element + ENCODE_END; - } - return ""; - } - - /** - * Create a new ANSI string from the specified elements. Any {@link AnsiElement}s will - * be encoded as required. - * @param elements the elements to encode - * @return a string of the encoded elements - */ - public static String toString(Object... elements) { - StringBuilder sb = new StringBuilder(); - if (isEnabled()) { - buildEnabled(sb, elements); - } - else { - buildDisabled(sb, elements); - } - return sb.toString(); - } - - private static void buildEnabled(StringBuilder sb, Object[] elements) { - boolean writingAnsi = false; - boolean containsEncoding = false; - for (Object element : elements) { - if (element instanceof AnsiElement) { - containsEncoding = true; - if (!writingAnsi) { - sb.append(ENCODE_START); - writingAnsi = true; - } - else { - sb.append(ENCODE_JOIN); - } - } - else { - if (writingAnsi) { - sb.append(ENCODE_END); - writingAnsi = false; - } - } - sb.append(element); - } - if (containsEncoding) { - sb.append(writingAnsi ? ENCODE_JOIN : ENCODE_START); - sb.append(RESET); - sb.append(ENCODE_END); - } - } - - private static void buildDisabled(StringBuilder sb, Object[] elements) { - for (Object element : elements) { - if (!(element instanceof AnsiElement) && element != null) { - sb.append(element); - } - } - } - - private static boolean isEnabled() { - if (enabled == Enabled.DETECT) { - if (ansiCapable == null) { - ansiCapable = detectIfAnsiCapable(); - } - return ansiCapable; - } - return enabled == Enabled.ALWAYS; - } - - private static boolean detectIfAnsiCapable() { - try { - if (Boolean.FALSE.equals(consoleAvailable)) { - return false; - } - if ((consoleAvailable == null) && (System.console() == null)) { - return false; - } - return !(OPERATING_SYSTEM_NAME.indexOf("win") >= 0); - } - catch (Throwable ex) { - return false; - } - } - - /** - * Possible values to pass to {@link AnsiOutput#setEnabled}. Determines when to output - * ANSI escape sequences for coloring application output. - */ - public enum Enabled { - - /** - * Try to detect whether ANSI coloring capabilities are available. The default - * value for {@link AnsiOutput}. - */ - DETECT, - - /** - * Enable ANSI-colored output. - */ - ALWAYS, - - /** - * Disable ANSI-colored output. - */ - NEVER - - } - -} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/appender/EnhancedLogstashEncoder.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/appender/EnhancedLogstashEncoder.java index 74ccff27e..6c5e3fe8f 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/appender/EnhancedLogstashEncoder.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/appender/EnhancedLogstashEncoder.java @@ -15,12 +15,12 @@ */ package io.seata.server.logging.logback.appender; -import java.util.ArrayList; - import net.logstash.logback.composite.JsonProvider; import net.logstash.logback.composite.JsonProviders; import net.logstash.logback.encoder.LogstashEncoder; +import java.util.ArrayList; + /** * The type Enhanced logstash encoder * diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/metrics/MeterIdConstants.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/metrics/MeterIdConstants.java index 8bdb5d0dd..3ac1660e9 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/metrics/MeterIdConstants.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/metrics/MeterIdConstants.java @@ -15,8 +15,8 @@ */ package io.seata.server.metrics; -import io.seata.metrics.IdConstants; import io.seata.metrics.Id; +import io.seata.metrics.IdConstants; /** * Constants for meter id in tc @@ -39,6 +39,16 @@ public interface MeterIdConstants { .withTag(IdConstants.METER_KEY, IdConstants.METER_VALUE_COUNTER) .withTag(IdConstants.STATUS_KEY, IdConstants.STATUS_VALUE_ROLLBACKED); + Id COUNTER_AFTER_ROLLBACKED = new Id(IdConstants.SEATA_TRANSACTION) + .withTag(IdConstants.ROLE_KEY, IdConstants.ROLE_VALUE_TC) + .withTag(IdConstants.METER_KEY, IdConstants.METER_VALUE_COUNTER) + .withTag(IdConstants.STATUS_KEY, IdConstants.STATUS_VALUE_AFTER_ROLLBACKED_KEY); + + Id COUNTER_AFTER_COMMITTED = new Id(IdConstants.SEATA_TRANSACTION) + .withTag(IdConstants.ROLE_KEY, IdConstants.ROLE_VALUE_TC) + .withTag(IdConstants.METER_KEY, IdConstants.METER_VALUE_COUNTER) + .withTag(IdConstants.STATUS_KEY, IdConstants.STATUS_VALUE_AFTER_COMMITTED_KEY); + Id SUMMARY_COMMITTED = new Id(IdConstants.SEATA_TRANSACTION) .withTag(IdConstants.ROLE_KEY, IdConstants.ROLE_VALUE_TC) .withTag(IdConstants.METER_KEY, IdConstants.METER_VALUE_SUMMARY) @@ -49,6 +59,25 @@ public interface MeterIdConstants { .withTag(IdConstants.METER_KEY, IdConstants.METER_VALUE_SUMMARY) .withTag(IdConstants.STATUS_KEY, IdConstants.STATUS_VALUE_ROLLBACKED); + Id SUMMARY_FAILED = new Id(IdConstants.SEATA_TRANSACTION).withTag(IdConstants.ROLE_KEY, IdConstants.ROLE_VALUE_TC) + .withTag(IdConstants.METER_KEY, IdConstants.METER_VALUE_SUMMARY) + .withTag(IdConstants.STATUS_KEY, IdConstants.STATUS_VALUE_FAILED); + + Id SUMMARY_TWO_PHASE_TIMEOUT = new Id(IdConstants.SEATA_TRANSACTION) + .withTag(IdConstants.ROLE_KEY, IdConstants.ROLE_VALUE_TC) + .withTag(IdConstants.METER_KEY, IdConstants.METER_VALUE_SUMMARY) + .withTag(IdConstants.STATUS_KEY, IdConstants.STATUS_VALUE_TWO_PHASE_TIMEOUT); + + Id SUMMARY_AFTER_ROLLBACKED = new Id(IdConstants.SEATA_TRANSACTION) + .withTag(IdConstants.ROLE_KEY, IdConstants.ROLE_VALUE_TC) + .withTag(IdConstants.METER_KEY, IdConstants.METER_VALUE_SUMMARY) + .withTag(IdConstants.STATUS_KEY, IdConstants.STATUS_VALUE_AFTER_ROLLBACKED_KEY); + + Id SUMMARY_AFTER_COMMITTED = new Id(IdConstants.SEATA_TRANSACTION) + .withTag(IdConstants.ROLE_KEY, IdConstants.ROLE_VALUE_TC) + .withTag(IdConstants.METER_KEY, IdConstants.METER_VALUE_SUMMARY) + .withTag(IdConstants.STATUS_KEY, IdConstants.STATUS_VALUE_AFTER_COMMITTED_KEY); + Id TIMER_COMMITTED = new Id(IdConstants.SEATA_TRANSACTION).withTag(IdConstants.ROLE_KEY, IdConstants.ROLE_VALUE_TC) .withTag(IdConstants.METER_KEY, IdConstants.METER_VALUE_TIMER) .withTag(IdConstants.STATUS_KEY, IdConstants.STATUS_VALUE_COMMITTED); @@ -57,4 +86,18 @@ public interface MeterIdConstants { .withTag(IdConstants.METER_KEY, IdConstants.METER_VALUE_TIMER) .withTag(IdConstants.STATUS_KEY, IdConstants.STATUS_VALUE_ROLLBACKED); + Id TIMER_FAILED = new Id(IdConstants.SEATA_TRANSACTION).withTag(IdConstants.ROLE_KEY, IdConstants.ROLE_VALUE_TC) + .withTag(IdConstants.METER_KEY, IdConstants.METER_VALUE_TIMER) + .withTag(IdConstants.STATUS_KEY, IdConstants.STATUS_VALUE_FAILED); + + Id TIMER_AFTER_ROLLBACKED = new Id(IdConstants.SEATA_TRANSACTION) + .withTag(IdConstants.ROLE_KEY, IdConstants.ROLE_VALUE_TC) + .withTag(IdConstants.METER_KEY, IdConstants.METER_VALUE_TIMER) + .withTag(IdConstants.STATUS_KEY, IdConstants.STATUS_VALUE_AFTER_ROLLBACKED_KEY); + + Id TIMER_AFTER_COMMITTED = new Id(IdConstants.SEATA_TRANSACTION) + .withTag(IdConstants.ROLE_KEY, IdConstants.ROLE_VALUE_TC) + .withTag(IdConstants.METER_KEY, IdConstants.METER_VALUE_TIMER) + .withTag(IdConstants.STATUS_KEY, IdConstants.STATUS_VALUE_AFTER_COMMITTED_KEY); + } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/metrics/MetricsManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/metrics/MetricsManager.java index 4b30f2b37..ed9044384 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/metrics/MetricsManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/metrics/MetricsManager.java @@ -15,8 +15,6 @@ */ package io.seata.server.metrics; -import java.util.List; - import io.seata.config.ConfigurationFactory; import io.seata.core.constants.ConfigurationKeys; import io.seata.metrics.exporter.Exporter; @@ -25,6 +23,10 @@ import io.seata.metrics.registry.Registry; import io.seata.metrics.registry.RegistryFactory; import io.seata.server.event.EventBusManager; +import java.util.List; + +import static io.seata.common.DefaultValues.DEFAULT_METRICS_ENABLED; + /** * Metrics manager for init * @@ -39,7 +41,7 @@ public class MetricsManager { } public static final MetricsManager get() { - return MetricsManager.SingletonHolder.INSTANCE; + return SingletonHolder.INSTANCE; } private Registry registry; @@ -49,8 +51,8 @@ public class MetricsManager { } public void init() { - boolean enabled = ConfigurationFactory.getInstance() - .getBoolean(ConfigurationKeys.METRICS_PREFIX + ConfigurationKeys.METRICS_ENABLED, false); + boolean enabled = ConfigurationFactory.getInstance().getBoolean( + ConfigurationKeys.METRICS_PREFIX + ConfigurationKeys.METRICS_ENABLED, DEFAULT_METRICS_ENABLED); if (enabled) { registry = RegistryFactory.getInstance(); if (registry != null) { diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/metrics/MetricsPublisher.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/metrics/MetricsPublisher.java new file mode 100644 index 000000000..95700d49e --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/metrics/MetricsPublisher.java @@ -0,0 +1,96 @@ +/* + * 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.metrics; + +import io.seata.core.event.EventBus; +import io.seata.core.event.GlobalTransactionEvent; +import io.seata.core.model.GlobalStatus; +import io.seata.server.event.EventBusManager; +import io.seata.server.session.GlobalSession; + +/** + * The type Metrics publisher. + * + * @author slievrly + */ +public class MetricsPublisher { + + private static final EventBus EVENT_BUS = EventBusManager.get(); + + /** + * post end event + * @param globalSession the global session + * @param retryGlobal the retry global + * @param retryBranch the retry branch + */ + public static void postSessionDoneEvent(final GlobalSession globalSession, boolean retryGlobal, + boolean retryBranch) { + postSessionDoneEvent(globalSession, globalSession.getStatus(), retryGlobal, retryBranch); + } + + /** + * post end event (force specified state) + * @param globalSession the global session + * @param status the global status + * @param retryGlobal the retry global + * @param retryBranch the retry branch + */ + public static void postSessionDoneEvent(final GlobalSession globalSession, GlobalStatus status, boolean retryGlobal, + boolean retryBranch) { + postSessionDoneEvent(globalSession, status.name(), retryGlobal, globalSession.getBeginTime(), retryBranch); + } + + /** + * Post session done event. + * @param globalSession the global session + * @param status the status + * @param retryGlobal the retry global + * @param beginTime the begin time + * @param retryBranch the retry branch + */ + public static void postSessionDoneEvent(final GlobalSession globalSession, String status, boolean retryGlobal, + long beginTime, boolean retryBranch) { + EVENT_BUS.post(new GlobalTransactionEvent(globalSession.getTransactionId(), GlobalTransactionEvent.ROLE_TC, + globalSession.getTransactionName(), globalSession.getApplicationId(), + globalSession.getTransactionServiceGroup(), beginTime, System.currentTimeMillis(), status, retryGlobal, + retryBranch)); + } + + /** + * Post session doing event. + * @param globalSession the global session + * @param retryGlobal the retry global + */ + public static void postSessionDoingEvent(final GlobalSession globalSession, boolean retryGlobal) { + postSessionDoingEvent(globalSession, globalSession.getStatus().name(), retryGlobal, false); + } + + /** + * Post session doing event. + * @param globalSession the global session + * @param status the status + * @param retryGlobal the retry global + * @param retryBranch the retry branch + */ + public static void postSessionDoingEvent(final GlobalSession globalSession, String status, boolean retryGlobal, + boolean retryBranch) { + EVENT_BUS.post(new GlobalTransactionEvent(globalSession.getTransactionId(), GlobalTransactionEvent.ROLE_TC, + globalSession.getTransactionName(), globalSession.getApplicationId(), + globalSession.getTransactionServiceGroup(), globalSession.getBeginTime(), null, status, retryGlobal, + retryBranch)); + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/metrics/MetricsSubscriber.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/metrics/MetricsSubscriber.java index 9776edb5e..5993ca177 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/metrics/MetricsSubscriber.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/metrics/MetricsSubscriber.java @@ -19,14 +19,16 @@ import com.google.common.eventbus.Subscribe; import io.seata.core.event.GlobalTransactionEvent; import io.seata.core.model.GlobalStatus; import io.seata.metrics.registry.Registry; +import io.seata.server.event.EventBusManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; -import static io.seata.metrics.IdConstants.APP_ID_KEY; -import static io.seata.metrics.IdConstants.GROUP_KEY; +import static io.seata.metrics.IdConstants.*; /** * Event subscriber for metrics @@ -35,31 +37,47 @@ import static io.seata.metrics.IdConstants.GROUP_KEY; */ public class MetricsSubscriber { + private static final Logger LOGGER = LoggerFactory.getLogger(MetricsSubscriber.class); + private final Registry registry; - private final Map> consumers; + private final Map> consumers; public MetricsSubscriber(Registry registry) { this.registry = registry; consumers = new HashMap<>(); - consumers.put(GlobalStatus.Begin, this::processGlobalStatusBegin); - consumers.put(GlobalStatus.Committed, this::processGlobalStatusCommitted); - consumers.put(GlobalStatus.Rollbacked, this::processGlobalStatusRollbacked); + consumers.put(GlobalStatus.Begin.name(), this::processGlobalStatusBegin); + consumers.put(GlobalStatus.Committed.name(), this::processGlobalStatusCommitted); + consumers.put(GlobalStatus.Rollbacked.name(), this::processGlobalStatusRollbacked); - consumers.put(GlobalStatus.CommitFailed, this::processGlobalStatusCommitFailed); - consumers.put(GlobalStatus.RollbackFailed, this::processGlobalStatusRollbackFailed); - consumers.put(GlobalStatus.TimeoutRollbacked, this::processGlobalStatusTimeoutRollbacked); - consumers.put(GlobalStatus.TimeoutRollbackFailed, this::processGlobalStatusTimeoutRollbackFailed); + consumers.put(GlobalStatus.CommitFailed.name(), this::processGlobalStatusCommitFailed); + consumers.put(GlobalStatus.RollbackFailed.name(), this::processGlobalStatusRollbackFailed); + consumers.put(GlobalStatus.TimeoutRollbacked.name(), this::processGlobalStatusTimeoutRollbacked); + consumers.put(GlobalStatus.TimeoutRollbackFailed.name(), this::processGlobalStatusTimeoutRollbackFailed); + + consumers.put(GlobalStatus.CommitRetryTimeout.name(), this::processGlobalStatusCommitRetryTimeout); + consumers.put(GlobalStatus.RollbackRetryTimeout.name(), this::processGlobalStatusTimeoutRollbackRetryTimeout); + + consumers.put(STATUS_VALUE_AFTER_COMMITTED_KEY, this::processAfterGlobalCommitted); + consumers.put(STATUS_VALUE_AFTER_ROLLBACKED_KEY, this::processAfterGlobalRollbacked); } private void processGlobalStatusBegin(GlobalTransactionEvent event) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("accept new event,xid:{},event:{}", event.getId(), event); + for (Object object : EventBusManager.get().getSubscribers()) { + LOGGER.debug("subscribe:{},threadName:{}", object.toString(), Thread.currentThread().getName()); + } + } registry.getCounter(MeterIdConstants.COUNTER_ACTIVE.withTag(APP_ID_KEY, event.getApplicationId()) .withTag(GROUP_KEY, event.getGroup())).increase(1); } private void processGlobalStatusCommitted(GlobalTransactionEvent event) { - registry.getCounter(MeterIdConstants.COUNTER_ACTIVE.withTag(APP_ID_KEY, event.getApplicationId()) - .withTag(GROUP_KEY, event.getGroup())).decrease(1); + if (event.isRetryGlobal()) { + return; + } + decreaseActive(event); registry.getCounter(MeterIdConstants.COUNTER_COMMITTED.withTag(APP_ID_KEY, event.getApplicationId()) .withTag(GROUP_KEY, event.getGroup())).increase(1); registry.getSummary(MeterIdConstants.SUMMARY_COMMITTED.withTag(APP_ID_KEY, event.getApplicationId()) @@ -70,8 +88,10 @@ public class MetricsSubscriber { } private void processGlobalStatusRollbacked(GlobalTransactionEvent event) { - registry.getCounter(MeterIdConstants.COUNTER_ACTIVE.withTag(APP_ID_KEY, event.getApplicationId()) - .withTag(GROUP_KEY, event.getGroup())).decrease(1); + if (event.isRetryGlobal()) { + return; + } + decreaseActive(event); registry.getCounter(MeterIdConstants.COUNTER_ROLLBACKED.withTag(APP_ID_KEY, event.getApplicationId()) .withTag(GROUP_KEY, event.getGroup())).increase(1); registry.getSummary(MeterIdConstants.SUMMARY_ROLLBACKED.withTag(APP_ID_KEY, event.getApplicationId()) @@ -81,24 +101,75 @@ public class MetricsSubscriber { .record(event.getEndTime() - event.getBeginTime(), TimeUnit.MILLISECONDS); } + private void processAfterGlobalRollbacked(GlobalTransactionEvent event) { + if (event.isRetryGlobal() && event.isRetryBranch()) { + decreaseActive(event); + } + registry.getCounter(MeterIdConstants.COUNTER_AFTER_ROLLBACKED.withTag(APP_ID_KEY, event.getApplicationId()) + .withTag(GROUP_KEY, event.getGroup())).increase(1); + registry.getSummary(MeterIdConstants.SUMMARY_AFTER_ROLLBACKED.withTag(APP_ID_KEY, event.getApplicationId()) + .withTag(GROUP_KEY, event.getGroup())).increase(1); + registry.getTimer(MeterIdConstants.TIMER_AFTER_ROLLBACKED.withTag(APP_ID_KEY, event.getApplicationId()) + .withTag(GROUP_KEY, event.getGroup())) + .record(event.getEndTime() - event.getBeginTime(), TimeUnit.MILLISECONDS); + } + + private void processAfterGlobalCommitted(GlobalTransactionEvent event) { + if (event.isRetryGlobal() && event.isRetryBranch()) { + decreaseActive(event); + } + registry.getCounter(MeterIdConstants.COUNTER_AFTER_COMMITTED.withTag(APP_ID_KEY, event.getApplicationId()) + .withTag(GROUP_KEY, event.getGroup())).increase(1); + registry.getSummary(MeterIdConstants.SUMMARY_AFTER_COMMITTED.withTag(APP_ID_KEY, event.getApplicationId()) + .withTag(GROUP_KEY, event.getGroup())).increase(1); + registry.getTimer(MeterIdConstants.TIMER_AFTER_COMMITTED.withTag(APP_ID_KEY, event.getApplicationId()) + .withTag(GROUP_KEY, event.getGroup())) + .record(event.getEndTime() - event.getBeginTime(), TimeUnit.MILLISECONDS); + } + private void processGlobalStatusCommitFailed(GlobalTransactionEvent event) { - registry.getCounter(MeterIdConstants.COUNTER_ACTIVE.withTag(APP_ID_KEY, event.getApplicationId()) - .withTag(GROUP_KEY, event.getGroup())).decrease(1); + decreaseActive(event); + reportFailed(event); } private void processGlobalStatusRollbackFailed(GlobalTransactionEvent event) { - registry.getCounter(MeterIdConstants.COUNTER_ACTIVE.withTag(APP_ID_KEY, event.getApplicationId()) - .withTag(GROUP_KEY, event.getGroup())).decrease(1); + decreaseActive(event); + reportFailed(event); } private void processGlobalStatusTimeoutRollbacked(GlobalTransactionEvent event) { + decreaseActive(event); + } + + private void processGlobalStatusTimeoutRollbackFailed(GlobalTransactionEvent event) { + decreaseActive(event); + reportTwoPhaseTimeout(event); + } + + private void processGlobalStatusCommitRetryTimeout(GlobalTransactionEvent event) { + decreaseActive(event); + reportTwoPhaseTimeout(event); + } + + private void processGlobalStatusTimeoutRollbackRetryTimeout(GlobalTransactionEvent event) { + decreaseActive(event); + } + + private void decreaseActive(GlobalTransactionEvent event) { registry.getCounter(MeterIdConstants.COUNTER_ACTIVE.withTag(APP_ID_KEY, event.getApplicationId()) .withTag(GROUP_KEY, event.getGroup())).decrease(1); } - private void processGlobalStatusTimeoutRollbackFailed(GlobalTransactionEvent event) { - registry.getCounter(MeterIdConstants.COUNTER_ACTIVE.withTag(APP_ID_KEY, event.getApplicationId()) - .withTag(GROUP_KEY, event.getGroup())).decrease(1); + private void reportFailed(GlobalTransactionEvent event) { + registry.getSummary(MeterIdConstants.SUMMARY_FAILED.withTag(APP_ID_KEY, event.getApplicationId()) + .withTag(GROUP_KEY, event.getGroup())).increase(1); + registry.getTimer(MeterIdConstants.TIMER_FAILED.withTag(APP_ID_KEY, event.getApplicationId()).withTag(GROUP_KEY, + event.getGroup())).record(event.getEndTime() - event.getBeginTime(), TimeUnit.MILLISECONDS); + } + + private void reportTwoPhaseTimeout(GlobalTransactionEvent event) { + registry.getSummary(MeterIdConstants.SUMMARY_TWO_PHASE_TIMEOUT.withTag(APP_ID_KEY, event.getApplicationId()) + .withTag(GROUP_KEY, event.getGroup())).increase(1); } @Subscribe @@ -108,4 +179,18 @@ public class MetricsSubscriber { } } + @Override + public boolean equals(Object obj) { + return this.getClass().getName().equals(obj.getClass().getName()); + } + + /** + * PMD check SuppressWarnings("checkstyle:EqualsHashCode") + * @return the hash code + */ + @Override + public int hashCode() { + return super.hashCode(); + } + } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/AbstractSessionManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/AbstractSessionManager.java index a719da4db..fbb26451a 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/AbstractSessionManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/AbstractSessionManager.java @@ -21,6 +21,7 @@ import io.seata.core.exception.TransactionException; import io.seata.core.exception.TransactionExceptionCode; import io.seata.core.model.BranchStatus; import io.seata.core.model.GlobalStatus; +import io.seata.core.model.LockStatus; import io.seata.server.store.SessionStorable; import io.seata.server.store.TransactionStoreManager; import io.seata.server.store.TransactionStoreManager.LogOperation; @@ -64,7 +65,7 @@ public abstract class AbstractSessionManager implements SessionManager, SessionL @Override public void addGlobalSession(GlobalSession session) throws TransactionException { if (LOGGER.isDebugEnabled()) { - LOGGER.debug("MANAGER[" + name + "] SESSION[" + session + "] " + LogOperation.GLOBAL_ADD); + LOGGER.debug("MANAGER[{}] SESSION[{}] {}", name, session, LogOperation.GLOBAL_ADD); } writeSession(LogOperation.GLOBAL_ADD, session); } @@ -72,7 +73,10 @@ public abstract class AbstractSessionManager implements SessionManager, SessionL @Override public void updateGlobalSessionStatus(GlobalSession session, GlobalStatus status) throws TransactionException { if (LOGGER.isDebugEnabled()) { - LOGGER.debug("MANAGER[" + name + "] SESSION[" + session + "] " + LogOperation.GLOBAL_UPDATE); + LOGGER.debug("MANAGER[{}] SESSION[{}] {}", name, session, LogOperation.GLOBAL_UPDATE); + } + if (GlobalStatus.Rollbacking == status) { + session.getBranchSessions().forEach(i -> i.setLockStatus(LockStatus.Rollbacking)); } writeSession(LogOperation.GLOBAL_UPDATE, session); } @@ -80,7 +84,7 @@ public abstract class AbstractSessionManager implements SessionManager, SessionL @Override public void removeGlobalSession(GlobalSession session) throws TransactionException { if (LOGGER.isDebugEnabled()) { - LOGGER.debug("MANAGER[" + name + "] SESSION[" + session + "] " + LogOperation.GLOBAL_REMOVE); + LOGGER.debug("MANAGER[{}] SESSION[{}] {}", name, session, LogOperation.GLOBAL_REMOVE); } writeSession(LogOperation.GLOBAL_REMOVE, session); } @@ -88,7 +92,7 @@ public abstract class AbstractSessionManager implements SessionManager, SessionL @Override public void addBranchSession(GlobalSession session, BranchSession branchSession) throws TransactionException { if (LOGGER.isDebugEnabled()) { - LOGGER.debug("MANAGER[" + name + "] SESSION[" + branchSession + "] " + LogOperation.BRANCH_ADD); + LOGGER.debug("MANAGER[{}] SESSION[{}] {}", name, branchSession, LogOperation.BRANCH_ADD); } writeSession(LogOperation.BRANCH_ADD, branchSession); } @@ -97,7 +101,7 @@ public abstract class AbstractSessionManager implements SessionManager, SessionL public void updateBranchSessionStatus(BranchSession branchSession, BranchStatus status) throws TransactionException { if (LOGGER.isDebugEnabled()) { - LOGGER.debug("MANAGER[" + name + "] SESSION[" + branchSession + "] " + LogOperation.BRANCH_UPDATE); + LOGGER.debug("MANAGER[{}] SESSION[{}] {}", name, branchSession, LogOperation.BRANCH_UPDATE); } writeSession(LogOperation.BRANCH_UPDATE, branchSession); } @@ -106,7 +110,7 @@ public abstract class AbstractSessionManager implements SessionManager, SessionL public void removeBranchSession(GlobalSession globalSession, BranchSession branchSession) throws TransactionException { if (LOGGER.isDebugEnabled()) { - LOGGER.debug("MANAGER[" + name + "] SESSION[" + branchSession + "] " + LogOperation.BRANCH_REMOVE); + LOGGER.debug("MANAGER[{}] SESSION[{}] {}", name, branchSession, LogOperation.BRANCH_REMOVE); } writeSession(LogOperation.BRANCH_REMOVE, branchSession); } @@ -143,10 +147,15 @@ public abstract class AbstractSessionManager implements SessionManager, SessionL } @Override - public void onEnd(GlobalSession globalSession) throws TransactionException { + public void onSuccessEnd(GlobalSession globalSession) throws TransactionException { removeGlobalSession(globalSession); } + @Override + public void onFailEnd(GlobalSession globalSession) throws TransactionException { + LOGGER.info("xid:{} fail end, transaction:{}", globalSession.getXid(), globalSession.toString()); + } + private void writeSession(LogOperation logOperation, SessionStorable sessionStorable) throws TransactionException { if (!transactionStoreManager.writeSession(logOperation, sessionStorable)) { if (LogOperation.GLOBAL_ADD.equals(logOperation)) { diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/BranchSession.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/BranchSession.java index 0b0af3228..09ad192a4 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/BranchSession.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/BranchSession.java @@ -15,22 +15,25 @@ */ package io.seata.server.session; +import io.seata.common.util.CompressUtil; +import io.seata.core.exception.TransactionException; +import io.seata.core.model.BranchStatus; +import io.seata.core.model.BranchType; +import io.seata.core.model.LockStatus; +import io.seata.server.lock.LockerManagerFactory; +import io.seata.server.storage.file.lock.FileLocker; +import io.seata.server.store.SessionStorable; +import io.seata.server.store.StoreConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.IOException; import java.nio.ByteBuffer; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import io.seata.server.storage.file.lock.FileLocker; -import io.seata.common.util.CompressUtil; -import io.seata.core.exception.TransactionException; -import io.seata.core.model.BranchStatus; -import io.seata.core.model.BranchType; -import io.seata.server.lock.LockerManagerFactory; -import io.seata.server.store.SessionStorable; -import io.seata.server.store.StoreConfig; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import static io.seata.core.model.LockStatus.Locked; /** * The type Branch session. @@ -66,6 +69,8 @@ public class BranchSession implements Lockable, Comparable, Sessi private String applicationData; + private LockStatus lockStatus = Locked; + private ConcurrentMap> lockHolder = new ConcurrentHashMap<>(); /** @@ -252,8 +257,12 @@ public class BranchSession implements Lockable, Comparable, Sessi @Override public boolean lock() throws TransactionException { + return this.lock(true, false); + } + + public boolean lock(boolean autoCommit, boolean skipCheckLock) throws TransactionException { if (this.getBranchType().equals(BranchType.AT)) { - return LockerManagerFactory.getLockManager().acquireLock(this); + return LockerManagerFactory.getLockManager().acquireLock(this, autoCommit, skipCheckLock); } return true; } @@ -266,6 +275,14 @@ public class BranchSession implements Lockable, Comparable, Sessi return true; } + public LockStatus getLockStatus() { + return lockStatus; + } + + public void setLockStatus(LockStatus lockStatus) { + this.lockStatus = lockStatus; + } + @Override public byte[] encode() { @@ -356,6 +373,7 @@ public class BranchSession implements Lockable, Comparable, Sessi byteBuffer.put(branchTypeByte); byteBuffer.put((byte) status.getCode()); + byteBuffer.put((byte) lockStatus.getCode()); byteBuffer.flip(); byte[] result = new byte[byteBuffer.limit()]; byteBuffer.get(result); @@ -430,7 +448,6 @@ public class BranchSession implements Lockable, Comparable, Sessi this.branchType = BranchType.values()[branchTypeId]; } this.status = BranchStatus.get(byteBuffer.get()); - } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/GlobalSession.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/GlobalSession.java index 240657045..43939eab7 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/GlobalSession.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/GlobalSession.java @@ -15,15 +15,6 @@ */ package io.seata.server.session; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - import io.seata.common.Constants; import io.seata.common.DefaultValues; import io.seata.common.XID; @@ -36,6 +27,7 @@ import io.seata.core.exception.TransactionExceptionCode; import io.seata.core.model.BranchStatus; import io.seata.core.model.BranchType; import io.seata.core.model.GlobalStatus; +import io.seata.core.model.LockStatus; import io.seata.server.UUIDGenerator; import io.seata.server.lock.LockerManagerFactory; import io.seata.server.store.SessionStorable; @@ -43,9 +35,13 @@ import io.seata.server.store.StoreConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static io.seata.core.model.GlobalStatus.AsyncCommitting; -import static io.seata.core.model.GlobalStatus.CommitRetrying; -import static io.seata.core.model.GlobalStatus.Committing; +import java.nio.ByteBuffer; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import static io.seata.core.model.GlobalStatus.*; /** * The type Global session. @@ -87,9 +83,11 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { private String applicationData; + private final boolean lazyLoadBranch; + private volatile boolean active = true; - private final ArrayList branchSessions = new ArrayList<>(); + private List branchSessions; private GlobalSessionLock globalSessionLock = new GlobalSessionLock(); @@ -99,7 +97,13 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { * @return the boolean */ public boolean add(BranchSession branchSession) { - return branchSessions.add(branchSession); + if (null != branchSessions) { + return branchSessions.add(branchSession); + } + else { + // db and redis no need to deal with + return true; + } } /** @@ -118,6 +122,7 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { * @return the boolean */ public boolean canBeCommittedAsync() { + List branchSessions = getBranchSessions(); for (BranchSession branchSession : branchSessions) { if (!branchSession.canBeCommittedAsync()) { return false; @@ -131,6 +136,7 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { * @return the boolean */ public boolean hasATBranch() { + List branchSessions = getBranchSessions(); for (BranchSession branchSession : branchSessions) { if (branchSession.getBranchType() == BranchType.AT) { return true; @@ -144,6 +150,7 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { * @return is saga */ public boolean isSaga() { + List branchSessions = getBranchSessions(); if (branchSessions.size() > 0) { return BranchType.SAGA == branchSessions.get(0).getBranchType(); } @@ -180,7 +187,10 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { } @Override - public void changeStatus(GlobalStatus status) throws TransactionException { + public void changeGlobalStatus(GlobalStatus status) throws TransactionException { + if (GlobalStatus.Rollbacking == status) { + LockerManagerFactory.getLockManager().updateLockStatus(xid, LockStatus.Rollbacking); + } this.status = status; for (SessionLifecycleListener lifecycleListener : lifecycleListeners) { lifecycleListener.onStatusChange(this, status); @@ -211,19 +221,31 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { @Override public void end() throws TransactionException { - // Clean locks first - clean(); - - for (SessionLifecycleListener lifecycleListener : lifecycleListeners) { - lifecycleListener.onEnd(this); + if (isSuccessEnd()) { + // Clean locks first + clean(); + for (SessionLifecycleListener lifecycleListener : lifecycleListeners) { + lifecycleListener.onSuccessEnd(this); + } + } + else { + for (SessionLifecycleListener lifecycleListener : lifecycleListeners) { + lifecycleListener.onFailEnd(this); + } } } + public boolean isSuccessEnd() { + if (status == GlobalStatus.Committed || status == GlobalStatus.Rollbacked + || status == GlobalStatus.TimeoutRollbacked) { + return true; + } + return false; + } + public void clean() throws TransactionException { - if (this.hasATBranch()) { - if (!LockerManagerFactory.getLockManager().releaseGlobalSessionLock(this)) { - throw new TransactionException("UnLock globalSession error, xid = " + this.xid); - } + if (!LockerManagerFactory.getLockManager().releaseGlobalSessionLock(this)) { + throw new TransactionException("UnLock globalSession error, xid = " + this.xid); } } @@ -233,7 +255,9 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { */ public void closeAndClean() throws TransactionException { close(); - clean(); + if (this.hasATBranch()) { + clean(); + } } /** @@ -261,6 +285,18 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { add(branchSession); } + public void loadBranchs() { + if (branchSessions == null && isLazyLoadBranch()) { + synchronized (this) { + if (branchSessions == null && isLazyLoadBranch()) { + branchSessions = new ArrayList<>(); + Optional.ofNullable(SessionHolder.getRootSessionManager().findGlobalSession(xid, true)) + .ifPresent(globalSession -> branchSessions.addAll(globalSession.getBranchSessions())); + } + } + } + } + @Override public void removeBranch(BranchSession branchSession) throws TransactionException { // do not unlock if global status in (Committing, CommitRetrying, @@ -284,7 +320,8 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { * @return the branch */ public BranchSession getBranch(long branchId) { - synchronized (branchSessions) { + synchronized (this) { + List branchSessions = getBranchSessions(); for (BranchSession branchSession : branchSessions) { if (branchSession.getBranchId() == branchId) { return branchSession; @@ -299,16 +336,16 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { * Gets sorted branches. * @return the sorted branches */ - public ArrayList getSortedBranches() { - return new ArrayList<>(branchSessions); + public List getSortedBranches() { + return new ArrayList<>(getBranchSessions()); } /** * Gets reverse sorted branches. * @return the reverse sorted branches */ - public ArrayList getReverseSortedBranches() { - ArrayList reversed = new ArrayList<>(branchSessions); + public List getReverseSortedBranches() { + List reversed = new ArrayList<>(getBranchSessions()); Collections.reverse(reversed); return reversed; } @@ -317,6 +354,30 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { * Instantiates a new Global session. */ public GlobalSession() { + this.lazyLoadBranch = false; + } + + /** + * Instantiates a new Global session. + * @param applicationId the application id + * @param transactionServiceGroup the transaction service group + * @param transactionName the transaction name + * @param timeout the timeout + * @param lazyLoadBranch the lazy load branch + */ + public GlobalSession(String applicationId, String transactionServiceGroup, String transactionName, int timeout, + boolean lazyLoadBranch) { + this.transactionId = UUIDGenerator.generateUUID(); + this.status = GlobalStatus.Begin; + this.lazyLoadBranch = lazyLoadBranch; + if (!lazyLoadBranch) { + this.branchSessions = new ArrayList<>(); + } + this.applicationId = applicationId; + this.transactionServiceGroup = transactionServiceGroup; + this.transactionName = transactionName; + this.timeout = timeout; + this.xid = XID.generateXID(transactionId); } /** @@ -327,14 +388,7 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { * @param timeout the timeout */ public GlobalSession(String applicationId, String transactionServiceGroup, String transactionName, int timeout) { - this.transactionId = UUIDGenerator.generateUUID(); - this.status = GlobalStatus.Begin; - - this.applicationId = applicationId; - this.transactionServiceGroup = transactionServiceGroup; - this.transactionName = transactionName; - this.timeout = timeout; - this.xid = XID.generateXID(transactionId); + this(applicationId, transactionServiceGroup, transactionName, timeout, false); } /** @@ -449,6 +503,10 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { this.applicationData = applicationData; } + public boolean isLazyLoadBranch() { + return lazyLoadBranch; + } + /** * Create global session global session. * @param applicationId the application id @@ -459,7 +517,7 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { */ public static GlobalSession createGlobalSession(String applicationId, String txServiceGroup, String txName, int timeout) { - GlobalSession session = new GlobalSession(applicationId, txServiceGroup, txName, timeout); + GlobalSession session = new GlobalSession(applicationId, txServiceGroup, txName, timeout, false); return session; } @@ -560,6 +618,7 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { @Override public void decode(byte[] a) { + this.branchSessions = new ArrayList<>(); ByteBuffer byteBuffer = ByteBuffer.wrap(a); this.transactionId = byteBuffer.getLong(); this.timeout = byteBuffer.getInt(); @@ -603,7 +662,7 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { * @return the boolean */ public boolean hasBranch() { - return branchSessions.size() > 0; + return getBranchSessions().size() > 0; } public void lock() throws TransactionException { @@ -653,32 +712,43 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { } - public ArrayList getBranchSessions() { + public List getBranchSessions() { + loadBranchs(); return branchSessions; } public void asyncCommit() throws TransactionException { this.addSessionLifecycleListener(SessionHolder.getAsyncCommittingSessionManager()); + this.setStatus(GlobalStatus.AsyncCommitting); SessionHolder.getAsyncCommittingSessionManager().addGlobalSession(this); - this.changeStatus(GlobalStatus.AsyncCommitting); } public void queueToRetryCommit() throws TransactionException { this.addSessionLifecycleListener(SessionHolder.getRetryCommittingSessionManager()); + this.setStatus(GlobalStatus.CommitRetrying); SessionHolder.getRetryCommittingSessionManager().addGlobalSession(this); - this.changeStatus(GlobalStatus.CommitRetrying); } public void queueToRetryRollback() throws TransactionException { this.addSessionLifecycleListener(SessionHolder.getRetryRollbackingSessionManager()); - SessionHolder.getRetryRollbackingSessionManager().addGlobalSession(this); GlobalStatus currentStatus = this.getStatus(); - if (SessionHelper.isTimeoutGlobalStatus(currentStatus)) { - this.changeStatus(GlobalStatus.TimeoutRollbackRetrying); + if (SessionStatusValidator.isTimeoutGlobalStatus(currentStatus)) { + this.setStatus(GlobalStatus.TimeoutRollbackRetrying); } else { - this.changeStatus(GlobalStatus.RollbackRetrying); + this.setStatus(GlobalStatus.RollbackRetrying); } + SessionHolder.getRetryRollbackingSessionManager().addGlobalSession(this); + } + + @Override + public String toString() { + return "GlobalSession{" + "xid='" + xid + '\'' + ", transactionId=" + transactionId + ", status=" + status + + ", applicationId='" + applicationId + '\'' + ", transactionServiceGroup='" + transactionServiceGroup + + '\'' + ", transactionName='" + transactionName + '\'' + ", timeout=" + timeout + ", beginTime=" + + beginTime + ", applicationData='" + applicationData + '\'' + ", lazyLoadBranch=" + lazyLoadBranch + + ", active=" + active + ", branchSessions=" + branchSessions + ", globalSessionLock=" + + globalSessionLock + ", lifecycleListeners=" + lifecycleListeners + '}'; } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionCondition.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionCondition.java index 3f6d36ead..289d94f09 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionCondition.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionCondition.java @@ -32,7 +32,9 @@ public class SessionCondition { private GlobalStatus[] statuses; - private long overTimeAliveMills; + private Long overTimeAliveMills; + + private boolean lazyLoadBranch; /** * Instantiates a new Session condition. @@ -54,14 +56,14 @@ public class SessionCondition { */ public SessionCondition(GlobalStatus status) { this.status = status; - statuses = new GlobalStatus[] { status }; + this.statuses = new GlobalStatus[] { status }; } /** * Instantiates a new Session condition. * @param statuses the statuses */ - public SessionCondition(GlobalStatus[] statuses) { + public SessionCondition(GlobalStatus... statuses) { this.statuses = statuses; } @@ -87,13 +89,14 @@ public class SessionCondition { */ public void setStatus(GlobalStatus status) { this.status = status; + this.statuses = new GlobalStatus[] { status }; } /** * Gets over time alive mills. * @return the over time alive mills */ - public long getOverTimeAliveMills() { + public Long getOverTimeAliveMills() { return overTimeAliveMills; } @@ -101,7 +104,7 @@ public class SessionCondition { * Sets over time alive mills. * @param overTimeAliveMills the over time alive mills */ - public void setOverTimeAliveMills(long overTimeAliveMills) { + public void setOverTimeAliveMills(Long overTimeAliveMills) { this.overTimeAliveMills = overTimeAliveMills; } @@ -125,8 +128,16 @@ public class SessionCondition { return statuses; } - public void setStatuses(GlobalStatus[] statuses) { + public void setStatuses(GlobalStatus... statuses) { this.statuses = statuses; } + public boolean isLazyLoadBranch() { + return lazyLoadBranch; + } + + public void setLazyLoadBranch(boolean lazyLoadBranch) { + this.lazyLoadBranch = lazyLoadBranch; + } + } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionHelper.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionHelper.java index 63f77945d..f272df766 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionHelper.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionHelper.java @@ -15,17 +15,30 @@ */ package io.seata.server.session; -import java.util.Collection; - +import io.seata.common.util.CollectionUtils; +import io.seata.config.Configuration; +import io.seata.config.ConfigurationFactory; +import io.seata.core.constants.ConfigurationKeys; import io.seata.core.context.RootContext; import io.seata.core.exception.TransactionException; import io.seata.core.model.BranchType; import io.seata.core.model.GlobalStatus; +import io.seata.metrics.IdConstants; import io.seata.server.UUIDGenerator; +import io.seata.server.coordinator.DefaultCoordinator; +import io.seata.server.metrics.MetricsPublisher; +import io.seata.server.store.StoreConfig; +import io.seata.server.store.StoreConfig.SessionMode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +import static io.seata.common.DefaultValues.DEFAULT_ENABLE_BRANCH_ASYNC_REMOVE; + /** * The type Session helper. * @@ -35,6 +48,21 @@ public class SessionHelper { private static final Logger LOGGER = LoggerFactory.getLogger(SessionHelper.class); + /** + * The constant CONFIG. + */ + private static final Configuration CONFIG = ConfigurationFactory.getInstance(); + + private static final Boolean ENABLE_BRANCH_ASYNC_REMOVE = CONFIG + .getBoolean(ConfigurationKeys.ENABLE_BRANCH_ASYNC_REMOVE, DEFAULT_ENABLE_BRANCH_ASYNC_REMOVE); + + /** + * The instance of DefaultCoordinator + */ + private static final DefaultCoordinator COORDINATOR = DefaultCoordinator.getInstance(); + + private static final boolean DELAY_HANDLE_SESSION = StoreConfig.getSessionMode() != SessionMode.FILE; + private SessionHelper() { } @@ -91,58 +119,126 @@ public class SessionHelper { /** * End committed. * @param globalSession the global session + * @param retryGlobal the retry global * @throws TransactionException the transaction exception */ - public static void endCommitted(GlobalSession globalSession) throws TransactionException { - globalSession.changeStatus(GlobalStatus.Committed); - globalSession.end(); + public static void endCommitted(GlobalSession globalSession, boolean retryGlobal) throws TransactionException { + if (retryGlobal || !DELAY_HANDLE_SESSION) { + long beginTime = System.currentTimeMillis(); + boolean retryBranch = globalSession.getStatus() == GlobalStatus.CommitRetrying; + globalSession.changeGlobalStatus(GlobalStatus.Committed); + globalSession.end(); + if (!DELAY_HANDLE_SESSION) { + MetricsPublisher.postSessionDoneEvent(globalSession, false, false); + } + MetricsPublisher.postSessionDoneEvent(globalSession, IdConstants.STATUS_VALUE_AFTER_COMMITTED_KEY, true, + beginTime, retryBranch); + } + else { + MetricsPublisher.postSessionDoneEvent(globalSession, false, false); + } } /** * End commit failed. * @param globalSession the global session + * @param retryGlobal the retry global * @throws TransactionException the transaction exception */ - public static void endCommitFailed(GlobalSession globalSession) throws TransactionException { - globalSession.changeStatus(GlobalStatus.CommitFailed); + public static void endCommitFailed(GlobalSession globalSession, boolean retryGlobal) throws TransactionException { + endCommitFailed(globalSession, retryGlobal, false); + } + + /** + * End commit failed. + * @param globalSession the global session + * @param retryGlobal the retry global + * @param isRetryTimeout is retry timeout + * @throws TransactionException the transaction exception + */ + public static void endCommitFailed(GlobalSession globalSession, boolean retryGlobal, boolean isRetryTimeout) + throws TransactionException { + if (isRetryTimeout) { + globalSession.changeGlobalStatus(GlobalStatus.CommitRetryTimeout); + } + else { + globalSession.changeGlobalStatus(GlobalStatus.CommitFailed); + } + LOGGER.error("The Global session {} has changed the status to {}, need to be handled it manually.", + globalSession.getXid(), globalSession.getStatus()); + globalSession.end(); + MetricsPublisher.postSessionDoneEvent(globalSession, retryGlobal, false); } /** * End rollbacked. * @param globalSession the global session + * @param retryGlobal the retry global * @throws TransactionException the transaction exception */ - public static void endRollbacked(GlobalSession globalSession) throws TransactionException { - GlobalStatus currentStatus = globalSession.getStatus(); - if (isTimeoutGlobalStatus(currentStatus)) { - globalSession.changeStatus(GlobalStatus.TimeoutRollbacked); + public static void endRollbacked(GlobalSession globalSession, boolean retryGlobal) throws TransactionException { + if (retryGlobal || !DELAY_HANDLE_SESSION) { + long beginTime = System.currentTimeMillis(); + boolean timeoutDone = false; + GlobalStatus currentStatus = globalSession.getStatus(); + if (currentStatus == GlobalStatus.TimeoutRollbacking) { + MetricsPublisher.postSessionDoneEvent(globalSession, GlobalStatus.TimeoutRollbacked, false, false); + timeoutDone = true; + } + boolean retryBranch = currentStatus == GlobalStatus.TimeoutRollbackRetrying + || currentStatus == GlobalStatus.RollbackRetrying; + if (SessionStatusValidator.isTimeoutGlobalStatus(currentStatus)) { + globalSession.changeGlobalStatus(GlobalStatus.TimeoutRollbacked); + } + else { + globalSession.changeGlobalStatus(GlobalStatus.Rollbacked); + } + globalSession.end(); + if (!DELAY_HANDLE_SESSION && !timeoutDone) { + MetricsPublisher.postSessionDoneEvent(globalSession, false, false); + } + MetricsPublisher.postSessionDoneEvent(globalSession, IdConstants.STATUS_VALUE_AFTER_ROLLBACKED_KEY, true, + beginTime, retryBranch); } else { - globalSession.changeStatus(GlobalStatus.Rollbacked); + MetricsPublisher.postSessionDoneEvent(globalSession, GlobalStatus.Rollbacked, false, false); } - globalSession.end(); } /** * End rollback failed. * @param globalSession the global session + * @param retryGlobal the retry global * @throws TransactionException the transaction exception */ - public static void endRollbackFailed(GlobalSession globalSession) throws TransactionException { - GlobalStatus currentStatus = globalSession.getStatus(); - if (isTimeoutGlobalStatus(currentStatus)) { - globalSession.changeStatus(GlobalStatus.TimeoutRollbackFailed); - } - else { - globalSession.changeStatus(GlobalStatus.RollbackFailed); - } - globalSession.end(); + public static void endRollbackFailed(GlobalSession globalSession, boolean retryGlobal) throws TransactionException { + endRollbackFailed(globalSession, retryGlobal, false); } - public static boolean isTimeoutGlobalStatus(GlobalStatus status) { - return status == GlobalStatus.TimeoutRollbacked || status == GlobalStatus.TimeoutRollbackFailed - || status == GlobalStatus.TimeoutRollbacking || status == GlobalStatus.TimeoutRollbackRetrying; + /** + * End rollback failed. + * @param globalSession the global session + * @param retryGlobal the retry global + * @param isRetryTimeout is retry timeout + * @throws TransactionException the transaction exception + */ + public static void endRollbackFailed(GlobalSession globalSession, boolean retryGlobal, boolean isRetryTimeout) + throws TransactionException { + GlobalStatus currentStatus = globalSession.getStatus(); + if (isRetryTimeout) { + globalSession.changeGlobalStatus(GlobalStatus.RollbackRetryTimeout); + } + else if (SessionStatusValidator.isTimeoutGlobalStatus(currentStatus)) { + globalSession.changeGlobalStatus(GlobalStatus.TimeoutRollbackFailed); + } + else { + globalSession.changeGlobalStatus(GlobalStatus.RollbackFailed); + } + LOGGER.error("The Global session {} has changed the status to {}, need to be handled it manually.", + globalSession.getXid(), globalSession.getStatus()); + globalSession.end(); + MetricsPublisher.postSessionDoneEvent(globalSession, retryGlobal, false); } /** @@ -152,7 +248,10 @@ public class SessionHelper { * @since 1.5.0 */ public static void forEach(Collection sessions, GlobalSessionHandler handler) { - for (GlobalSession globalSession : sessions) { + if (CollectionUtils.isEmpty(sessions)) { + return; + } + sessions.parallelStream().forEach(globalSession -> { try { MDC.put(RootContext.MDC_KEY_XID, globalSession.getXid()); handler.handle(globalSession); @@ -163,7 +262,7 @@ public class SessionHelper { finally { MDC.remove(RootContext.MDC_KEY_XID); } - } + }); } /** @@ -191,4 +290,40 @@ public class SessionHelper { return null; } + /** + * remove branchSession from globalSession + * @param globalSession the globalSession + * @param branchSession the branchSession + * @param isAsync if asynchronous remove + */ + public static void removeBranch(GlobalSession globalSession, BranchSession branchSession, boolean isAsync) + throws TransactionException { + if (Objects.equals(Boolean.TRUE, ENABLE_BRANCH_ASYNC_REMOVE) && isAsync) { + COORDINATOR.doBranchRemoveAsync(globalSession, branchSession); + } + else { + globalSession.removeBranch(branchSession); + } + } + + /** + * remove branchSession from globalSession + * @param globalSession the globalSession + * @param isAsync if asynchronous remove + */ + public static void removeAllBranch(GlobalSession globalSession, boolean isAsync) throws TransactionException { + List branchSessions = globalSession.getSortedBranches(); + 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); + } + } + } + } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionHolder.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionHolder.java index 6393eeda6..da0c2dca9 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionHolder.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionHolder.java @@ -15,12 +15,8 @@ */ package io.seata.server.session; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; - +import io.seata.common.ConfigurationKeys; +import io.seata.common.XID; import io.seata.common.exception.ShouldNeverHappenException; import io.seata.common.exception.StoreException; import io.seata.common.loader.EnhancedServiceLoader; @@ -28,18 +24,23 @@ import io.seata.common.util.CollectionUtils; import io.seata.common.util.StringUtils; import io.seata.config.Configuration; import io.seata.config.ConfigurationFactory; -import io.seata.core.constants.ConfigurationKeys; import io.seata.core.exception.TransactionException; import io.seata.core.model.GlobalStatus; -import io.seata.core.store.StoreMode; +import io.seata.core.model.LockStatus; +import io.seata.core.store.DistributedLockDO; +import io.seata.core.store.DistributedLocker; +import io.seata.server.lock.distributed.DistributedLockerFactory; +import io.seata.server.store.StoreConfig; +import io.seata.server.store.StoreConfig.SessionMode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static io.seata.common.Constants.ASYNC_COMMITTING; -import static io.seata.common.Constants.RETRY_COMMITTING; -import static io.seata.common.Constants.RETRY_ROLLBACKING; -import static io.seata.common.Constants.TX_TIMEOUT_CHECK; -import static io.seata.common.Constants.UNDOLOG_DELETE; +import java.io.IOException; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import static io.seata.common.DefaultValues.DEFAULT_DISTRIBUTED_LOCK_EXPIRE_TIME; /** * The type Session holder. @@ -80,6 +81,12 @@ public class SessionHolder { */ public static final String DEFAULT_SESSION_STORE_FILE_DIR = "sessionStore"; + /** + * The redis distributed lock expire time + */ + private static long DISTRIBUTED_LOCK_EXPIRE_TIME = CONFIG.getLong(ConfigurationKeys.DISTRIBUTED_LOCK_EXPIRE_TIME, + DEFAULT_DISTRIBUTED_LOCK_EXPIRE_TIME); + private static SessionManager ROOT_SESSION_MANAGER; private static SessionManager ASYNC_COMMITTING_SESSION_MANAGER; @@ -88,121 +95,149 @@ public class SessionHolder { private static SessionManager RETRY_ROLLBACKING_SESSION_MANAGER; + private static DistributedLocker DISTRIBUTED_LOCKER; + + public static void init() { + init(null); + } + /** * Init. - * @param mode the store mode: file, db + * @param sessionMode the store mode: file, db, redis * @throws IOException the io exception */ - public static void init(String mode) { - if (StringUtils.isBlank(mode)) { - mode = CONFIG.getConfig(ConfigurationKeys.STORE_MODE); + public static void init(SessionMode sessionMode) { + if (null == sessionMode) { + sessionMode = StoreConfig.getSessionMode(); } - StoreMode storeMode = StoreMode.get(mode); - if (StoreMode.DB.equals(storeMode)) { - ROOT_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.DB.getName()); - ASYNC_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.DB.getName(), - new Object[] { ASYNC_COMMITTING_SESSION_MANAGER_NAME }); - RETRY_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.DB.getName(), - new Object[] { RETRY_COMMITTING_SESSION_MANAGER_NAME }); - RETRY_ROLLBACKING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.DB.getName(), - new Object[] { RETRY_ROLLBACKING_SESSION_MANAGER_NAME }); + 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(), new Object[] { ASYNC_COMMITTING_SESSION_MANAGER_NAME }); + RETRY_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, + SessionMode.DB.getName(), new Object[] { RETRY_COMMITTING_SESSION_MANAGER_NAME }); + RETRY_ROLLBACKING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, + SessionMode.DB.getName(), new Object[] { RETRY_ROLLBACKING_SESSION_MANAGER_NAME }); + + DISTRIBUTED_LOCKER = DistributedLockerFactory.getDistributedLocker(SessionMode.DB.getName()); } - else if (StoreMode.FILE.equals(storeMode)) { + else if (SessionMode.FILE.equals(sessionMode)) { String sessionStorePath = CONFIG.getConfig(ConfigurationKeys.STORE_FILE_DIR, DEFAULT_SESSION_STORE_FILE_DIR); if (StringUtils.isBlank(sessionStorePath)) { throw new StoreException("the {store.file.dir} is empty."); } - ROOT_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.FILE.getName(), + ROOT_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, SessionMode.FILE.getName(), new Object[] { ROOT_SESSION_MANAGER_NAME, sessionStorePath }); - ASYNC_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, - StoreMode.FILE.getName(), new Class[] { String.class, String.class }, - new Object[] { ASYNC_COMMITTING_SESSION_MANAGER_NAME, null }); - RETRY_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, - StoreMode.FILE.getName(), new Class[] { String.class, String.class }, - new Object[] { RETRY_COMMITTING_SESSION_MANAGER_NAME, null }); - RETRY_ROLLBACKING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, - StoreMode.FILE.getName(), new Class[] { String.class, String.class }, - new Object[] { RETRY_ROLLBACKING_SESSION_MANAGER_NAME, null }); + ASYNC_COMMITTING_SESSION_MANAGER = ROOT_SESSION_MANAGER; + RETRY_COMMITTING_SESSION_MANAGER = ROOT_SESSION_MANAGER; + RETRY_ROLLBACKING_SESSION_MANAGER = ROOT_SESSION_MANAGER; + + DISTRIBUTED_LOCKER = DistributedLockerFactory.getDistributedLocker(SessionMode.FILE.getName()); } - else if (StoreMode.REDIS.equals(storeMode)) { - ROOT_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.REDIS.getName()); + else if (SessionMode.REDIS.equals(sessionMode)) { + ROOT_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, SessionMode.REDIS.getName()); ASYNC_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, - StoreMode.REDIS.getName(), new Object[] { ASYNC_COMMITTING_SESSION_MANAGER_NAME }); + SessionMode.REDIS.getName(), new Object[] { ASYNC_COMMITTING_SESSION_MANAGER_NAME }); RETRY_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, - StoreMode.REDIS.getName(), new Object[] { RETRY_COMMITTING_SESSION_MANAGER_NAME }); + SessionMode.REDIS.getName(), new Object[] { RETRY_COMMITTING_SESSION_MANAGER_NAME }); RETRY_ROLLBACKING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, - StoreMode.REDIS.getName(), new Object[] { RETRY_ROLLBACKING_SESSION_MANAGER_NAME }); + SessionMode.REDIS.getName(), new Object[] { RETRY_ROLLBACKING_SESSION_MANAGER_NAME }); + + DISTRIBUTED_LOCKER = DistributedLockerFactory.getDistributedLocker(SessionMode.REDIS.getName()); } else { // unknown store - throw new IllegalArgumentException("unknown store mode:" + mode); + throw new IllegalArgumentException("unknown store mode:" + sessionMode.getName()); } - reload(storeMode); + reload(sessionMode); } // region reload /** * Reload. + * @param sessionMode the mode of store */ - protected static void reload(StoreMode storeMode) { + protected static void reload(SessionMode sessionMode) { + if (ROOT_SESSION_MANAGER instanceof Reloadable) { ((Reloadable) ROOT_SESSION_MANAGER).reload(); } - Collection allSessions = ROOT_SESSION_MANAGER.allSessions(); - if (CollectionUtils.isNotEmpty(allSessions)) { - List removeGlobalSessions = new ArrayList<>(); - Iterator iterator = allSessions.iterator(); - while (iterator.hasNext()) { - GlobalSession globalSession = iterator.next(); - GlobalStatus globalStatus = globalSession.getStatus(); - switch (globalStatus) { - case UnKnown: - case Committed: - case CommitFailed: - case Rollbacked: - case RollbackFailed: - case TimeoutRollbacked: - case TimeoutRollbackFailed: - case Finished: - removeGlobalSessions.add(globalSession); - break; - case AsyncCommitting: - if (storeMode == StoreMode.FILE) { + if (SessionMode.FILE.equals(sessionMode)) { + Collection allSessions = ROOT_SESSION_MANAGER.allSessions(); + if (CollectionUtils.isNotEmpty(allSessions)) { + for (GlobalSession globalSession : allSessions) { + GlobalStatus globalStatus = globalSession.getStatus(); + switch (globalStatus) { + case UnKnown: + case Committed: + case CommitFailed: + case Rollbacked: + case RollbackFailed: + case TimeoutRollbacked: + case TimeoutRollbackFailed: + case Finished: + removeInErrorState(globalSession); + break; + case AsyncCommitting: queueToAsyncCommitting(globalSession); - } - break; - default: { - if (storeMode == StoreMode.FILE) { + break; + case Committing: + case CommitRetrying: + queueToRetryCommit(globalSession); + break; + default: { lockBranchSessions(globalSession.getSortedBranches()); - switch (globalStatus) { - case Committing: - case CommitRetrying: - queueToRetryCommit(globalSession); - break; case Rollbacking: case RollbackRetrying: case TimeoutRollbacking: case TimeoutRollbackRetrying: + globalSession.getBranchSessions().parallelStream().forEach( + branchSession -> branchSession.setLockStatus(LockStatus.Rollbacking)); queueToRetryRollback(globalSession); break; case Begin: globalSession.setActive(true); break; default: + LOGGER.error("Could not handle the global session, xid: {}", + globalSession.getXid()); throw new ShouldNeverHappenException("NOT properly handled " + globalStatus); } + break; } - break; } } } - for (GlobalSession globalSession : removeGlobalSessions) { - removeInErrorState(globalSession); - } + } + else { + // Redis, db and so on + CompletableFuture.runAsync(() -> { + SessionCondition searchCondition = new SessionCondition(GlobalStatus.UnKnown, GlobalStatus.Committed, + GlobalStatus.Rollbacked, GlobalStatus.TimeoutRollbacked, GlobalStatus.Finished); + searchCondition.setLazyLoadBranch(true); + + long now = System.currentTimeMillis(); + List errorStatusGlobalSessions = ROOT_SESSION_MANAGER + .findGlobalSessions(searchCondition); + while (!CollectionUtils.isEmpty(errorStatusGlobalSessions)) { + for (GlobalSession errorStatusGlobalSession : errorStatusGlobalSessions) { + if (errorStatusGlobalSession.getBeginTime() >= now) { + // Exit when the global transaction begin after the instance + // started + return; + } + + removeInErrorState(errorStatusGlobalSession); + } + + // Load the next part + errorStatusGlobalSessions = ROOT_SESSION_MANAGER.findGlobalSessions(searchCondition); + } + }); } } @@ -232,7 +267,7 @@ public class SessionHolder { } } - private static void lockBranchSessions(ArrayList branchSessions) { + private static void lockBranchSessions(List branchSessions) { branchSessions.forEach(branchSession -> { try { branchSession.lock(); @@ -282,6 +317,7 @@ public class SessionHolder { * Gets async committing session manager. * @return the async committing session manager */ + @Deprecated public static SessionManager getAsyncCommittingSessionManager() { if (ASYNC_COMMITTING_SESSION_MANAGER == null) { throw new ShouldNeverHappenException("SessionManager is NOT init!"); @@ -293,6 +329,7 @@ public class SessionHolder { * Gets retry committing session manager. * @return the retry committing session manager */ + @Deprecated public static SessionManager getRetryCommittingSessionManager() { if (RETRY_COMMITTING_SESSION_MANAGER == null) { throw new ShouldNeverHappenException("SessionManager is NOT init!"); @@ -304,6 +341,7 @@ public class SessionHolder { * Gets retry rollbacking session manager. * @return the retry rollbacking session manager */ + @Deprecated public static SessionManager getRetryRollbackingSessionManager() { if (RETRY_ROLLBACKING_SESSION_MANAGER == null) { throw new ShouldNeverHappenException("SessionManager is NOT init!"); @@ -344,98 +382,64 @@ public class SessionHolder { } /** - * retry rollbacking lock + * acquire lock + * @param lockKey the lock key, should be distinct for each lock * @return the boolean */ - public static boolean retryRollbackingLock() { - return getRootSessionManager().scheduledLock(RETRY_ROLLBACKING); + public static boolean acquireDistributedLock(String lockKey) { + return DISTRIBUTED_LOCKER + .acquireLock(new DistributedLockDO(lockKey, XID.getIpAddressAndPort(), DISTRIBUTED_LOCK_EXPIRE_TIME)); } /** - * retry committing lock + * release lock * @return the boolean */ - public static boolean retryCommittingLock() { - return getRootSessionManager().scheduledLock(RETRY_COMMITTING); + public static boolean releaseDistributedLock(String lockKey) { + return DISTRIBUTED_LOCKER + .releaseLock(new DistributedLockDO(lockKey, XID.getIpAddressAndPort(), DISTRIBUTED_LOCK_EXPIRE_TIME)); } /** - * async committing lock - * @return the boolean + * Execute the function after get the distribute lock + * @param key the distribute lock key + * @param func the function to be call + * @return whether the func be call */ - public static boolean asyncCommittingLock() { - return getRootSessionManager().scheduledLock(ASYNC_COMMITTING); - } - - /** - * tx timeout check lOck - * @return the boolean - */ - public static boolean txTimeoutCheckLock() { - return getRootSessionManager().scheduledLock(TX_TIMEOUT_CHECK); - } - - /** - * undolog delete lock - * @return the boolean - */ - public static boolean undoLogDeleteLock() { - return getRootSessionManager().scheduledLock(UNDOLOG_DELETE); - } - - /** - * un retry rollbacking lock - * @return the boolean - */ - public static boolean unRetryRollbackingLock() { - return getRootSessionManager().unScheduledLock(RETRY_ROLLBACKING); - } - - /** - * un retry committing lock - * @return the boolean - */ - public static boolean unRetryCommittingLock() { - return getRootSessionManager().unScheduledLock(RETRY_COMMITTING); - } - - /** - * un async committing lock - * @return the boolean - */ - public static boolean unAsyncCommittingLock() { - return getRootSessionManager().unScheduledLock(ASYNC_COMMITTING); - } - - /** - * un tx timeout check lOck - * @return the boolean - */ - public static boolean unTxTimeoutCheckLock() { - return getRootSessionManager().unScheduledLock(TX_TIMEOUT_CHECK); - } - - /** - * un undolog delete lock - * @return the boolean - */ - public static boolean unUndoLogDeleteLock() { - return getRootSessionManager().unScheduledLock(UNDOLOG_DELETE); + public static boolean distributedLockAndExecute(String key, NoArgsFunc func) { + boolean lock = false; + try { + if (lock = acquireDistributedLock(key)) { + func.call(); + } + } + catch (Exception e) { + LOGGER.info("Exception running function with key = {}", key, e); + } + finally { + if (lock) { + try { + SessionHolder.releaseDistributedLock(key); + } + catch (Exception ex) { + LOGGER.warn("release distribute lock failure, message = {}", ex.getMessage(), ex); + } + } + } + return lock; } public static void destroy() { if (ROOT_SESSION_MANAGER != null) { ROOT_SESSION_MANAGER.destroy(); } - if (ASYNC_COMMITTING_SESSION_MANAGER != null) { - ASYNC_COMMITTING_SESSION_MANAGER.destroy(); - } - if (RETRY_COMMITTING_SESSION_MANAGER != null) { - RETRY_COMMITTING_SESSION_MANAGER.destroy(); - } - if (RETRY_ROLLBACKING_SESSION_MANAGER != null) { - RETRY_ROLLBACKING_SESSION_MANAGER.destroy(); - } + } + + @FunctionalInterface + public interface NoArgsFunc { + + void call(); + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionLifecycle.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionLifecycle.java index 8c54200cf..90e0a84ae 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionLifecycle.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionLifecycle.java @@ -37,7 +37,7 @@ public interface SessionLifecycle { * @param status the status * @throws TransactionException the transaction exception */ - void changeStatus(GlobalStatus status) throws TransactionException; + void changeGlobalStatus(GlobalStatus status) throws TransactionException; /** * Change branch status. @@ -74,7 +74,7 @@ public interface SessionLifecycle { void close() throws TransactionException; /** - * End. + * end. * @throws TransactionException the transaction exception */ void end() throws TransactionException; diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionLifecycleListener.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionLifecycleListener.java index dd8f03bed..5dd444c28 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionLifecycleListener.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionLifecycleListener.java @@ -79,6 +79,13 @@ public interface SessionLifecycleListener { * @param globalSession the global session * @throws TransactionException the transaction exception */ - void onEnd(GlobalSession globalSession) throws TransactionException; + void onSuccessEnd(GlobalSession globalSession) throws TransactionException; + + /** + * On fail end. + * @param globalSession the global session + * @throws TransactionException the transaction exception + */ + void onFailEnd(GlobalSession globalSession) throws TransactionException; } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionManager.java index a95184d18..132216d9f 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionManager.java @@ -15,13 +15,14 @@ */ package io.seata.server.session; -import java.util.Collection; -import java.util.List; import io.seata.core.exception.TransactionException; import io.seata.core.model.BranchStatus; import io.seata.core.model.GlobalStatus; import io.seata.core.rpc.Disposable; +import java.util.Collection; +import java.util.List; + /** * The interface Session manager. * @@ -108,26 +109,9 @@ public interface SessionManager extends SessionLifecycleListener, Disposable { * @param globalSession the global session * @param lockCallable the lock Callable * @return the value + * @throws TransactionException the transaction exception */ T lockAndExecute(GlobalSession globalSession, GlobalSession.LockCallable lockCallable) throws TransactionException; - /** - * scheduled lock - * @param key the lock key - * @return the boolean - */ - default boolean scheduledLock(String key) { - return true; - } - - /** - * un scheduled lock - * @param key the lock key - * @return the boolean - */ - default boolean unScheduledLock(String key) { - return true; - } - } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionStatusValidator.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionStatusValidator.java new file mode 100644 index 000000000..9601a6b59 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionStatusValidator.java @@ -0,0 +1,77 @@ +/* + * 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.session; + +import io.seata.core.model.GlobalStatus; + +/** + * The type change status validator. + * + * @author Bughue + */ +public class SessionStatusValidator { + + /** + * is timeout global status + * @param status the global session + */ + public static boolean isTimeoutGlobalStatus(GlobalStatus status) { + return status == GlobalStatus.TimeoutRollbacked || status == GlobalStatus.TimeoutRollbackFailed + || status == GlobalStatus.TimeoutRollbacking || status == GlobalStatus.TimeoutRollbackRetrying; + } + + /** + * is rollback global status + * @param status the global session + */ + public static boolean isRollbackGlobalStatus(GlobalStatus status) { + return status == GlobalStatus.Rollbacking || status == GlobalStatus.RollbackRetrying + || status == GlobalStatus.Rollbacked || status == GlobalStatus.RollbackFailed + || status == GlobalStatus.RollbackRetryTimeout; + } + + /** + * is commit global status + * @param status the global session + */ + public static boolean isCommitGlobalStatus(GlobalStatus status) { + return status == GlobalStatus.Committing || status == GlobalStatus.AsyncCommitting + || status == GlobalStatus.CommitRetrying || status == GlobalStatus.Committed + || status == GlobalStatus.CommitFailed || status == GlobalStatus.CommitRetryTimeout; + } + + /** + * check the relation of before status and after status + * @param before the global session + * @param after the global session + */ + public static boolean validateUpdateStatus(GlobalStatus before, GlobalStatus after) { + if (isTimeoutGlobalStatus(before) && isCommitGlobalStatus(after)) { + return false; + } + if (isCommitGlobalStatus(before) && isTimeoutGlobalStatus(after)) { + return false; + } + if (isRollbackGlobalStatus(before) && isCommitGlobalStatus(after)) { + return false; + } + if (isCommitGlobalStatus(before) && isRollbackGlobalStatus(after)) { + return false; + } + return true; + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/SessionConverter.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/SessionConverter.java index f66c812d4..e183651b5 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/SessionConverter.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/SessionConverter.java @@ -15,30 +15,37 @@ */ package io.seata.server.storage; +import io.seata.common.util.CollectionUtils; import io.seata.common.util.StringUtils; import io.seata.core.model.BranchStatus; import io.seata.core.model.BranchType; import io.seata.core.model.GlobalStatus; import io.seata.core.store.BranchTransactionDO; import io.seata.core.store.GlobalTransactionDO; +import io.seata.server.console.vo.BranchSessionVO; +import io.seata.server.console.vo.GlobalSessionVO; import io.seata.server.session.BranchSession; import io.seata.server.session.GlobalSession; import io.seata.server.store.SessionStorable; +import org.springframework.beans.BeanUtils; + +import java.util.*; /** * The session converter * * @author wangzhongxiang + * @author doubleDimple */ public class SessionConverter { - public static GlobalSession convertGlobalSession(GlobalTransactionDO globalTransactionDO) { + public static GlobalSession convertGlobalSession(GlobalTransactionDO globalTransactionDO, boolean lazyLoadBranch) { if (globalTransactionDO == null) { return null; } GlobalSession session = new GlobalSession(globalTransactionDO.getApplicationId(), globalTransactionDO.getTransactionServiceGroup(), globalTransactionDO.getTransactionName(), - globalTransactionDO.getTimeout()); + globalTransactionDO.getTimeout(), lazyLoadBranch); session.setXid(globalTransactionDO.getXid()); session.setTransactionId(globalTransactionDO.getTransactionId()); session.setStatus(GlobalStatus.get(globalTransactionDO.getStatus())); @@ -47,6 +54,10 @@ public class SessionConverter { return session; } + public static GlobalSession convertGlobalSession(GlobalTransactionDO globalTransactionDO) { + return convertGlobalSession(globalTransactionDO, false); + } + public static BranchSession convertBranchSession(BranchTransactionDO branchTransactionDO) { if (branchTransactionDO == null) { return null; @@ -103,4 +114,75 @@ public class SessionConverter { return branchTransactionDO; } + public static void convertToGlobalSessionVo(List result, List globalSessions) { + if (CollectionUtils.isNotEmpty(globalSessions)) { + for (GlobalSession globalSession : globalSessions) { + GlobalSessionVO globalSessionVO = new GlobalSessionVO(); + BeanUtils.copyProperties(globalSession, globalSessionVO); + globalSessionVO.setStatus(globalSession.getStatus().getCode()); + globalSessionVO.setTimeout(Long.valueOf(globalSession.getTimeout())); + globalSessionVO.setBranchSessionVOs(converToBranchSession(globalSession.getBranchSessions())); + result.add(globalSessionVO); + } + } + } + + public static Set converToBranchSession(List branchSessions) { + Set branchSessionVOs = new HashSet<>(branchSessions.size()); + if (CollectionUtils.isNotEmpty(branchSessions)) { + for (BranchSession branchSession : branchSessions) { + BranchSessionVO branchSessionVONew = new BranchSessionVO(); + BeanUtils.copyProperties(branchSession, branchSessionVONew); + + branchSessionVONew.setBranchType(branchSession.getBranchType().name()); + branchSessionVONew.setStatus(branchSession.getStatus().getCode()); + branchSessionVOs.add(branchSessionVONew); + } + } + return branchSessionVOs; + } + + /** + * convert GlobalSession to GlobalSessionVO + * @param filteredSessions the GlobalSession list + * @return the GlobalSessionVO list + */ + public static List convertGlobalSession(List filteredSessions) { + + if (CollectionUtils.isEmpty(filteredSessions)) { + return Collections.emptyList(); + } + + final ArrayList result = new ArrayList<>(filteredSessions.size()); + + for (GlobalSession session : filteredSessions) { + result.add(new GlobalSessionVO(session.getXid(), session.getTransactionId(), session.getStatus().getCode(), + session.getApplicationId(), session.getTransactionServiceGroup(), session.getTransactionName(), + (long) session.getTimeout(), session.getBeginTime(), session.getApplicationData(), + convertBranchSession(session.getBranchSessions()))); + } + return result; + } + + /** + * convert BranchSession to BranchSessionVO + * @param branchSessions the BranchSession list + * @return the BranchSessionVO list + */ + public static Set convertBranchSession(List branchSessions) { + + if (CollectionUtils.isEmpty(branchSessions)) { + return Collections.emptySet(); + } + + final Set result = new HashSet<>(branchSessions.size()); + + for (BranchSession session : branchSessions) { + result.add(new BranchSessionVO(session.getXid(), session.getTransactionId(), session.getBranchId(), + session.getResourceGroupId(), session.getResourceId(), session.getBranchType().name(), + session.getStatus().getCode(), session.getClientId(), session.getApplicationData())); + } + return result; + } + } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/lock/DataBaseDistributedLocker.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/lock/DataBaseDistributedLocker.java new file mode 100644 index 000000000..3840414e7 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/lock/DataBaseDistributedLocker.java @@ -0,0 +1,263 @@ +/* + * 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.storage.db.lock; + +import io.seata.common.exception.ShouldNeverHappenException; +import io.seata.common.loader.EnhancedServiceLoader; +import io.seata.common.loader.LoadLevel; +import io.seata.common.loader.Scope; +import io.seata.common.util.IOUtil; +import io.seata.common.util.StringUtils; +import io.seata.config.*; +import io.seata.core.constants.ConfigurationKeys; +import io.seata.core.constants.ServerTableColumnsName; +import io.seata.core.store.DistributedLockDO; +import io.seata.core.store.DistributedLocker; +import io.seata.core.store.db.DataSourceProvider; +import io.seata.core.store.db.sql.distributed.lock.DistributedLockSqlFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Objects; + +import static io.seata.core.constants.ConfigurationKeys.DISTRIBUTED_LOCK_DB_TABLE; + +/** + * @author chd + */ +@LoadLevel(name = "db", scope = Scope.SINGLETON) +public class DataBaseDistributedLocker implements DistributedLocker { + + private static final Logger LOGGER = LoggerFactory.getLogger(DataBaseDistributedLocker.class); + + private final String dbType; + + private final String datasourceType; + + private volatile String distributedLockTable; + + private DataSource distributedLockDataSource; + + /** + * whether the distribute lock demotion using for 1.5.0 only and will remove in 1.6.0 + */ + @Deprecated + private volatile boolean demotion; + + /** + * Instantiates a new Log store data base dao. + */ + public DataBaseDistributedLocker() { + Configuration configuration = ConfigurationFactory.getInstance(); + + distributedLockTable = configuration.getConfig(DISTRIBUTED_LOCK_DB_TABLE); + dbType = configuration.getConfig(ConfigurationKeys.STORE_DB_TYPE); + datasourceType = configuration.getConfig(ConfigurationKeys.STORE_DB_DATASOURCE_TYPE); + + if (StringUtils.isBlank(distributedLockTable)) { + demotion = true; + ConfigurationCache.addConfigListener(DISTRIBUTED_LOCK_DB_TABLE, new ConfigurationChangeListener() { + @Override + public void onChangeEvent(ConfigurationChangeEvent event) { + String newValue = event.getNewValue(); + if (StringUtils.isNotBlank(newValue)) { + distributedLockTable = newValue; + init(); + demotion = false; + ConfigurationCache.removeConfigListener(DISTRIBUTED_LOCK_DB_TABLE, this); + } + } + }); + + LOGGER.error("The distribute lock table is not config, please create the target table and config it"); + return; + } + + init(); + } + + @Override + public boolean acquireLock(DistributedLockDO distributedLockDO) { + if (demotion) { + return true; + } + + Connection connection = null; + boolean originalAutoCommit = false; + try { + connection = distributedLockDataSource.getConnection(); + originalAutoCommit = connection.getAutoCommit(); + connection.setAutoCommit(false); + + DistributedLockDO distributedLockDOFromDB = getDistributedLockDO(connection, + distributedLockDO.getLockKey()); + if (null == distributedLockDOFromDB) { + boolean ret = insertDistribute(connection, distributedLockDO); + connection.commit(); + return ret; + } + + if (distributedLockDOFromDB.getExpireTime() >= System.currentTimeMillis()) { + LOGGER.debug("the distribute lock for key :{} is holding by :{}, acquire lock failure.", + distributedLockDO.getLockKey(), distributedLockDOFromDB.getLockValue()); + connection.commit(); + return false; + } + + boolean ret = updateDistributedLock(connection, distributedLockDO); + connection.commit(); + + return ret; + } + catch (SQLException ex) { + LOGGER.error("execute acquire lock failure, key is: {}", distributedLockDO.getLockKey(), ex); + try { + if (connection != null) { + connection.rollback(); + } + } + catch (SQLException e) { + LOGGER.warn("rollback fail because of {}", e.getMessage(), e); + } + return false; + } + finally { + try { + if (originalAutoCommit) { + connection.setAutoCommit(true); + } + IOUtil.close(connection); + } + catch (SQLException ignore) { + } + } + } + + @Override + public boolean releaseLock(DistributedLockDO distributedLockDO) { + if (demotion) { + return true; + } + + Connection connection = null; + boolean originalAutoCommit = false; + try { + connection = distributedLockDataSource.getConnection(); + originalAutoCommit = connection.getAutoCommit(); + connection.setAutoCommit(false); + + DistributedLockDO distributedLockDOFromDB = getDistributedLockDO(connection, + distributedLockDO.getLockKey()); + if (null == distributedLockDOFromDB) { + throw new ShouldNeverHappenException( + "distributedLockDO would not be null when release distribute lock"); + } + + 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.", + distributedLockDO.getLockKey(), distributedLockDOFromDB.getLockValue()); + connection.commit(); + return true; + } + + distributedLockDO.setLockValue(StringUtils.SPACE); + distributedLockDO.setExpireTime(0L); + boolean ret = updateDistributedLock(connection, distributedLockDO); + + connection.commit(); + return ret; + } + catch (SQLException ex) { + LOGGER.error("execute release lock failure, key is: {}", distributedLockDO.getLockKey(), ex); + + try { + if (connection != null) { + connection.rollback(); + } + } + catch (SQLException e) { + LOGGER.warn("rollback fail because of {}", e.getMessage(), e); + } + return false; + } + finally { + try { + if (originalAutoCommit) { + connection.setAutoCommit(true); + } + IOUtil.close(connection); + } + catch (SQLException ignore) { + } + } + } + + protected DistributedLockDO getDistributedLockDO(Connection connection, String key) throws SQLException { + try (PreparedStatement pst = connection.prepareStatement(DistributedLockSqlFactory + .getDistributedLogStoreSql(dbType).getSelectDistributeForUpdateSql(distributedLockTable))) { + + pst.setString(1, key); + ResultSet resultSet = pst.executeQuery(); + + if (resultSet.next()) { + DistributedLockDO distributedLock = new DistributedLockDO(); + distributedLock.setExpireTime(resultSet.getLong(ServerTableColumnsName.DISTRIBUTED_LOCK_EXPIRE)); + distributedLock.setLockValue(resultSet.getString(ServerTableColumnsName.DISTRIBUTED_LOCK_VALUE)); + distributedLock.setLockKey(key); + return distributedLock; + } + return null; + } + } + + protected boolean insertDistribute(Connection connection, DistributedLockDO distributedLockDO) throws SQLException { + try (PreparedStatement insertPst = connection.prepareStatement( + DistributedLockSqlFactory.getDistributedLogStoreSql(dbType).getInsertSql(distributedLockTable))) { + insertPst.setString(1, distributedLockDO.getLockKey()); + insertPst.setString(2, distributedLockDO.getLockValue()); + if (distributedLockDO.getExpireTime() > 0) { + distributedLockDO.setExpireTime(distributedLockDO.getExpireTime() + System.currentTimeMillis()); + } + insertPst.setLong(3, distributedLockDO.getExpireTime()); + return insertPst.executeUpdate() > 0; + } + } + + protected boolean updateDistributedLock(Connection connection, DistributedLockDO distributedLockDO) + throws SQLException { + try (PreparedStatement updatePst = connection.prepareStatement( + DistributedLockSqlFactory.getDistributedLogStoreSql(dbType).getUpdateSql(distributedLockTable))) { + updatePst.setString(1, distributedLockDO.getLockValue()); + if (distributedLockDO.getExpireTime() > 0) { + distributedLockDO.setExpireTime(distributedLockDO.getExpireTime() + System.currentTimeMillis()); + } + updatePst.setLong(2, distributedLockDO.getExpireTime()); + updatePst.setString(3, distributedLockDO.getLockKey()); + return updatePst.executeUpdate() > 0; + } + } + + private void init() { + this.distributedLockDataSource = EnhancedServiceLoader.load(DataSourceProvider.class, datasourceType).provide(); + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/lock/DataBaseLockManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/lock/DataBaseLockManager.java index ea2f88f09..05328f3df 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/lock/DataBaseLockManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/lock/DataBaseLockManager.java @@ -15,14 +15,9 @@ */ package io.seata.server.storage.db.lock; -import java.util.List; -import java.util.stream.Collectors; -import javax.sql.DataSource; - import io.seata.common.executor.Initialize; import io.seata.common.loader.EnhancedServiceLoader; import io.seata.common.loader.LoadLevel; -import io.seata.common.util.CollectionUtils; import io.seata.config.ConfigurationFactory; import io.seata.core.constants.ConfigurationKeys; import io.seata.core.exception.TransactionException; @@ -32,6 +27,8 @@ import io.seata.server.lock.AbstractLockManager; import io.seata.server.session.BranchSession; import io.seata.server.session.GlobalSession; +import javax.sql.DataSource; + /** * The type db lock manager. * @@ -72,17 +69,11 @@ public class DataBaseLockManager extends AbstractLockManager implements Initiali @Override public boolean releaseGlobalSessionLock(GlobalSession globalSession) throws TransactionException { - List branchSessions = globalSession.getBranchSessions(); - if (CollectionUtils.isEmpty(branchSessions)) { - return true; - } - List branchIds = branchSessions.stream().map(BranchSession::getBranchId).collect(Collectors.toList()); try { - return getLocker().releaseLock(globalSession.getXid(), branchIds); + return getLocker().releaseLock(globalSession.getXid()); } catch (Exception t) { - LOGGER.error("unLock globalSession error, xid:{} branchIds:{}", globalSession.getXid(), - CollectionUtils.toString(branchIds), t); + LOGGER.error("unLock globalSession error, xid:{}", globalSession.getXid(), t); return false; } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/lock/DataBaseLocker.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/lock/DataBaseLocker.java index b0816d78d..f154b79a4 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/lock/DataBaseLocker.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/lock/DataBaseLocker.java @@ -15,16 +15,17 @@ */ package io.seata.server.storage.db.lock; -import java.util.List; -import javax.sql.DataSource; - import io.seata.common.exception.DataAccessException; import io.seata.common.exception.StoreException; import io.seata.common.util.CollectionUtils; import io.seata.core.lock.AbstractLocker; import io.seata.core.lock.RowLock; +import io.seata.core.model.LockStatus; import io.seata.core.store.LockStore; +import javax.sql.DataSource; +import java.util.List; + /** * The type Data base locker. * @@ -50,12 +51,17 @@ public class DataBaseLocker extends AbstractLocker { @Override public boolean acquireLock(List locks) { + return acquireLock(locks, true, false); + } + + @Override + public boolean acquireLock(List locks, boolean autoCommit, boolean skipCheckLock) { if (CollectionUtils.isEmpty(locks)) { // no lock return true; } try { - return lockStore.acquireLock(convertToLockDO(locks)); + return lockStore.acquireLock(convertToLockDO(locks), autoCommit, skipCheckLock); } catch (StoreException e) { throw e; @@ -87,7 +93,7 @@ public class DataBaseLocker extends AbstractLocker { @Override public boolean releaseLock(String xid, Long branchId) { try { - return lockStore.unLock(xid, branchId); + return lockStore.unLock(branchId); } catch (StoreException e) { throw e; @@ -99,20 +105,15 @@ public class DataBaseLocker extends AbstractLocker { } @Override - public boolean releaseLock(String xid, List branchIds) { - if (CollectionUtils.isEmpty(branchIds)) { - // no lock - return true; - } + public boolean releaseLock(String xid) { try { - return lockStore.unLock(xid, branchIds); + return lockStore.unLock(xid); } catch (StoreException e) { throw e; } catch (Exception t) { - LOGGER.error("unLock by branchIds error, xid {}, branchIds:{}", xid, CollectionUtils.toString(branchIds), - t); + LOGGER.error("unLock by branchIds error, xid {}", xid, t); return false; } } @@ -135,6 +136,11 @@ public class DataBaseLocker extends AbstractLocker { } } + @Override + public void updateLockStatus(String xid, LockStatus lockStatus) { + lockStore.updateLockStatus(xid, lockStatus); + } + /** * Sets lock store. * @param lockStore the lock store diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/lock/LockStoreDataBaseDAO.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/lock/LockStoreDataBaseDAO.java index bbe9fd29e..b3475b11c 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/lock/LockStoreDataBaseDAO.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/lock/LockStoreDataBaseDAO.java @@ -15,18 +15,6 @@ */ package io.seata.server.storage.db.lock; -import javax.sql.DataSource; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.StringJoiner; -import java.util.stream.Collectors; - import io.seata.common.exception.DataAccessException; import io.seata.common.exception.StoreException; import io.seata.common.util.CollectionUtils; @@ -37,13 +25,24 @@ import io.seata.config.Configuration; import io.seata.config.ConfigurationFactory; import io.seata.core.constants.ConfigurationKeys; import io.seata.core.constants.ServerTableColumnsName; +import io.seata.core.exception.BranchTransactionException; +import io.seata.core.model.LockStatus; import io.seata.core.store.LockDO; import io.seata.core.store.LockStore; import io.seata.core.store.db.sql.lock.LockStoreSqlFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.sql.DataSource; +import java.sql.*; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + import static io.seata.common.DefaultValues.DEFAULT_LOCK_DB_TABLE; +import static io.seata.core.exception.TransactionExceptionCode.LockKeyConflictFailFast; /** * The type Data base lock store. @@ -97,6 +96,11 @@ public class LockStoreDataBaseDAO implements LockStore { @Override public boolean acquireLock(List lockDOs) { + return acquireLock(lockDOs, true, false); + } + + @Override + public boolean acquireLock(List lockDOs, boolean autoCommit, boolean skipCheckLock) { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; @@ -111,53 +115,63 @@ public class LockStoreDataBaseDAO implements LockStore { if (originalAutoCommit = conn.getAutoCommit()) { conn.setAutoCommit(false); } + List unrepeatedLockDOs = lockDOs; + // check lock - StringJoiner sj = new StringJoiner(","); - for (int i = 0; i < lockDOs.size(); i++) { - sj.add("?"); - } - boolean canLock = true; - // query - String checkLockSQL = LockStoreSqlFactory.getLogStoreSql(dbType).getCheckLockableSql(lockTable, - sj.toString()); - ps = conn.prepareStatement(checkLockSQL); - for (int i = 0; i < lockDOs.size(); i++) { - ps.setString(i + 1, lockDOs.get(i).getRowKey()); - } - rs = ps.executeQuery(); - String currentXID = lockDOs.get(0).getXid(); - while (rs.next()) { - String dbXID = rs.getString(ServerTableColumnsName.LOCK_TABLE_XID); - if (!StringUtils.equals(dbXID, currentXID)) { - if (LOGGER.isInfoEnabled()) { - String dbPk = rs.getString(ServerTableColumnsName.LOCK_TABLE_PK); - String dbTableName = rs.getString(ServerTableColumnsName.LOCK_TABLE_TABLE_NAME); - Long dbBranchId = rs.getLong(ServerTableColumnsName.LOCK_TABLE_BRANCH_ID); - LOGGER.info("Global lock on [{}:{}] is holding by xid {} branchId {}", dbTableName, dbPk, dbXID, - dbBranchId); - } - canLock &= false; - break; + if (!skipCheckLock) { + + boolean canLock = true; + // query + String checkLockSQL = LockStoreSqlFactory.getLogStoreSql(dbType).getCheckLockableSql(lockTable, + lockDOs.size()); + ps = conn.prepareStatement(checkLockSQL); + for (int i = 0; i < lockDOs.size(); i++) { + ps.setString(i + 1, lockDOs.get(i).getRowKey()); + } + rs = ps.executeQuery(); + String currentXID = lockDOs.get(0).getXid(); + boolean failFast = false; + while (rs.next()) { + String dbXID = rs.getString(ServerTableColumnsName.LOCK_TABLE_XID); + if (!StringUtils.equals(dbXID, currentXID)) { + if (LOGGER.isInfoEnabled()) { + String dbPk = rs.getString(ServerTableColumnsName.LOCK_TABLE_PK); + String dbTableName = rs.getString(ServerTableColumnsName.LOCK_TABLE_TABLE_NAME); + long dbBranchId = rs.getLong(ServerTableColumnsName.LOCK_TABLE_BRANCH_ID); + LOGGER.info("Global lock on [{}:{}] is holding by xid {} branchId {}", dbTableName, dbPk, + dbXID, dbBranchId); + } + if (!autoCommit) { + int status = rs.getInt(ServerTableColumnsName.LOCK_TABLE_STATUS); + if (status == LockStatus.Rollbacking.getCode()) { + failFast = true; + } + } + canLock = false; + break; + } + + dbExistedRowKeys.add(rs.getString(ServerTableColumnsName.LOCK_TABLE_ROW_KEY)); + } + if (!canLock) { + conn.rollback(); + if (failFast) { + throw new StoreException(new BranchTransactionException(LockKeyConflictFailFast)); + } + return false; + } + // If the lock has been exists in db, remove it from the lockDOs + if (CollectionUtils.isNotEmpty(dbExistedRowKeys)) { + unrepeatedLockDOs = lockDOs.stream() + .filter(lockDO -> !dbExistedRowKeys.contains(lockDO.getRowKey())) + .collect(Collectors.toList()); + } + if (CollectionUtils.isEmpty(unrepeatedLockDOs)) { + conn.rollback(); + return true; } - dbExistedRowKeys.add(rs.getString(ServerTableColumnsName.LOCK_TABLE_ROW_KEY)); } - if (!canLock) { - conn.rollback(); - return false; - } - List unrepeatedLockDOs = null; - if (CollectionUtils.isNotEmpty(dbExistedRowKeys)) { - unrepeatedLockDOs = lockDOs.stream().filter(lockDO -> !dbExistedRowKeys.contains(lockDO.getRowKey())) - .collect(Collectors.toList()); - } - else { - unrepeatedLockDOs = lockDOs; - } - if (CollectionUtils.isEmpty(unrepeatedLockDOs)) { - conn.rollback(); - return true; - } // lock if (unrepeatedLockDOs.size() == 1) { LockDO lockDO = unrepeatedLockDOs.get(0); @@ -215,13 +229,9 @@ public class LockStoreDataBaseDAO implements LockStore { conn = lockStoreDataSource.getConnection(); conn.setAutoCommit(true); - StringJoiner sj = new StringJoiner(","); - for (int i = 0; i < lockDOs.size(); i++) { - sj.add("?"); - } // batch release lock String batchDeleteSQL = LockStoreSqlFactory.getLogStoreSql(dbType).getBatchDeleteLockSql(lockTable, - sj.toString()); + lockDOs.size()); ps = conn.prepareStatement(batchDeleteSQL); ps.setString(1, lockDOs.get(0).getXid()); for (int i = 0; i < lockDOs.size(); i++) { @@ -239,17 +249,16 @@ public class LockStoreDataBaseDAO implements LockStore { } @Override - public boolean unLock(String xid, Long branchId) { + public boolean unLock(String xid) { Connection conn = null; PreparedStatement ps = null; try { conn = lockStoreDataSource.getConnection(); conn.setAutoCommit(true); - // batch release lock by branch - String batchDeleteSQL = LockStoreSqlFactory.getLogStoreSql(dbType).getBatchDeleteLockSqlByBranch(lockTable); + // batch release lock by branch list + String batchDeleteSQL = LockStoreSqlFactory.getLogStoreSql(dbType).getBatchDeleteLockSqlByXid(lockTable); ps = conn.prepareStatement(batchDeleteSQL); ps.setString(1, xid); - ps.setLong(2, branchId); ps.executeUpdate(); } catch (SQLException e) { @@ -262,22 +271,17 @@ public class LockStoreDataBaseDAO implements LockStore { } @Override - public boolean unLock(String xid, List branchIds) { + public boolean unLock(Long branchId) { Connection conn = null; PreparedStatement ps = null; try { conn = lockStoreDataSource.getConnection(); conn.setAutoCommit(true); - StringJoiner sj = new StringJoiner(","); - branchIds.forEach(branchId -> sj.add("?")); - // batch release lock by branch list - String batchDeleteSQL = LockStoreSqlFactory.getLogStoreSql(dbType).getBatchDeleteLockSqlByBranchs(lockTable, - sj.toString()); + // batch release lock by branchId + String batchDeleteSQL = LockStoreSqlFactory.getLogStoreSql(dbType) + .getBatchDeleteLockSqlByBranchId(lockTable); ps = conn.prepareStatement(batchDeleteSQL); - ps.setString(1, xid); - for (int i = 0; i < branchIds.size(); i++) { - ps.setLong(i + 2, branchIds.get(i)); - } + ps.setLong(1, branchId); ps.executeUpdate(); } catch (SQLException e) { @@ -308,6 +312,22 @@ public class LockStoreDataBaseDAO implements LockStore { } } + @Override + public void updateLockStatus(String xid, LockStatus lockStatus) { + String updateStatusLockByGlobalSql = LockStoreSqlFactory.getLogStoreSql(dbType) + .getBatchUpdateStatusLockByGlobalSql(lockTable); + try (Connection conn = lockStoreDataSource.getConnection(); + PreparedStatement ps = conn.prepareStatement(updateStatusLockByGlobalSql)) { + conn.setAutoCommit(true); + ps.setInt(1, lockStatus.getCode()); + ps.setString(2, xid); + ps.executeUpdate(); + } + catch (SQLException e) { + throw new DataAccessException(e); + } + } + /** * Do acquire lock boolean. * @param conn the conn @@ -327,9 +347,13 @@ public class LockStoreDataBaseDAO implements LockStore { ps.setString(5, lockDO.getTableName()); ps.setString(6, lockDO.getPk()); ps.setString(7, lockDO.getRowKey()); + ps.setInt(8, LockStatus.Locked.getCode()); return ps.executeUpdate() > 0; } catch (SQLException e) { + if (e instanceof SQLIntegrityConstraintViolationException) { + return false; + } throw new StoreException(e); } finally { @@ -343,7 +367,7 @@ public class LockStoreDataBaseDAO implements LockStore { * @param lockDOs the lock do list * @return the boolean */ - protected boolean doAcquireLocks(Connection conn, List lockDOs) { + protected boolean doAcquireLocks(Connection conn, List lockDOs) throws SQLException { PreparedStatement ps = null; try { // insert @@ -357,15 +381,19 @@ public class LockStoreDataBaseDAO implements LockStore { ps.setString(5, lockDO.getTableName()); ps.setString(6, lockDO.getPk()); ps.setString(7, lockDO.getRowKey()); + ps.setInt(8, lockDO.getStatus()); ps.addBatch(); } return ps.executeBatch().length == lockDOs.size(); } - catch (SQLException e) { + catch (SQLIntegrityConstraintViolationException e) { LOGGER.error("Global lock batch acquire error: {}", e.getMessage(), e); // return false,let the caller go to conn.rollabck() return false; } + catch (SQLException e) { + throw e; + } finally { IOUtil.close(ps); } @@ -381,14 +409,9 @@ public class LockStoreDataBaseDAO implements LockStore { PreparedStatement ps = null; ResultSet rs = null; try { - StringJoiner sj = new StringJoiner(","); - for (int i = 0; i < lockDOs.size(); i++) { - sj.add("?"); - } - // query String checkLockSQL = LockStoreSqlFactory.getLogStoreSql(dbType).getCheckLockableSql(lockTable, - sj.toString()); + lockDOs.size()); ps = conn.prepareStatement(checkLockSQL); for (int i = 0; i < lockDOs.size(); i++) { ps.setString(i + 1, lockDOs.get(i).getRowKey()); diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/session/DataBaseSessionManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/session/DataBaseSessionManager.java index 95c62077b..ce8778346 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/session/DataBaseSessionManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/session/DataBaseSessionManager.java @@ -15,27 +15,23 @@ */ package io.seata.server.storage.db.session; -import java.util.Collection; -import java.util.List; - import io.seata.common.exception.StoreException; import io.seata.common.executor.Initialize; import io.seata.common.loader.LoadLevel; +import io.seata.common.loader.Scope; import io.seata.common.util.StringUtils; import io.seata.core.exception.TransactionException; import io.seata.core.model.BranchStatus; import io.seata.core.model.GlobalStatus; -import io.seata.server.session.AbstractSessionManager; -import io.seata.server.session.BranchSession; -import io.seata.server.session.GlobalSession; -import io.seata.server.session.SessionCondition; -import io.seata.server.session.SessionHolder; +import io.seata.server.session.*; import io.seata.server.storage.db.store.DataBaseTransactionStoreManager; import io.seata.server.store.TransactionStoreManager.LogOperation; -import io.seata.common.loader.Scope; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Collection; +import java.util.List; + /** * The Data base session manager. * @@ -108,7 +104,7 @@ public class DataBaseSessionManager extends AbstractSessionManager implements In * retryCommitSessionManager and retryRollbackSessionManager remove retry expired * globalSession * @param session the session - * @throws TransactionException + * @throws TransactionException the transaction exception */ @Override public void removeGlobalSession(GlobalSession session) throws TransactionException { @@ -168,19 +164,18 @@ public class DataBaseSessionManager extends AbstractSessionManager implements In return findGlobalSessions(new SessionCondition(GlobalStatus.AsyncCommitting)); } else if (SessionHolder.RETRY_COMMITTING_SESSION_MANAGER_NAME.equalsIgnoreCase(taskName)) { - return findGlobalSessions( - new SessionCondition(new GlobalStatus[] { GlobalStatus.CommitRetrying, GlobalStatus.Committing })); + return findGlobalSessions(new SessionCondition(GlobalStatus.CommitRetrying, GlobalStatus.Committing)); } else if (SessionHolder.RETRY_ROLLBACKING_SESSION_MANAGER_NAME.equalsIgnoreCase(taskName)) { - return findGlobalSessions(new SessionCondition(new GlobalStatus[] { GlobalStatus.RollbackRetrying, - GlobalStatus.Rollbacking, GlobalStatus.TimeoutRollbacking, GlobalStatus.TimeoutRollbackRetrying })); + return findGlobalSessions(new SessionCondition(GlobalStatus.RollbackRetrying, GlobalStatus.Rollbacking, + GlobalStatus.TimeoutRollbacking, GlobalStatus.TimeoutRollbackRetrying)); } else { // all data - return findGlobalSessions(new SessionCondition(new GlobalStatus[] { GlobalStatus.UnKnown, - GlobalStatus.Begin, GlobalStatus.Committing, GlobalStatus.CommitRetrying, GlobalStatus.Rollbacking, + return findGlobalSessions(new SessionCondition(GlobalStatus.UnKnown, GlobalStatus.Begin, + GlobalStatus.Committing, GlobalStatus.CommitRetrying, GlobalStatus.Rollbacking, GlobalStatus.RollbackRetrying, GlobalStatus.TimeoutRollbacking, - GlobalStatus.TimeoutRollbackRetrying, GlobalStatus.AsyncCommitting })); + GlobalStatus.TimeoutRollbackRetrying, GlobalStatus.AsyncCommitting)); } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/store/DataBaseTransactionStoreManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/store/DataBaseTransactionStoreManager.java index bb7b49bd2..f399ce042 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/store/DataBaseTransactionStoreManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/store/DataBaseTransactionStoreManager.java @@ -15,13 +15,6 @@ */ package io.seata.server.storage.db.store; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import javax.sql.DataSource; - import io.seata.common.exception.StoreException; import io.seata.common.loader.EnhancedServiceLoader; import io.seata.common.util.CollectionUtils; @@ -36,10 +29,16 @@ import io.seata.core.store.LogStore; import io.seata.core.store.db.DataSourceProvider; import io.seata.server.session.GlobalSession; import io.seata.server.session.SessionCondition; +import io.seata.server.storage.SessionConverter; import io.seata.server.store.AbstractTransactionStoreManager; import io.seata.server.store.SessionStorable; import io.seata.server.store.TransactionStoreManager; -import io.seata.server.storage.SessionConverter; + +import javax.sql.DataSource; +import java.util.*; +import java.util.stream.Collectors; + +import static io.seata.common.DefaultValues.DEFAULT_QUERY_LIMIT; /** * The type Database transaction store manager. @@ -56,11 +55,6 @@ public class DataBaseTransactionStoreManager extends AbstractTransactionStoreMan */ protected static final Configuration CONFIG = ConfigurationFactory.getInstance(); - /** - * The constant DEFAULT_LOG_QUERY_LIMIT. - */ - protected static final int DEFAULT_LOG_QUERY_LIMIT = 100; - /** * The Log store. */ @@ -89,7 +83,7 @@ public class DataBaseTransactionStoreManager extends AbstractTransactionStoreMan * Instantiates a new Database transaction store manager. */ private DataBaseTransactionStoreManager() { - logQueryLimit = CONFIG.getInt(ConfigurationKeys.STORE_DB_LOG_QUERY_LIMIT, DEFAULT_LOG_QUERY_LIMIT); + logQueryLimit = CONFIG.getInt(ConfigurationKeys.STORE_DB_LOG_QUERY_LIMIT, DEFAULT_QUERY_LIMIT); String datasourceType = CONFIG.getConfig(ConfigurationKeys.STORE_DB_DATASOURCE_TYPE); // init dataSource DataSource logStoreDataSource = EnhancedServiceLoader.load(DataSourceProvider.class, datasourceType).provide(); @@ -170,27 +164,39 @@ public class DataBaseTransactionStoreManager extends AbstractTransactionStoreMan return getGlobalSession(globalTransactionDO, branchTransactionDOs); } + @Override + public List readSortByTimeoutBeginSessions(boolean withBranchSessions) { + return readSession(new GlobalStatus[] { GlobalStatus.Begin }, withBranchSessions); + } + /** * Read session list. * @param statuses the statuses * @return the list */ - public List readSession(GlobalStatus[] statuses) { + @Override + public List readSession(GlobalStatus[] statuses, boolean withBranchSessions) { int[] states = new int[statuses.length]; for (int i = 0; i < statuses.length; i++) { states[i] = statuses[i].getCode(); } // global transaction List globalTransactionDOs = logStore.queryGlobalTransactionDO(states, logQueryLimit); - if (CollectionUtils.isEmpty(globalTransactionDOs)) { - return null; + Map> branchTransactionDOsMap = Collections.emptyMap(); + if (CollectionUtils.isNotEmpty(globalTransactionDOs)) { + List xids = globalTransactionDOs.stream().map(GlobalTransactionDO::getXid) + .collect(Collectors.toList()); + if (withBranchSessions) { + List branchTransactionDOs = logStore.queryBranchTransactionDO(xids); + branchTransactionDOsMap = branchTransactionDOs.stream().collect( + Collectors.groupingBy(BranchTransactionDO::getXid, LinkedHashMap::new, Collectors.toList())); + } } - List xids = globalTransactionDOs.stream().map(GlobalTransactionDO::getXid).collect(Collectors.toList()); - List branchTransactionDOs = logStore.queryBranchTransactionDO(xids); - Map> branchTransactionDOsMap = branchTransactionDOs.stream() - .collect(Collectors.groupingBy(BranchTransactionDO::getXid, LinkedHashMap::new, Collectors.toList())); - return globalTransactionDOs.stream().map(globalTransactionDO -> getGlobalSession(globalTransactionDO, - branchTransactionDOsMap.get(globalTransactionDO.getXid()))).collect(Collectors.toList()); + Map> finalBranchTransactionDOsMap = branchTransactionDOsMap; + return globalTransactionDOs.stream() + .map(globalTransactionDO -> getGlobalSession(globalTransactionDO, + finalBranchTransactionDOsMap.get(globalTransactionDO.getXid()), withBranchSessions)) + .collect(Collectors.toList()); } @Override @@ -212,14 +218,19 @@ public class DataBaseTransactionStoreManager extends AbstractTransactionStoreMan } } else if (CollectionUtils.isNotEmpty(sessionCondition.getStatuses())) { - return readSession(sessionCondition.getStatuses()); + return readSession(sessionCondition.getStatuses(), !sessionCondition.isLazyLoadBranch()); } return null; } private GlobalSession getGlobalSession(GlobalTransactionDO globalTransactionDO, List branchTransactionDOs) { - GlobalSession globalSession = SessionConverter.convertGlobalSession(globalTransactionDO); + return getGlobalSession(globalTransactionDO, branchTransactionDOs, true); + } + + private GlobalSession getGlobalSession(GlobalTransactionDO globalTransactionDO, + List branchTransactionDOs, boolean withBranchSessions) { + GlobalSession globalSession = SessionConverter.convertGlobalSession(globalTransactionDO, !withBranchSessions); // branch transactions if (CollectionUtils.isNotEmpty(branchTransactionDOs)) { for (BranchTransactionDO branchTransactionDO : branchTransactionDOs) { diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/store/LogStoreDataBaseDAO.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/store/LogStoreDataBaseDAO.java index bc613e982..1d2ec5475 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/store/LogStoreDataBaseDAO.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/store/LogStoreDataBaseDAO.java @@ -15,15 +15,6 @@ */ package io.seata.server.storage.db.store; -import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; -import javax.sql.DataSource; - import io.seata.common.exception.DataAccessException; import io.seata.common.exception.StoreException; import io.seata.common.util.IOUtil; @@ -39,6 +30,11 @@ import io.seata.core.store.db.sql.log.LogStoreSqlsFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.sql.DataSource; +import java.sql.*; +import java.util.ArrayList; +import java.util.List; + import static io.seata.common.DefaultValues.DEFAULT_STORE_DB_BRANCH_TABLE; import static io.seata.common.DefaultValues.DEFAULT_STORE_DB_GLOBAL_TABLE; @@ -199,21 +195,22 @@ public class LogStoreDataBaseDAO implements LogStore { Connection conn = null; PreparedStatement ps = null; try { + int index = 1; conn = logStoreDataSource.getConnection(); conn.setAutoCommit(true); ps = conn.prepareStatement(sql); - ps.setString(1, globalTransactionDO.getXid()); - ps.setLong(2, globalTransactionDO.getTransactionId()); - ps.setInt(3, globalTransactionDO.getStatus()); - ps.setString(4, globalTransactionDO.getApplicationId()); - ps.setString(5, globalTransactionDO.getTransactionServiceGroup()); + ps.setString(index++, globalTransactionDO.getXid()); + ps.setLong(index++, globalTransactionDO.getTransactionId()); + ps.setInt(index++, globalTransactionDO.getStatus()); + ps.setString(index++, globalTransactionDO.getApplicationId()); + ps.setString(index++, globalTransactionDO.getTransactionServiceGroup()); String transactionName = globalTransactionDO.getTransactionName(); transactionName = transactionName.length() > transactionNameColumnSize ? transactionName.substring(0, transactionNameColumnSize) : transactionName; - ps.setString(6, transactionName); - ps.setInt(7, globalTransactionDO.getTimeout()); - ps.setLong(8, globalTransactionDO.getBeginTime()); - ps.setString(9, globalTransactionDO.getApplicationData()); + ps.setString(index++, transactionName); + ps.setInt(index++, globalTransactionDO.getTimeout()); + ps.setLong(index++, globalTransactionDO.getBeginTime()); + ps.setString(index++, globalTransactionDO.getApplicationData()); return ps.executeUpdate() > 0; } catch (SQLException e) { @@ -230,11 +227,12 @@ public class LogStoreDataBaseDAO implements LogStore { Connection conn = null; PreparedStatement ps = null; try { + int index = 1; conn = logStoreDataSource.getConnection(); conn.setAutoCommit(true); ps = conn.prepareStatement(sql); - ps.setInt(1, globalTransactionDO.getStatus()); - ps.setString(2, globalTransactionDO.getXid()); + ps.setInt(index++, globalTransactionDO.getStatus()); + ps.setString(index++, globalTransactionDO.getXid()); return ps.executeUpdate() > 0; } catch (SQLException e) { @@ -331,18 +329,19 @@ public class LogStoreDataBaseDAO implements LogStore { Connection conn = null; PreparedStatement ps = null; try { + int index = 1; conn = logStoreDataSource.getConnection(); conn.setAutoCommit(true); ps = conn.prepareStatement(sql); - ps.setString(1, branchTransactionDO.getXid()); - ps.setLong(2, branchTransactionDO.getTransactionId()); - ps.setLong(3, branchTransactionDO.getBranchId()); - ps.setString(4, branchTransactionDO.getResourceGroupId()); - ps.setString(5, branchTransactionDO.getResourceId()); - ps.setString(6, branchTransactionDO.getBranchType()); - ps.setInt(7, branchTransactionDO.getStatus()); - ps.setString(8, branchTransactionDO.getClientId()); - ps.setString(9, branchTransactionDO.getApplicationData()); + ps.setString(index++, branchTransactionDO.getXid()); + ps.setLong(index++, branchTransactionDO.getTransactionId()); + ps.setLong(index++, branchTransactionDO.getBranchId()); + ps.setString(index++, branchTransactionDO.getResourceGroupId()); + ps.setString(index++, branchTransactionDO.getResourceId()); + ps.setString(index++, branchTransactionDO.getBranchType()); + ps.setInt(index++, branchTransactionDO.getStatus()); + ps.setString(index++, branchTransactionDO.getClientId()); + ps.setString(index++, branchTransactionDO.getApplicationData()); return ps.executeUpdate() > 0; } catch (SQLException e) { @@ -355,16 +354,23 @@ public class LogStoreDataBaseDAO implements LogStore { @Override public boolean updateBranchTransactionDO(BranchTransactionDO branchTransactionDO) { - String sql = LogStoreSqlsFactory.getLogStoreSqls(dbType).getUpdateBranchTransactionStatusSQL(branchTable); + boolean shouldUpdateAppData = StringUtils.isNotBlank(branchTransactionDO.getApplicationData()); + String sql = shouldUpdateAppData + ? LogStoreSqlsFactory.getLogStoreSqls(dbType).getUpdateBranchTransactionStatusAppDataSQL(branchTable) + : LogStoreSqlsFactory.getLogStoreSqls(dbType).getUpdateBranchTransactionStatusSQL(branchTable); Connection conn = null; PreparedStatement ps = null; try { + int index = 1; conn = logStoreDataSource.getConnection(); conn.setAutoCommit(true); ps = conn.prepareStatement(sql); - ps.setInt(1, branchTransactionDO.getStatus()); - ps.setString(2, branchTransactionDO.getXid()); - ps.setLong(3, branchTransactionDO.getBranchId()); + ps.setInt(index++, branchTransactionDO.getStatus()); + if (shouldUpdateAppData) { + ps.setString(index++, branchTransactionDO.getApplicationData()); + } + ps.setString(index++, branchTransactionDO.getXid()); + ps.setLong(index++, branchTransactionDO.getBranchId()); return ps.executeUpdate() > 0; } catch (SQLException e) { @@ -403,7 +409,7 @@ public class LogStoreDataBaseDAO implements LogStore { String branchMaxSql = LogStoreSqlsFactory.getLogStoreSqls(dbType).getQueryBranchMax(branchTable); long maxTransId = getCurrentMaxSessionId(transMaxSql, high, low); long maxBranchId = getCurrentMaxSessionId(branchMaxSql, high, low); - return maxBranchId > maxTransId ? maxBranchId : maxTransId; + return Math.max(maxBranchId, maxTransId); } private long getCurrentMaxSessionId(String sql, long high, long low) { @@ -412,11 +418,12 @@ public class LogStoreDataBaseDAO implements LogStore { PreparedStatement ps = null; ResultSet rs = null; try { + int index = 1; conn = logStoreDataSource.getConnection(); conn.setAutoCommit(true); ps = conn.prepareStatement(sql); - ps.setLong(1, high); - ps.setLong(2, low); + ps.setLong(index++, high); + ps.setLong(index++, low); rs = ps.executeQuery(); while (rs.next()) { diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/TransactionWriteStore.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/TransactionWriteStore.java index 315d21dac..7c9c7d220 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/TransactionWriteStore.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/TransactionWriteStore.java @@ -15,14 +15,14 @@ */ package io.seata.server.storage.file; -import java.nio.ByteBuffer; - import io.seata.common.exception.ShouldNeverHappenException; import io.seata.server.session.BranchSession; import io.seata.server.session.GlobalSession; import io.seata.server.store.SessionStorable; import io.seata.server.store.TransactionStoreManager.LogOperation; +import java.nio.ByteBuffer; + /** * The type Transaction write store. * diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/lock/FileLockManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/lock/FileLockManager.java index 3899d2bcc..efb288e6d 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/lock/FileLockManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/lock/FileLockManager.java @@ -15,8 +15,6 @@ */ package io.seata.server.storage.file.lock; -import java.util.ArrayList; - import io.seata.common.loader.LoadLevel; import io.seata.core.exception.TransactionException; import io.seata.core.lock.Locker; @@ -25,6 +23,8 @@ import io.seata.server.session.BranchSession; import io.seata.server.session.GlobalSession; import org.slf4j.MDC; +import java.util.List; + import static io.seata.core.context.RootContext.MDC_KEY_BRANCH_ID; /** @@ -42,7 +42,7 @@ public class FileLockManager extends AbstractLockManager { @Override public boolean releaseGlobalSessionLock(GlobalSession globalSession) throws TransactionException { - ArrayList branchSessions = globalSession.getBranchSessions(); + List branchSessions = globalSession.getBranchSessions(); boolean releaseLockResult = true; for (BranchSession branchSession : branchSessions) { try { diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/lock/FileLocker.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/lock/FileLocker.java index c348446e6..4ae03b96b 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/lock/FileLocker.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/lock/FileLocker.java @@ -15,19 +15,23 @@ */ package io.seata.server.storage.file.lock; +import io.seata.common.exception.FrameworkException; +import io.seata.common.exception.StoreException; +import io.seata.common.util.CollectionUtils; +import io.seata.core.exception.BranchTransactionException; +import io.seata.core.exception.TransactionException; +import io.seata.core.lock.AbstractLocker; +import io.seata.core.lock.RowLock; +import io.seata.core.model.LockStatus; +import io.seata.server.session.BranchSession; + import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import io.netty.util.internal.ConcurrentSet; -import io.seata.common.exception.FrameworkException; -import io.seata.common.util.CollectionUtils; -import io.seata.core.exception.TransactionException; -import io.seata.core.lock.AbstractLocker; -import io.seata.core.lock.RowLock; -import io.seata.server.session.BranchSession; +import static io.seata.core.exception.TransactionExceptionCode.LockKeyConflictFailFast; /** * The type Memory locker. @@ -47,7 +51,7 @@ public class FileLocker extends AbstractLocker { /** * The Branch session. */ - protected BranchSession branchSession = null; + protected BranchSession branchSession; /** * Instantiates a new Memory locker. @@ -59,6 +63,11 @@ public class FileLocker extends AbstractLocker { @Override public boolean acquireLock(List rowLocks) { + return acquireLock(rowLocks, true, false); + } + + @Override + public boolean acquireLock(List rowLocks, boolean autoCommit, boolean skipCheckLock) { if (CollectionUtils.isEmpty(rowLocks)) { // no lock return true; @@ -69,7 +78,8 @@ public class FileLocker extends AbstractLocker { ConcurrentMap> bucketHolder = branchSession.getLockHolder(); ConcurrentMap> dbLockMap = CollectionUtils .computeIfAbsent(LOCK_MAP, resourceId, key -> new ConcurrentHashMap<>()); - + boolean failFast = false; + boolean canLock = true; for (RowLock lock : rowLocks) { String tableName = lock.getTableName(); String pk = lock.getPk(); @@ -79,19 +89,20 @@ public class FileLocker extends AbstractLocker { int bucketId = pk.hashCode() % BUCKET_PER_TABLE; BucketLockMap bucketLockMap = CollectionUtils.computeIfAbsent(tableLockMap, bucketId, key -> new BucketLockMap()); - Long previousLockTransactionId = bucketLockMap.get().putIfAbsent(pk, transactionId); - if (previousLockTransactionId == null) { + BranchSession previousLockBranchSession = bucketLockMap.get().putIfAbsent(pk, branchSession); + if (previousLockBranchSession == null) { // No existing lock, and now locked by myself Set keysInHolder = CollectionUtils.computeIfAbsent(bucketHolder, bucketLockMap, - key -> new ConcurrentSet<>()); + key -> ConcurrentHashMap.newKeySet()); keysInHolder.add(pk); } - else if (previousLockTransactionId == transactionId) { + else if (previousLockBranchSession.getTransactionId() == transactionId) { // Locked by me before continue; } else { - LOGGER.info("Global lock on [" + tableName + ":" + pk + "] is holding by " + previousLockTransactionId); + LOGGER.info("Global lock on [" + tableName + ":" + pk + "] is holding by " + + previousLockBranchSession.getBranchId()); try { // Release all acquired locks. branchSession.unlock(); @@ -99,10 +110,22 @@ public class FileLocker extends AbstractLocker { catch (TransactionException e) { throw new FrameworkException(e); } - return false; + if (!autoCommit && previousLockBranchSession.getLockStatus() == LockStatus.Rollbacking) { + failFast = true; + break; + } + if (canLock) { + canLock = false; + if (autoCommit) { + break; + } + } } } - return true; + if (failFast) { + throw new StoreException(new BranchTransactionException(LockKeyConflictFailFast)); + } + return canLock; } @Override @@ -120,7 +143,7 @@ public class FileLocker extends AbstractLocker { Set keys = entry.getValue(); for (String key : keys) { // remove lock only if it locked by myself - bucket.get().remove(key, branchSession.getTransactionId()); + bucket.get().remove(key, branchSession); } } lockHolder.clear(); @@ -152,7 +175,8 @@ public class FileLocker extends AbstractLocker { if (bucketLockMap == null) { continue; } - Long lockingTransactionId = bucketLockMap.get().get(pk); + BranchSession branchSession = bucketLockMap.get().get(pk); + Long lockingTransactionId = branchSession != null ? branchSession.getTransactionId() : null; if (lockingTransactionId == null || lockingTransactionId.longValue() == transactionId) { // Locked by me continue; @@ -165,6 +189,10 @@ public class FileLocker extends AbstractLocker { return true; } + @Override + public void updateLockStatus(String xid, LockStatus lockStatus) { + } + @Override public void cleanAllLocks() { LOCK_MAP.clear(); @@ -172,16 +200,16 @@ public class FileLocker extends AbstractLocker { /** * Because bucket lock map will be key of HashMap(lockHolder), however - * {@link ConcurrentHashMap} overwrites {@link Object##hashCode()} and - * {@link Object##equals(Object)}, that leads to hash key conflict in lockHolder. We + * {@link ConcurrentHashMap} overwrites {@link Object#hashCode()} and + * {@link Object#equals(Object)}, that leads to hash key conflict in lockHolder. We * define a {@link BucketLockMap} to hold the ConcurrentHashMap(bucketLockMap) and * replace it as key of HashMap(lockHolder). */ public static class BucketLockMap { - private final ConcurrentHashMap bucketLockMap = new ConcurrentHashMap<>(); + private final ConcurrentHashMap bucketLockMap = new ConcurrentHashMap<>(); - ConcurrentHashMap get() { + ConcurrentHashMap get() { return bucketLockMap; } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/session/FileSessionManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/session/FileSessionManager.java index e19dd488a..9db69e033 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/session/FileSessionManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/session/FileSessionManager.java @@ -15,30 +15,16 @@ */ package io.seata.server.storage.file.session; -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - import io.seata.common.exception.ShouldNeverHappenException; import io.seata.common.loader.LoadLevel; import io.seata.common.loader.Scope; +import io.seata.common.util.CollectionUtils; import io.seata.common.util.StringUtils; import io.seata.config.ConfigurationFactory; import io.seata.core.constants.ConfigurationKeys; import io.seata.core.exception.TransactionException; import io.seata.core.model.GlobalStatus; -import io.seata.server.session.AbstractSessionManager; -import io.seata.server.session.BranchSession; -import io.seata.server.session.GlobalSession; -import io.seata.server.session.Reloadable; -import io.seata.server.session.SessionCondition; +import io.seata.server.session.*; import io.seata.server.storage.file.ReloadableStore; import io.seata.server.storage.file.TransactionWriteStore; import io.seata.server.storage.file.store.FileTransactionStoreManager; @@ -46,6 +32,13 @@ import io.seata.server.store.AbstractTransactionStoreManager; import io.seata.server.store.SessionStorable; import io.seata.server.store.TransactionStoreManager; +import java.io.File; +import java.io.IOException; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +import static io.seata.common.DefaultValues.DEFAULT_SERVICE_SESSION_RELOAD_READ_SIZE; + /** * The type File based session manager. * @@ -55,7 +48,7 @@ import io.seata.server.store.TransactionStoreManager; public class FileSessionManager extends AbstractSessionManager implements Reloadable { private static final int READ_SIZE = ConfigurationFactory.getInstance() - .getInt(ConfigurationKeys.SERVICE_SESSION_RELOAD_READ_SIZE, 100); + .getInt(ConfigurationKeys.SERVICE_SESSION_RELOAD_READ_SIZE, DEFAULT_SERVICE_SESSION_RELOAD_READ_SIZE); /** * The Session map. @@ -91,8 +84,15 @@ public class FileSessionManager extends AbstractSessionManager implements Reload @Override public void addGlobalSession(GlobalSession session) throws TransactionException { - super.addGlobalSession(session); - sessionMap.put(session.getXid(), session); + CollectionUtils.computeIfAbsent(sessionMap, session.getXid(), k -> { + try { + super.addGlobalSession(session); + } + catch (TransactionException e) { + LOGGER.error("addGlobalSession fail, msg: {}", e.getMessage()); + } + return session; + }); } @Override @@ -108,8 +108,9 @@ public class FileSessionManager extends AbstractSessionManager implements Reload @Override public void removeGlobalSession(GlobalSession session) throws TransactionException { - super.removeGlobalSession(session); - sessionMap.remove(session.getXid()); + if (sessionMap.remove(session.getXid()) != null) { + super.removeGlobalSession(session); + } } @Override @@ -120,10 +121,48 @@ public class FileSessionManager extends AbstractSessionManager implements Reload @Override public List findGlobalSessions(SessionCondition condition) { List found = new ArrayList<>(); + + List globalStatuses = null; + if (null != condition.getStatuses() && condition.getStatuses().length > 0) { + globalStatuses = Arrays.asList(condition.getStatuses()); + } for (GlobalSession globalSession : sessionMap.values()) { - if (System.currentTimeMillis() - globalSession.getBeginTime() > condition.getOverTimeAliveMills()) { - found.add(globalSession); + if (null != condition.getOverTimeAliveMills() && condition.getOverTimeAliveMills() > 0) { + if (System.currentTimeMillis() - globalSession.getBeginTime() <= condition.getOverTimeAliveMills()) { + continue; + } } + + if (!StringUtils.isEmpty(condition.getXid())) { + if (Objects.equals(condition.getXid(), globalSession.getXid())) { + // Only one will be found, just add and return + found.add(globalSession); + return found; + } + else { + continue; + } + } + + if (null != condition.getTransactionId() && condition.getTransactionId() > 0) { + if (Objects.equals(condition.getTransactionId(), globalSession.getTransactionId())) { + // Only one will be found, just add and return + found.add(globalSession); + return found; + } + else { + continue; + } + } + + if (null != globalStatuses) { + if (!globalStatuses.contains(globalSession.getStatus())) { + continue; + } + } + + // All test pass, add to resp + found.add(globalSession); } return found; } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/store/FileTransactionStoreManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/store/FileTransactionStoreManager.java index ce4707333..00bca3127 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/store/FileTransactionStoreManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/store/FileTransactionStoreManager.java @@ -15,6 +15,24 @@ */ package io.seata.server.storage.file.store; +import io.seata.common.exception.StoreException; +import io.seata.common.thread.NamedThreadFactory; +import io.seata.common.util.CollectionUtils; +import io.seata.server.session.BranchSession; +import io.seata.server.session.GlobalSession; +import io.seata.server.session.SessionCondition; +import io.seata.server.session.SessionManager; +import io.seata.server.storage.file.FlushDiskMode; +import io.seata.server.storage.file.ReloadableStore; +import io.seata.server.storage.file.TransactionWriteStore; +import io.seata.server.store.AbstractTransactionStoreManager; +import io.seata.server.store.SessionStorable; +import io.seata.server.store.StoreConfig; +import io.seata.server.store.TransactionStoreManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; + import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; @@ -24,32 +42,10 @@ import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.ReentrantLock; -import io.seata.common.exception.StoreException; -import io.seata.common.thread.NamedThreadFactory; -import io.seata.common.util.CollectionUtils; -import io.seata.server.session.BranchSession; -import io.seata.server.session.GlobalSession; -import io.seata.server.session.SessionCondition; -import io.seata.server.session.SessionManager; -import io.seata.server.store.AbstractTransactionStoreManager; -import io.seata.server.storage.file.FlushDiskMode; -import io.seata.server.storage.file.ReloadableStore; -import io.seata.server.store.SessionStorable; -import io.seata.server.store.StoreConfig; -import io.seata.server.store.TransactionStoreManager; -import io.seata.server.storage.file.TransactionWriteStore; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.slf4j.MDC; - import static io.seata.core.context.RootContext.MDC_KEY_BRANCH_ID; /** @@ -70,7 +66,7 @@ public class FileTransactionStoreManager extends AbstractTransactionStoreManager private static final int MAX_SHUTDOWN_RETRY = 3; - private static final int SHUTDOWN_CHECK_INTERNAL = 1 * 1000; + private static final int SHUTDOWN_CHECK_INTERVAL = 1 * 1000; private static final int MAX_WRITE_RETRY = 5; @@ -173,8 +169,8 @@ public class FileTransactionStoreManager extends AbstractTransactionStoreManager @Override public boolean writeSession(LogOperation logOperation, SessionStorable session) { - writeSessionLock.lock(); long curFileTrxNum; + writeSessionLock.lock(); try { if (!writeDataFile(new TransactionWriteStore(session, logOperation).encode())) { return false; @@ -332,7 +328,7 @@ public class FileTransactionStoreManager extends AbstractTransactionStoreManager while (!fileWriteExecutor.isTerminated() && retry < MAX_SHUTDOWN_RETRY) { ++retry; try { - Thread.sleep(SHUTDOWN_CHECK_INTERNAL); + Thread.sleep(SHUTDOWN_CHECK_INTERVAL); } catch (InterruptedException ignore) { } @@ -342,10 +338,12 @@ public class FileTransactionStoreManager extends AbstractTransactionStoreManager } } try { - currFileChannel.force(true); + if (currFileChannel.isOpen()) { + currFileChannel.force(true); + } } catch (IOException e) { - LOGGER.error("fileChannel force error{}", e.getMessage(), e); + LOGGER.error("fileChannel force error: {}", e.getMessage(), e); } closeFile(currRaf); } @@ -370,9 +368,9 @@ public class FileTransactionStoreManager extends AbstractTransactionStoreManager @Override public boolean hasRemaining(boolean isHistory) { - File file = null; + File file; RandomAccessFile raf = null; - long currentOffset = 0; + long currentOffset; if (isHistory) { file = new File(hisFullFileName); currentOffset = recoverHisOffset; diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/JedisPooledFactory.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/JedisPooledFactory.java index f7cab8a87..7b73af39a 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/JedisPooledFactory.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/JedisPooledFactory.java @@ -15,25 +15,21 @@ */ package io.seata.server.storage.redis; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.seata.common.util.ConfigTools; import io.seata.common.exception.RedisException; +import io.seata.common.util.ConfigTools; import io.seata.common.util.StringUtils; import io.seata.config.Configuration; import io.seata.config.ConfigurationFactory; import io.seata.core.constants.ConfigurationKeys; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import redis.clients.jedis.*; -import redis.clients.jedis.Jedis; -import redis.clients.jedis.JedisPool; -import redis.clients.jedis.JedisPoolAbstract; -import redis.clients.jedis.JedisPoolConfig; -import redis.clients.jedis.JedisSentinelPool; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import static io.seata.common.DefaultValues.*; /** * @author funkye @@ -51,12 +47,6 @@ public class JedisPooledFactory { private static final int PORT = 6379; - private static final int MINCONN = 1; - - private static final int MAXCONN = 10; - - private static final int MAXTOTAL = 100; - private static final int DATABASE = 0; private static final int SENTINEL_HOST_NUMBER = 3; @@ -71,8 +61,9 @@ public class JedisPooledFactory { if (jedisPool == null) { synchronized (JedisPooledFactory.class) { if (jedisPool == null) { + JedisPoolAbstract tempJedisPool = null; if (jedisPools != null && jedisPools.length > 0) { - jedisPool = jedisPools[0]; + tempJedisPool = jedisPools[0]; } else { String password = CONFIGURATION.getConfig(ConfigurationKeys.STORE_REDIS_PASSWORD); @@ -93,9 +84,12 @@ public class JedisPooledFactory { } } JedisPoolConfig poolConfig = new JedisPoolConfig(); - poolConfig.setMinIdle(CONFIGURATION.getInt(ConfigurationKeys.STORE_REDIS_MIN_CONN, MINCONN)); - poolConfig.setMaxIdle(CONFIGURATION.getInt(ConfigurationKeys.STORE_REDIS_MAX_CONN, MAXCONN)); - poolConfig.setMaxTotal(CONFIGURATION.getInt(ConfigurationKeys.STORE_REDIS_MAX_TOTAL, MAXTOTAL)); + poolConfig.setMinIdle( + CONFIGURATION.getInt(ConfigurationKeys.STORE_REDIS_MIN_CONN, DEFAULT_REDIS_MIN_IDLE)); + poolConfig.setMaxIdle( + CONFIGURATION.getInt(ConfigurationKeys.STORE_REDIS_MAX_CONN, DEFAULT_REDIS_MAX_IDLE)); + poolConfig.setMaxTotal( + CONFIGURATION.getInt(ConfigurationKeys.STORE_REDIS_MAX_TOTAL, DEFAULT_REDIS_MAX_TOTAL)); String mode = CONFIGURATION.getConfig(ConfigurationKeys.STORE_REDIS_MODE, ConfigurationKeys.REDIS_SINGLE_MODE); if (mode.equals(ConfigurationKeys.REDIS_SENTINEL_MODE)) { @@ -108,7 +102,7 @@ public class JedisPooledFactory { String[] sentinelHosts = CONFIGURATION .getConfig(ConfigurationKeys.STORE_REDIS_SENTINEL_HOST).split(","); Arrays.asList(sentinelHosts).forEach(sentinelHost -> sentinels.add(sentinelHost)); - jedisPool = new JedisSentinelPool(masterName, sentinels, poolConfig, 60000, password, + tempJedisPool = new JedisSentinelPool(masterName, sentinels, poolConfig, 60000, password, CONFIGURATION.getInt(ConfigurationKeys.STORE_REDIS_DATABASE, DATABASE)); } else if (mode.equals(ConfigurationKeys.REDIS_SINGLE_MODE)) { @@ -117,7 +111,7 @@ public class JedisPooledFactory { ? CONFIGURATION.getConfig(ConfigurationKeys.STORE_REDIS_HOST, HOST) : host; int port = CONFIGURATION.getInt(ConfigurationKeys.STORE_REDIS_SINGLE_PORT); port = port == 0 ? CONFIGURATION.getInt(ConfigurationKeys.STORE_REDIS_PORT, PORT) : port; - jedisPool = new JedisPool(poolConfig, host, port, 60000, password, + tempJedisPool = new JedisPool(poolConfig, host, port, 60000, password, CONFIGURATION.getInt(ConfigurationKeys.STORE_REDIS_DATABASE, DATABASE)); } else { @@ -127,6 +121,7 @@ public class JedisPooledFactory { if (LOGGER.isInfoEnabled()) { LOGGER.info("initialization of the build redis connection pool is complete"); } + jedisPool = tempJedisPool; } } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/lock/RedisDistributedLocker.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/lock/RedisDistributedLocker.java new file mode 100644 index 000000000..9d9b93470 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/lock/RedisDistributedLocker.java @@ -0,0 +1,90 @@ +/* + * 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.storage.redis.lock; + +import io.seata.common.loader.LoadLevel; +import io.seata.common.loader.Scope; +import io.seata.core.store.DistributedLockDO; +import io.seata.core.store.DistributedLocker; +import io.seata.server.storage.redis.JedisPooledFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.Transaction; +import redis.clients.jedis.params.SetParams; + +/** + * Redis distributed lock + * + * @author zhongxiang.wang + */ +@LoadLevel(name = "redis", scope = Scope.SINGLETON) +public class RedisDistributedLocker implements DistributedLocker { + + protected static final Logger LOGGER = LoggerFactory.getLogger(RedisDistributedLocker.class); + + private static final String SUCCESS = "OK"; + + /** + * Acquire the distributed lock + * @param distributedLockDO the distributed lock info + * @return the distributed lock info + */ + @Override + public boolean acquireLock(DistributedLockDO distributedLockDO) { + try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { + // Don't need retry,if can't acquire the lock,let the other get the lock + String result = jedis.set(distributedLockDO.getLockKey(), distributedLockDO.getLockValue(), + SetParams.setParams().nx().px(distributedLockDO.getExpireTime())); + return SUCCESS.equalsIgnoreCase(result); + } + catch (Exception ex) { + LOGGER.error("The {} acquired the {} distributed lock failed.", distributedLockDO.getLockValue(), + distributedLockDO.getLockKey(), ex); + return false; + } + } + + /** + * Release the distributed lock + * @param distributedLockDO the distributed lock info + * @return the boolean + */ + @Override + public boolean releaseLock(DistributedLockDO distributedLockDO) { + String lockKey = distributedLockDO.getLockKey(); + String lockValue = distributedLockDO.getLockValue(); + try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { + jedis.watch(lockKey); + // Check the value to prevent release the other's lock + if (lockValue.equals(jedis.get(lockKey))) { + Transaction multi = jedis.multi(); + multi.del(lockKey); + multi.exec(); + return true; + } + // The lock hold by others,If other one get the lock,we release lock success + // too as for current lockKey + jedis.unwatch(); + return true; + } + catch (Exception ex) { + LOGGER.error("The {} release the {} distributed lock failed.", lockValue, lockKey, ex); + return false; + } + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/lock/RedisLockManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/lock/RedisLockManager.java index 784463c8a..488a7b76d 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/lock/RedisLockManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/lock/RedisLockManager.java @@ -15,12 +15,8 @@ */ package io.seata.server.storage.redis.lock; -import java.util.List; -import java.util.stream.Collectors; - import io.seata.common.executor.Initialize; import io.seata.common.loader.LoadLevel; -import io.seata.common.util.CollectionUtils; import io.seata.core.exception.TransactionException; import io.seata.core.lock.Locker; import io.seata.server.lock.AbstractLockManager; @@ -61,17 +57,11 @@ public class RedisLockManager extends AbstractLockManager implements Initialize @Override public boolean releaseGlobalSessionLock(GlobalSession globalSession) throws TransactionException { - List branchSessions = globalSession.getBranchSessions(); - if (CollectionUtils.isEmpty(branchSessions)) { - return true; - } - List branchIds = branchSessions.stream().map(BranchSession::getBranchId).collect(Collectors.toList()); try { - return getLocker().releaseLock(globalSession.getXid(), branchIds); + return getLocker().releaseLock(globalSession.getXid()); } catch (Exception t) { - LOGGER.error("unLock globalSession error, xid:{} branchIds:{}", globalSession.getXid(), - CollectionUtils.toString(branchIds), t); + LOGGER.error("unLock globalSession error, xid:{}", globalSession.getXid(), t); return false; } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/lock/RedisLocker.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/lock/RedisLocker.java index 86343f39b..fd4cb7010 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/lock/RedisLocker.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/lock/RedisLocker.java @@ -15,27 +15,31 @@ */ package io.seata.server.storage.redis.lock; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.StringJoiner; -import java.util.stream.Collectors; import com.google.common.collect.Lists; +import io.seata.common.exception.StoreException; +import io.seata.common.io.FileLoader; import io.seata.common.util.CollectionUtils; import io.seata.common.util.LambdaUtils; import io.seata.common.util.StringUtils; +import io.seata.core.exception.BranchTransactionException; import io.seata.core.lock.AbstractLocker; import io.seata.core.lock.RowLock; +import io.seata.core.model.LockStatus; import io.seata.core.store.LockDO; import io.seata.server.storage.redis.JedisPooledFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import redis.clients.jedis.Jedis; import redis.clients.jedis.Pipeline; +import java.io.*; +import java.util.*; +import java.util.stream.Collectors; + import static io.seata.common.Constants.ROW_LOCK_KEY_SPLIT_CHAR; +import static io.seata.core.constants.RedisKeyConstants.DEFAULT_REDIS_SEATA_GLOBAL_LOCK_PREFIX; +import static io.seata.core.constants.RedisKeyConstants.DEFAULT_REDIS_SEATA_ROW_LOCK_PREFIX; +import static io.seata.core.exception.TransactionExceptionCode.LockKeyConflictFailFast; /** * The redis lock store operation @@ -45,14 +49,12 @@ import static io.seata.common.Constants.ROW_LOCK_KEY_SPLIT_CHAR; */ public class RedisLocker extends AbstractLocker { + private static final Logger LOGGER = LoggerFactory.getLogger(RedisLocker.class); + private static final Integer SUCCEED = 1; private static final Integer FAILED = 0; - private static final String DEFAULT_REDIS_SEATA_ROW_LOCK_PREFIX = "SEATA_ROW_LOCK_"; - - private static final String DEFAULT_REDIS_SEATA_GLOBAL_LOCK_PREFIX = "SEATA_GLOBAL_LOCK"; - private static final String XID = "xid"; private static final String TRANSACTION_ID = "transactionId"; @@ -65,38 +67,119 @@ public class RedisLocker extends AbstractLocker { private static final String PK = "pk"; + private static final String STATUS = "status"; + private static final String ROW_KEY = "rowKey"; + private static final String REDIS_LUA_FILE_NAME = "lua/redislocker/redislock.lua"; + + private static String ACQUIRE_LOCK_SHA; + + private static final String WHITE_SPACE = " "; + + private static final String ANNOTATION_LUA = "--"; + /** * Instantiates a new Redis locker. */ public RedisLocker() { + if (ACQUIRE_LOCK_SHA == null) { + File luaFile = FileLoader.load(REDIS_LUA_FILE_NAME); + if (luaFile != null) { + StringBuilder acquireLockLuaByFile = new StringBuilder(); + try (FileInputStream fis = new FileInputStream(luaFile)) { + BufferedReader br = new BufferedReader(new InputStreamReader(fis)); + String line; + while ((line = br.readLine()) != null) { + if (line.trim().startsWith(ANNOTATION_LUA)) { + continue; + } + acquireLockLuaByFile.append(line); + acquireLockLuaByFile.append(WHITE_SPACE); + } + // if it fails to read the file, pipeline mode is used + } + catch (IOException e) { + LOGGER.info("redis locker use pipeline mode"); + return; + } + try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { + ACQUIRE_LOCK_SHA = jedis.scriptLoad(acquireLockLuaByFile.toString()); + LOGGER.info("redis locker use lua mode"); + } + } + else { + LOGGER.info("redis locker use pipeline mode"); + } + } } @Override public boolean acquireLock(List rowLocks) { + return acquireLock(rowLocks, true, false); + } + + @Override + public boolean acquireLock(List rowLocks, boolean autoCommit, boolean skipCheckLock) { if (CollectionUtils.isEmpty(rowLocks)) { return true; } + try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { + if (ACQUIRE_LOCK_SHA != null && autoCommit) { + return acquireLockByLua(jedis, rowLocks); + } + else { + return acquireLockByPipeline(jedis, rowLocks, autoCommit, skipCheckLock); + } + } + } + + private boolean acquireLockByPipeline(Jedis jedis, List rowLocks, boolean autoCommit, + boolean skipCheckLock) { String needLockXid = rowLocks.get(0).getXid(); Long branchId = rowLocks.get(0).getBranchId(); + List needLockDOS = convertToLockDO(rowLocks); + if (needLockDOS.size() > 1) { + needLockDOS = needLockDOS.stream().filter(LambdaUtils.distinctByKey(LockDO::getRowKey)) + .collect(Collectors.toList()); + } + List needLockKeys = new ArrayList<>(); + needLockDOS.forEach(lockDO -> needLockKeys.add(buildLockKey(lockDO.getRowKey()))); + Map needAddLock = new HashMap<>(needLockKeys.size(), 1); - try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { - List needLockDOS = convertToLockDO(rowLocks); - if (needLockDOS.size() > 1) { - needLockDOS = needLockDOS.stream().filter(LambdaUtils.distinctByKey(LockDO::getRowKey)) - .collect(Collectors.toList()); - } - List needLockKeys = new ArrayList<>(); - needLockDOS.forEach(lockDO -> needLockKeys.add(buildLockKey(lockDO.getRowKey()))); - + if (!skipCheckLock) { Pipeline pipeline1 = jedis.pipelined(); - needLockKeys.stream().forEachOrdered(needLockKey -> pipeline1.hget(needLockKey, XID)); - List existedLockInfos = (List) (List) pipeline1.syncAndReturnAll(); - Map needAddLock = new HashMap<>(needLockKeys.size(), 1); + needLockKeys.stream().forEachOrdered(needLockKey -> { + pipeline1.hget(needLockKey, XID); + if (!autoCommit) { + pipeline1.hget(needLockKey, STATUS); + } + }); + List> existedLockInfos = Lists.partition((List) (List) pipeline1.syncAndReturnAll(), + autoCommit ? 1 : 2); + // When the local transaction and the global transaction are enabled, + // the branch registration fails to acquire the global lock, + // the lock holder is in the second-stage rollback, + // and the branch registration fails to be retried quickly, + // because the retry with the local transaction does not release the database + // lock , + // resulting in a two-phase rollback wait. + // Therefore, if a global lock is found in the Rollbacking state, + // the fail-fast code is returned directly. + if (!autoCommit) { + boolean hasRollBackingLock = existedLockInfos.parallelStream().anyMatch( + result -> StringUtils.equals(result.get(1), String.valueOf(LockStatus.Rollbacking.getCode()))); + if (hasRollBackingLock) { + throw new StoreException(new BranchTransactionException(LockKeyConflictFailFast)); + } + } + + // The logic is executed here, there must be a lock without Rollbacking status + // when autoCommit equals false for (int i = 0; i < needLockKeys.size(); i++) { - String existedLockXid = existedLockInfos.get(i); + List results = existedLockInfos.get(i); + String existedLockXid = CollectionUtils.isEmpty(results) ? null : existedLockInfos.get(i).get(0); if (StringUtils.isEmpty(existedLockXid)) { // If empty,we need to lock this row needAddLock.put(needLockKeys.get(i), needLockDOS.get(i)); @@ -105,54 +188,97 @@ public class RedisLocker extends AbstractLocker { if (!StringUtils.equals(existedLockXid, needLockXid)) { // If not equals,means the rowkey is holding by another global // transaction + logGlobalLockConflictInfo(needLockXid, needLockKeys.get(i), existedLockXid); return false; } } } - if (needAddLock.isEmpty()) { return true; } - Pipeline pipeline = jedis.pipelined(); - List readyKeys = new ArrayList<>(); - needAddLock.forEach((key, value) -> { - pipeline.hsetnx(key, XID, value.getXid()); - pipeline.hsetnx(key, TRANSACTION_ID, value.getTransactionId().toString()); - pipeline.hsetnx(key, BRANCH_ID, value.getBranchId().toString()); - pipeline.hset(key, ROW_KEY, value.getRowKey()); - pipeline.hset(key, RESOURCE_ID, value.getResourceId()); - pipeline.hset(key, TABLE_NAME, value.getTableName()); - pipeline.hset(key, PK, value.getPk()); - readyKeys.add(key); - }); - List results = (List) (List) pipeline.syncAndReturnAll(); - List> partitions = Lists.partition(results, 7); + } - ArrayList success = new ArrayList<>(partitions.size()); - Integer status = SUCCEED; - for (int i = 0; i < partitions.size(); i++) { - if (Objects.equals(partitions.get(i).get(0), FAILED)) { - status = FAILED; - } - else { - success.add(readyKeys.get(i)); - } - } + Pipeline pipeline = jedis.pipelined(); + List readyKeys = new ArrayList<>(needAddLock.keySet()); + needAddLock.forEach((key, value) -> { + pipeline.hsetnx(key, XID, value.getXid()); + pipeline.hsetnx(key, TRANSACTION_ID, value.getTransactionId().toString()); + pipeline.hsetnx(key, BRANCH_ID, value.getBranchId().toString()); + pipeline.hset(key, ROW_KEY, value.getRowKey()); + pipeline.hset(key, RESOURCE_ID, value.getResourceId()); + pipeline.hset(key, TABLE_NAME, value.getTableName()); + pipeline.hset(key, PK, value.getPk()); + }); + List results = (List) (List) pipeline.syncAndReturnAll(); + List> partitions = Lists.partition(results, 7); - // If someone has failed,all the lockkey which has been added need to be - // delete. - if (FAILED.equals(status)) { - if (success.size() > 0) { - jedis.del(success.toArray(new String[0])); - } - return false; + ArrayList success = new ArrayList<>(partitions.size()); + Integer status = SUCCEED; + for (int i = 0; i < partitions.size(); i++) { + if (Objects.equals(partitions.get(i).get(0), FAILED)) { + status = FAILED; } - String xidLockKey = buildXidLockKey(needLockXid); - StringJoiner lockKeysString = new StringJoiner(ROW_LOCK_KEY_SPLIT_CHAR); - needLockKeys.forEach(lockKeysString::add); - jedis.hset(xidLockKey, branchId.toString(), lockKeysString.toString()); + else { + success.add(readyKeys.get(i)); + } + } + + // If someone has failed,all the lockkey which has been added need to be delete. + if (FAILED.equals(status)) { + if (success.size() > 0) { + jedis.del(success.toArray(new String[0])); + } + return false; + } + String xidLockKey = buildXidLockKey(needLockXid); + StringJoiner lockKeysString = new StringJoiner(ROW_LOCK_KEY_SPLIT_CHAR); + needLockKeys.forEach(lockKeysString::add); + jedis.hset(xidLockKey, branchId.toString(), lockKeysString.toString()); + return true; + } + + private boolean acquireLockByLua(Jedis jedis, List rowLocks) { + String needLockXid = rowLocks.get(0).getXid(); + Long branchId = rowLocks.get(0).getBranchId(); + List needLockDOs = rowLocks.stream().map(this::convertToLockDO) + .filter(LambdaUtils.distinctByKey(LockDO::getRowKey)).collect(Collectors.toList()); + ArrayList keys = new ArrayList<>(); + ArrayList args = new ArrayList<>(); + int size = needLockDOs.size(); + args.add(String.valueOf(size)); + // args index 2 placeholder + args.add(null); + args.add(needLockXid); + for (LockDO lockDO : needLockDOs) { + keys.add(buildLockKey(lockDO.getRowKey())); + args.add(lockDO.getTransactionId().toString()); + args.add(lockDO.getBranchId().toString()); + args.add(lockDO.getResourceId()); + args.add(lockDO.getTableName()); + args.add(lockDO.getRowKey()); + args.add(lockDO.getPk()); + } + String xidLockKey = buildXidLockKey(needLockXid); + StringJoiner lockKeysString = new StringJoiner(ROW_LOCK_KEY_SPLIT_CHAR); + needLockDOs.stream().map(lockDO -> buildLockKey(lockDO.getRowKey())).forEach(lockKeysString::add); + keys.add(xidLockKey); + keys.add(branchId.toString()); + args.add(lockKeysString.toString()); + // reset args index 2 + args.set(1, String.valueOf(args.size())); + String xIdOwnLock = (String) jedis.evalsha(ACQUIRE_LOCK_SHA, keys, args); + if (xIdOwnLock.equals(needLockXid)) { return true; } + else { + logGlobalLockConflictInfo(needLockXid, keys.get(0), xIdOwnLock); + return false; + } + } + + private void logGlobalLockConflictInfo(String needLockXid, String lockKey, String xIdOwnLock) { + LOGGER.info("tx:[{}] acquire Global lock failed. Global lock on [{}] is holding by xid {}", needLockXid, + lockKey, xIdOwnLock); } @Override @@ -168,8 +294,7 @@ public class RedisLocker extends AbstractLocker { needReleaseKeys[i] = buildLockKey(needReleaseLocks.get(i).getRowKey()); } - try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { - Pipeline pipelined = jedis.pipelined(); + try (Jedis jedis = JedisPooledFactory.getJedisInstance(); Pipeline pipelined = jedis.pipelined()) { pipelined.del(needReleaseKeys); pipelined.hdel(buildXidLockKey(currentXid), branchId.toString()); pipelined.sync(); @@ -178,21 +303,73 @@ public class RedisLocker extends AbstractLocker { } @Override - public boolean releaseLock(String xid, List branchIds) { - if (CollectionUtils.isEmpty(branchIds)) { + public boolean releaseLock(String xid) { + return doReleaseLock(xid, null); + } + + @Override + public boolean releaseLock(String xid, Long branchId) { + if (branchId == null) { + return true; + } + return doReleaseLock(xid, branchId); + } + + @Override + public boolean isLockable(List rowLocks) { + if (CollectionUtils.isEmpty(rowLocks)) { return true; } try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { - String xidLockKey = buildXidLockKey(xid); - String[] branchIdsArray = new String[branchIds.size()]; - for (int i = 0; i < branchIds.size(); i++) { - branchIdsArray[i] = branchIds.get(i).toString(); + List locks = convertToLockDO(rowLocks); + Set lockKeys = new HashSet<>(); + for (LockDO rowlock : locks) { + lockKeys.add(buildLockKey(rowlock.getRowKey())); } - List rowKeys = jedis.hmget(xidLockKey, branchIdsArray); + String xid = rowLocks.get(0).getXid(); + try (Pipeline pipeline = jedis.pipelined()) { + lockKeys.forEach(key -> pipeline.hget(key, XID)); + List existedXids = (List) (List) pipeline.syncAndReturnAll(); + return existedXids.stream().allMatch(existedXid -> existedXid == null || xid.equals(existedXid)); + } + } + } + + @Override + public void updateLockStatus(String xid, LockStatus lockStatus) { + try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { + String xidLockKey = buildXidLockKey(xid); + Map branchAndLockKeys = jedis.hgetAll(xidLockKey); + if (CollectionUtils.isNotEmpty(branchAndLockKeys)) { + try (Pipeline pipeline = jedis.pipelined()) { + branchAndLockKeys.values() + .forEach(k -> pipeline.hset(k, STATUS, String.valueOf(lockStatus.getCode()))); + pipeline.sync(); + } + } + } + } + + private boolean doReleaseLock(String xid, Long branchId) { + try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { + String xidLockKey = buildXidLockKey(xid); + final List rowKeys = new ArrayList<>(); + if (null == branchId) { + Map rowKeyMap = jedis.hgetAll(xidLockKey); + rowKeyMap.forEach((branch, rowKey) -> rowKeys.add(rowKey)); + } + else { + rowKeys.add(jedis.hget(xidLockKey, branchId.toString())); + } if (CollectionUtils.isNotEmpty(rowKeys)) { Pipeline pipelined = jedis.pipelined(); - pipelined.hdel(xidLockKey, branchIdsArray); + if (null == branchId) { + pipelined.del(xidLockKey); + } + else { + pipelined.hdel(xidLockKey, branchId.toString()); + } rowKeys.forEach(rowKeyStr -> { if (StringUtils.isNotEmpty(rowKeyStr)) { if (rowKeyStr.contains(ROW_LOCK_KEY_SPLIT_CHAR)) { @@ -210,33 +387,6 @@ public class RedisLocker extends AbstractLocker { } } - @Override - public boolean releaseLock(String xid, Long branchId) { - List branchIds = new ArrayList<>(); - branchIds.add(branchId); - return releaseLock(xid, branchIds); - } - - @Override - public boolean isLockable(List rowLocks) { - if (CollectionUtils.isEmpty(rowLocks)) { - return true; - } - try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { - List locks = convertToLockDO(rowLocks); - Set lockKeys = new HashSet<>(); - for (LockDO rowlock : locks) { - lockKeys.add(buildLockKey(rowlock.getRowKey())); - } - - String xid = rowLocks.get(0).getXid(); - Pipeline pipeline = jedis.pipelined(); - lockKeys.forEach(key -> pipeline.hget(key, XID)); - List existedXids = (List) (List) pipeline.syncAndReturnAll(); - return existedXids.stream().allMatch(existedXid -> existedXid == null || xid.equals(existedXid)); - } - } - private String buildXidLockKey(String xid) { return DEFAULT_REDIS_SEATA_GLOBAL_LOCK_PREFIX + xid; } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/session/RedisSessionManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/session/RedisSessionManager.java index 7c326ff0b..ee604c0a5 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/session/RedisSessionManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/session/RedisSessionManager.java @@ -15,8 +15,6 @@ */ package io.seata.server.storage.redis.session; -import java.util.Collection; -import java.util.List; import io.seata.common.exception.StoreException; import io.seata.common.executor.Initialize; import io.seata.common.loader.LoadLevel; @@ -25,16 +23,15 @@ import io.seata.common.util.StringUtils; import io.seata.core.exception.TransactionException; import io.seata.core.model.BranchStatus; import io.seata.core.model.GlobalStatus; -import io.seata.server.session.AbstractSessionManager; -import io.seata.server.session.BranchSession; -import io.seata.server.session.GlobalSession; -import io.seata.server.session.SessionCondition; -import io.seata.server.session.SessionHolder; +import io.seata.server.session.*; import io.seata.server.storage.redis.store.RedisTransactionStoreManager; import io.seata.server.store.TransactionStoreManager.LogOperation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Collection; +import java.util.List; + /** * @author funkye */ @@ -165,19 +162,18 @@ public class RedisSessionManager extends AbstractSessionManager implements Initi return findGlobalSessions(new SessionCondition(GlobalStatus.AsyncCommitting)); } else if (SessionHolder.RETRY_COMMITTING_SESSION_MANAGER_NAME.equalsIgnoreCase(taskName)) { - return findGlobalSessions( - new SessionCondition(new GlobalStatus[] { GlobalStatus.CommitRetrying, GlobalStatus.Committing })); + return findGlobalSessions(new SessionCondition(GlobalStatus.CommitRetrying, GlobalStatus.Committing)); } else if (SessionHolder.RETRY_ROLLBACKING_SESSION_MANAGER_NAME.equalsIgnoreCase(taskName)) { - return findGlobalSessions(new SessionCondition(new GlobalStatus[] { GlobalStatus.RollbackRetrying, - GlobalStatus.Rollbacking, GlobalStatus.TimeoutRollbacking, GlobalStatus.TimeoutRollbackRetrying })); + return findGlobalSessions(new SessionCondition(GlobalStatus.RollbackRetrying, GlobalStatus.Rollbacking, + GlobalStatus.TimeoutRollbacking, GlobalStatus.TimeoutRollbackRetrying)); } else { // all data - return findGlobalSessions(new SessionCondition(new GlobalStatus[] { GlobalStatus.UnKnown, - GlobalStatus.Begin, GlobalStatus.Committing, GlobalStatus.CommitRetrying, GlobalStatus.Rollbacking, + return findGlobalSessions(new SessionCondition(GlobalStatus.UnKnown, GlobalStatus.Begin, + GlobalStatus.Committing, GlobalStatus.CommitRetrying, GlobalStatus.Rollbacking, GlobalStatus.RollbackRetrying, GlobalStatus.TimeoutRollbacking, - GlobalStatus.TimeoutRollbackRetrying, GlobalStatus.AsyncCommitting })); + GlobalStatus.TimeoutRollbackRetrying, GlobalStatus.AsyncCommitting)); } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/store/RedisTransactionStoreManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/store/RedisTransactionStoreManager.java index 197955af4..eb1515a3d 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/store/RedisTransactionStoreManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/store/RedisTransactionStoreManager.java @@ -15,48 +15,47 @@ */ package io.seata.server.storage.redis.store; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import redis.clients.jedis.Jedis; -import redis.clients.jedis.Pipeline; -import redis.clients.jedis.Transaction; +import com.google.common.collect.ImmutableMap; +import io.seata.common.XID; +import io.seata.common.exception.RedisException; import io.seata.common.exception.StoreException; +import io.seata.common.util.BeanUtils; import io.seata.common.util.CollectionUtils; import io.seata.common.util.StringUtils; -import io.seata.common.exception.RedisException; -import io.seata.common.util.BeanUtils; -import io.seata.common.XID; +import io.seata.config.Configuration; +import io.seata.config.ConfigurationFactory; import io.seata.core.model.GlobalStatus; import io.seata.core.store.BranchTransactionDO; import io.seata.core.store.GlobalTransactionDO; +import io.seata.server.console.param.GlobalSessionParam; import io.seata.server.session.GlobalSession; import io.seata.server.session.SessionCondition; +import io.seata.server.session.SessionStatusValidator; import io.seata.server.storage.SessionConverter; import io.seata.server.storage.redis.JedisPooledFactory; import io.seata.server.store.AbstractTransactionStoreManager; import io.seata.server.store.SessionStorable; import io.seata.server.store.TransactionStoreManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.Pipeline; +import redis.clients.jedis.Transaction; -import static io.seata.core.constants.RedisKeyConstants.REDIS_KEY_BRANCH_GMT_MODIFIED; -import static io.seata.core.constants.RedisKeyConstants.REDIS_KEY_BRANCH_STATUS; -import static io.seata.core.constants.RedisKeyConstants.REDIS_KEY_BRANCH_XID; -import static io.seata.core.constants.RedisKeyConstants.REDIS_KEY_GLOBAL_GMT_MODIFIED; -import static io.seata.core.constants.RedisKeyConstants.REDIS_KEY_GLOBAL_STATUS; -import static io.seata.core.constants.RedisKeyConstants.REDIS_KEY_GLOBAL_XID; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static io.seata.common.ConfigurationKeys.STORE_REDIS_QUERY_LIMIT; +import static io.seata.common.DefaultValues.DEFAULT_QUERY_LIMIT; +import static io.seata.core.constants.RedisKeyConstants.*; /** * The redis transaction store manager * * @author funkye * @author wangzhongxiang + * @author doubleDimple */ public class RedisTransactionStoreManager extends AbstractTransactionStoreManager implements TransactionStoreManager { @@ -74,10 +73,23 @@ public class RedisTransactionStoreManager extends AbstractTransactionStoreManage /** the prefix of the global transaction status */ private static final String REDIS_SEATA_STATUS_PREFIX = "SEATA_STATUS_"; + /** the key of global transaction status for begin */ + private static final String REDIS_SEATA_BEGIN_TRANSACTIONS_KEY = "SEATA_BEGIN_TRANSACTIONS"; + private static volatile RedisTransactionStoreManager instance; private static final String OK = "OK"; + /** + * The constant CONFIG. + */ + private static final Configuration CONFIG = ConfigurationFactory.getInstance(); + + /** + * The Log query limit. + */ + private int logQueryLimit; + /** * Get the instance. */ @@ -92,25 +104,58 @@ public class RedisTransactionStoreManager extends AbstractTransactionStoreManage return instance; } + /** + * init map to constructor + */ + public RedisTransactionStoreManager() { + super(); + initGlobalMap(); + initBranchMap(); + logQueryLimit = CONFIG.getInt(STORE_REDIS_QUERY_LIMIT, DEFAULT_QUERY_LIMIT); + } + + /** + * Map for LogOperation Global Operation + */ + public static volatile ImmutableMap> globalMap; + + /** + * Map for LogOperation Branch Operation + */ + public static volatile ImmutableMap> branchMap; + + /** + * init globalMap + * + */ + public void initGlobalMap() { + if (CollectionUtils.isEmpty(branchMap)) { + globalMap = ImmutableMap.>builder() + .put(LogOperation.GLOBAL_ADD, this::insertGlobalTransactionDO) + .put(LogOperation.GLOBAL_UPDATE, this::updateGlobalTransactionDO) + .put(LogOperation.GLOBAL_REMOVE, this::deleteGlobalTransactionDO).build(); + } + } + + /** + * init branchMap + * + */ + public void initBranchMap() { + if (CollectionUtils.isEmpty(branchMap)) { + branchMap = ImmutableMap.>builder() + .put(LogOperation.BRANCH_ADD, this::insertBranchTransactionDO) + .put(LogOperation.BRANCH_UPDATE, this::updateBranchTransactionDO) + .put(LogOperation.BRANCH_REMOVE, this::deleteBranchTransactionDO).build(); + } + } + @Override public boolean writeSession(LogOperation logOperation, SessionStorable session) { - if (LogOperation.GLOBAL_ADD.equals(logOperation)) { - return insertGlobalTransactionDO(SessionConverter.convertGlobalTransactionDO(session)); - } - else if (LogOperation.GLOBAL_UPDATE.equals(logOperation)) { - return updateGlobalTransactionDO(SessionConverter.convertGlobalTransactionDO(session)); - } - else if (LogOperation.GLOBAL_REMOVE.equals(logOperation)) { - return deleteGlobalTransactionDO(SessionConverter.convertGlobalTransactionDO(session)); - } - else if (LogOperation.BRANCH_ADD.equals(logOperation)) { - return insertBranchTransactionDO(SessionConverter.convertBranchTransactionDO(session)); - } - else if (LogOperation.BRANCH_UPDATE.equals(logOperation)) { - return updateBranchTransactionDO(SessionConverter.convertBranchTransactionDO(session)); - } - else if (LogOperation.BRANCH_REMOVE.equals(logOperation)) { - return deleteBranchTransactionDO(SessionConverter.convertBranchTransactionDO(session)); + if (globalMap.containsKey(logOperation) || branchMap.containsKey(logOperation)) { + return globalMap.containsKey(logOperation) + ? globalMap.get(logOperation).apply(SessionConverter.convertGlobalTransactionDO(session)) + : branchMap.get(logOperation).apply(SessionConverter.convertBranchTransactionDO(session)); } else { throw new StoreException("Unknown LogOperation:" + logOperation.name()); @@ -125,11 +170,10 @@ public class RedisTransactionStoreManager extends AbstractTransactionStoreManage private boolean insertBranchTransactionDO(BranchTransactionDO branchTransactionDO) { String branchKey = buildBranchKey(branchTransactionDO.getBranchId()); String branchListKey = buildBranchListKeyByXid(branchTransactionDO.getXid()); - try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { + try (Jedis jedis = JedisPooledFactory.getJedisInstance(); Pipeline pipelined = jedis.pipelined()) { Date now = new Date(); branchTransactionDO.setGmtCreate(now); branchTransactionDO.setGmtModified(now); - Pipeline pipelined = jedis.pipelined(); pipelined.hmset(branchKey, BeanUtils.objectToMap(branchTransactionDO)); pipelined.rpush(branchListKey, branchKey); pipelined.sync(); @@ -148,16 +192,16 @@ public class RedisTransactionStoreManager extends AbstractTransactionStoreManage private boolean deleteBranchTransactionDO(BranchTransactionDO branchTransactionDO) { String branchKey = buildBranchKey(branchTransactionDO.getBranchId()); try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { - Map branchTransactionDOMap = jedis.hgetAll(branchKey); - String xid = branchTransactionDOMap.get(REDIS_KEY_BRANCH_XID); + String xid = jedis.hget(branchKey, REDIS_KEY_BRANCH_XID); if (StringUtils.isEmpty(xid)) { return true; } String branchListKey = buildBranchListKeyByXid(branchTransactionDO.getXid()); - Pipeline pipelined = jedis.pipelined(); - pipelined.lrem(branchListKey, 0, branchKey); - pipelined.del(branchKey); - pipelined.sync(); + try (Pipeline pipelined = jedis.pipelined()) { + pipelined.lrem(branchListKey, 0, branchKey); + pipelined.del(branchKey); + pipelined.sync(); + } return true; } catch (Exception ex) { @@ -177,9 +221,12 @@ public class RedisTransactionStoreManager extends AbstractTransactionStoreManage if (StringUtils.isEmpty(previousBranchStatus)) { throw new StoreException("Branch transaction is not exist, update branch transaction failed."); } - Map map = new HashMap<>(2, 1); + Map map = new HashMap<>(3, 1); map.put(REDIS_KEY_BRANCH_STATUS, String.valueOf(branchTransactionDO.getStatus())); map.put(REDIS_KEY_BRANCH_GMT_MODIFIED, String.valueOf((new Date()).getTime())); + if (StringUtils.isNotBlank(branchTransactionDO.getApplicationData())) { + map.put(REDIS_KEY_BRANCH_APPLICATION_DATA, String.valueOf(branchTransactionDO.getApplicationData())); + } jedis.hmset(branchKey, map); return true; } @@ -195,13 +242,15 @@ public class RedisTransactionStoreManager extends AbstractTransactionStoreManage */ private boolean insertGlobalTransactionDO(GlobalTransactionDO globalTransactionDO) { String globalKey = buildGlobalKeyByTransactionId(globalTransactionDO.getTransactionId()); - try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { + try (Jedis jedis = JedisPooledFactory.getJedisInstance(); Pipeline pipelined = jedis.pipelined()) { Date now = new Date(); globalTransactionDO.setGmtCreate(now); globalTransactionDO.setGmtModified(now); - Pipeline pipelined = jedis.pipelined(); pipelined.hmset(globalKey, BeanUtils.objectToMap(globalTransactionDO)); - pipelined.rpush(buildGlobalStatus(globalTransactionDO.getStatus()), globalTransactionDO.getXid()); + String xid = globalTransactionDO.getXid(); + pipelined.rpush(buildGlobalStatus(globalTransactionDO.getStatus()), xid); + pipelined.zadd(REDIS_SEATA_BEGIN_TRANSACTIONS_KEY, + globalTransactionDO.getBeginTime() + globalTransactionDO.getTimeout(), globalKey); pipelined.sync(); return true; } @@ -220,17 +269,21 @@ public class RedisTransactionStoreManager extends AbstractTransactionStoreManage private boolean deleteGlobalTransactionDO(GlobalTransactionDO globalTransactionDO) { String globalKey = buildGlobalKeyByTransactionId(globalTransactionDO.getTransactionId()); try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { - Map globalTransactionDoMap = jedis.hgetAll(globalKey); - String xid = globalTransactionDoMap.get(REDIS_KEY_GLOBAL_XID); + String xid = jedis.hget(globalKey, REDIS_KEY_GLOBAL_XID); if (StringUtils.isEmpty(xid)) { LOGGER.warn("Global transaction is not exist,xid = {}.Maybe has been deleted by another tc server", globalTransactionDO.getXid()); return true; } - Pipeline pipelined = jedis.pipelined(); - pipelined.lrem(buildGlobalStatus(globalTransactionDO.getStatus()), 0, globalTransactionDO.getXid()); - pipelined.del(globalKey); - pipelined.sync(); + try (Pipeline pipelined = jedis.pipelined()) { + pipelined.lrem(buildGlobalStatus(globalTransactionDO.getStatus()), 0, globalTransactionDO.getXid()); + pipelined.del(globalKey); + if (GlobalStatus.Begin.getCode() == globalTransactionDO.getStatus() + || GlobalStatus.UnKnown.getCode() == globalTransactionDO.getStatus()) { + pipelined.zrem(REDIS_SEATA_BEGIN_TRANSACTIONS_KEY, globalKey); + } + pipelined.sync(); + } return true; } catch (Exception ex) { @@ -261,6 +314,13 @@ public class RedisTransactionStoreManager extends AbstractTransactionStoreManage jedis.unwatch(); return true; } + GlobalStatus before = GlobalStatus.get(Integer.parseInt(previousStatus)); + GlobalStatus after = GlobalStatus.get(globalTransactionDO.getStatus()); + if (!SessionStatusValidator.validateUpdateStatus(before, after)) { + throw new StoreException( + "Illegal changing of global status, update global transaction failed." + " beforeStatus[" + + before.name() + "] cannot be changed to afterStatus[" + after.name() + "]"); + } String previousGmtModified = statusAndGmtModified.get(1); Transaction multi = jedis.multi(); @@ -270,7 +330,16 @@ public class RedisTransactionStoreManager extends AbstractTransactionStoreManage multi.hmset(globalKey, map); multi.lrem(buildGlobalStatus(Integer.valueOf(previousStatus)), 0, xid); multi.rpush(buildGlobalStatus(globalTransactionDO.getStatus()), xid); + multi.zrem(REDIS_SEATA_BEGIN_TRANSACTIONS_KEY, globalKey); List exec = multi.exec(); + if (CollectionUtils.isEmpty(exec)) { + // The data has changed by another tc, so we still think the modification + // is successful. + LOGGER.warn( + "The global transaction xid = {}, maybe changed by another TC. It does not affect the results", + globalTransactionDO.getXid()); + return true; + } String hmset = exec.get(0).toString(); long lrem = (long) exec.get(1); long rpush = (long) exec.get(2); @@ -328,7 +397,8 @@ public class RedisTransactionStoreManager extends AbstractTransactionStoreManage if (withBranchSessions) { branchTransactionDOs = this.readBranchSessionByXid(jedis, xid); } - return getGlobalSession(globalTransactionDO, branchTransactionDOs); + GlobalSession session = getGlobalSession(globalTransactionDO, branchTransactionDOs, withBranchSessions); + return session; } } @@ -347,28 +417,84 @@ public class RedisTransactionStoreManager extends AbstractTransactionStoreManage * @param statuses the statuses * @return the list */ - public List readSession(GlobalStatus[] statuses) { - List statusKeys = new ArrayList<>(); - for (int i = 0; i < statuses.length; i++) { - statusKeys.add(buildGlobalStatus(statuses[i].getCode())); + @Override + public List readSession(GlobalStatus[] statuses, boolean withBranchSessions) { + List globalSessions = Collections.synchronizedList(new ArrayList<>()); + List statusKeys = convertStatusKeys(statuses); + Map targetMap = calculateStatuskeysHasData(statusKeys); + if (targetMap.size() == 0 || logQueryLimit <= 0) { + return globalSessions; } - try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { - Pipeline pipelined = jedis.pipelined(); - statusKeys.stream().forEach(statusKey -> pipelined.lrange(statusKey, 0, -1)); - List> list = (List>) (List) pipelined.syncAndReturnAll(); - List xids = new ArrayList<>(); - if (CollectionUtils.isNotEmpty(list)) { - xids = list.stream().flatMap(ll -> ll.stream()).collect(Collectors.toList()); - } - List globalSessions = new ArrayList<>(); + int perStatusLimit = resetLogQueryLimit(targetMap); + final long countGlobalSessions = targetMap.values().stream() + .collect(Collectors.summarizingInt(Integer::intValue)).getSum(); + // queryCount + final long queryCount = Math.min(logQueryLimit, countGlobalSessions); + List> list = new ArrayList<>(); + dogetXidsForTargetMapRecursive(targetMap, 0L, perStatusLimit - 1, queryCount, list); + if (CollectionUtils.isNotEmpty(list)) { + List xids = list.stream().flatMap(Collection::stream).collect(Collectors.toList()); xids.parallelStream().forEach(xid -> { - GlobalSession globalSession = this.readSession(xid, true); + GlobalSession globalSession = this.readSession(xid, withBranchSessions); if (globalSession != null) { globalSessions.add(globalSession); } }); - return globalSessions; } + return globalSessions; + } + + @Override + public List readSortByTimeoutBeginSessions(boolean withBranchSessions) { + List list = Collections.emptyList(); + List statusKeys = convertStatusKeys(GlobalStatus.Begin); + Map targetMap = calculateStatuskeysHasData(statusKeys); + if (targetMap.size() == 0 || logQueryLimit <= 0) { + return list; + } + final long countGlobalSessions = targetMap.values().stream() + .collect(Collectors.summarizingInt(Integer::intValue)).getSum(); + // queryCount + final long queryCount = Math.min(logQueryLimit, countGlobalSessions); + try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { + Set values = jedis.zrangeByScore(REDIS_SEATA_BEGIN_TRANSACTIONS_KEY, 0, System.currentTimeMillis(), + 0, (int) queryCount); + List> rep; + try (Pipeline pipeline = jedis.pipelined()) { + for (String value : values) { + pipeline.hgetAll(value); + } + rep = (List>) (List) pipeline.syncAndReturnAll(); + } + list = rep.stream().map(map -> { + GlobalTransactionDO globalTransactionDO = (GlobalTransactionDO) BeanUtils.mapToObject(map, + GlobalTransactionDO.class); + if (globalTransactionDO != null) { + String xid = globalTransactionDO.getXid(); + List branchTransactionDOs = new ArrayList<>(); + if (withBranchSessions) { + branchTransactionDOs = this.readBranchSessionByXid(jedis, xid); + } + return getGlobalSession(globalTransactionDO, branchTransactionDOs, withBranchSessions); + } + return null; + }).filter(Objects::nonNull).collect(Collectors.toList()); + } + return list; + } + + /** + * get everyone keys limit + * @param targetMap + * @return + */ + private int resetLogQueryLimit(Map targetMap) { + int resetLimitQuery = logQueryLimit; + if (targetMap.size() > 1) { + int size = targetMap.size(); + resetLimitQuery = (logQueryLimit / size) == 0 ? 1 : (logQueryLimit / size); + } + return resetLimitQuery; } /** @@ -380,38 +506,70 @@ public class RedisTransactionStoreManager extends AbstractTransactionStoreManage public List readSession(SessionCondition sessionCondition) { List globalSessions = new ArrayList<>(); if (StringUtils.isNotEmpty(sessionCondition.getXid())) { - GlobalSession globalSession = this.readSession(sessionCondition.getXid(), true); + GlobalSession globalSession = this.readSession(sessionCondition.getXid(), + !sessionCondition.isLazyLoadBranch()); if (globalSession != null) { globalSessions.add(globalSession); } return globalSessions; } else if (sessionCondition.getTransactionId() != null) { - GlobalSession globalSession = this - .readSessionByTransactionId(sessionCondition.getTransactionId().toString(), true); + GlobalSession globalSession = this.readSessionByTransactionId( + sessionCondition.getTransactionId().toString(), !sessionCondition.isLazyLoadBranch()); if (globalSession != null) { globalSessions.add(globalSession); } return globalSessions; } else if (CollectionUtils.isNotEmpty(sessionCondition.getStatuses())) { - return readSession(sessionCondition.getStatuses()); - } - else if (sessionCondition.getStatus() != null) { - return readSession(new GlobalStatus[] { sessionCondition.getStatus() }); + if (sessionCondition.getStatuses().length == 1 && sessionCondition.getStatuses()[0] == GlobalStatus.Begin) { + return this.readSortByTimeoutBeginSessions(!sessionCondition.isLazyLoadBranch()); + } + else { + return readSession(sessionCondition.getStatuses(), !sessionCondition.isLazyLoadBranch()); + } } return null; } + /** + * query GlobalSession by status with page + * @param param + * @return List + */ + public List readSessionStatusByPage(GlobalSessionParam param) { + List globalSessions = new ArrayList<>(); + + int pageNum = param.getPageNum(); + int pageSize = param.getPageSize(); + int start = Math.max((pageNum - 1) * pageSize, 0); + int end = pageNum * pageSize - 1; + + if (param.getStatus() != null) { + String statusKey = buildGlobalStatus(GlobalStatus.get(param.getStatus()).getCode()); + try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { + final List xids = jedis.lrange(statusKey, start, end); + xids.forEach(xid -> { + GlobalSession globalSession = this.readSession(xid, param.isWithBranch()); + if (globalSession != null) { + globalSessions.add(globalSession); + } + }); + } + } + return globalSessions; + } + /** * assemble the global session and branch session * @param globalTransactionDO the global transactionDo * @param branchTransactionDOs the branch transactionDos + * @param withBranchSessions if read branch sessions * @return the global session with branch session */ private GlobalSession getGlobalSession(GlobalTransactionDO globalTransactionDO, - List branchTransactionDOs) { - GlobalSession globalSession = SessionConverter.convertGlobalSession(globalTransactionDO); + List branchTransactionDOs, boolean withBranchSessions) { + GlobalSession globalSession = SessionConverter.convertGlobalSession(globalTransactionDO, !withBranchSessions); if (CollectionUtils.isNotEmpty(branchTransactionDOs)) { for (BranchTransactionDO branchTransactionDO : branchTransactionDOs) { globalSession.add(SessionConverter.convertBranchSession(branchTransactionDO)); @@ -443,40 +601,217 @@ public class RedisTransactionStoreManager extends AbstractTransactionStoreManage if (withBranchSessions) { branchTransactionDOs = this.readBranchSessionByXid(jedis, xid); } - return getGlobalSession(globalTransactionDO, branchTransactionDOs); + return getGlobalSession(globalTransactionDO, branchTransactionDOs, withBranchSessions); } } /** * Read the branch session list by xid - * @param jedis + * @param jedis the jedis * @param xid the xid * @return the branch transactionDo list */ private List readBranchSessionByXid(Jedis jedis, String xid) { List branchTransactionDOs = new ArrayList<>(); String branchListKey = buildBranchListKeyByXid(xid); - List branchKeys = jedis.lrange(branchListKey, 0, -1); - Pipeline pipeline = jedis.pipelined(); + List branchKeys = lRange(jedis, branchListKey); if (CollectionUtils.isNotEmpty(branchKeys)) { - branchKeys.stream().forEachOrdered(branchKey -> pipeline.hgetAll(branchKey)); - List branchInfos = pipeline.syncAndReturnAll(); - for (Object branchInfo : branchInfos) { - if (branchInfo != null) { - Map branchInfoMap = (Map) branchInfo; - Optional branchTransactionDO = Optional.ofNullable( - (BranchTransactionDO) BeanUtils.mapToObject(branchInfoMap, BranchTransactionDO.class)); - branchTransactionDO.ifPresent(branchTransactionDOs::add); + try (Pipeline pipeline = jedis.pipelined()) { + branchKeys.stream().forEach(branchKey -> pipeline.hgetAll(branchKey)); + List branchInfos = pipeline.syncAndReturnAll(); + for (Object branchInfo : branchInfos) { + if (branchInfo != null) { + Map branchInfoMap = (Map) branchInfo; + Optional branchTransactionDO = Optional.ofNullable( + (BranchTransactionDO) BeanUtils.mapToObject(branchInfoMap, BranchTransactionDO.class)); + branchTransactionDO.ifPresent(branchTransactionDOs::add); + } } } } if (CollectionUtils.isNotEmpty(branchTransactionDOs)) { - branchTransactionDOs = branchTransactionDOs.stream() - .sorted(Comparator.comparing(BranchTransactionDO::getGmtCreate)).collect(Collectors.toList()); + Collections.sort(branchTransactionDOs); } return branchTransactionDOs; } + private List lRange(Jedis jedis, String key) { + List keys = new ArrayList<>(); + List values; + int limit = 20; + int start = 0; + int stop = limit; + for (;;) { + values = jedis.lrange(key, start, stop); + keys.addAll(values); + if (CollectionUtils.isEmpty(values) || values.size() < limit) { + break; + } + start = keys.size(); + stop = start + limit; + } + return keys; + } + + public List findBranchSessionByXid(String xid) { + try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { + return readBranchSessionByXid(jedis, xid); + } + } + + /** + * query globalSession by page + * @param pageNum + * @param pageSize + * @param withBranchSessions + * @return List + */ + public List findGlobalSessionByPage(int pageNum, int pageSize, boolean withBranchSessions) { + List globalSessions = new ArrayList<>(); + int start = Math.max((pageNum - 1) * pageSize, 0); + int end = pageNum * pageSize - 1; + + List statusKeys = convertStatusKeys(GlobalStatus.values()); + Map stringLongMap = calculateStatuskeysHasData(statusKeys); + + List> list = dogetXidsForTargetMap(stringLongMap, start, end, pageSize); + + if (CollectionUtils.isNotEmpty(list)) { + List xids = list.stream().flatMap(Collection::stream).collect(Collectors.toList()); + xids.forEach(xid -> { + if (globalSessions.size() < pageSize) { + GlobalSession globalSession = this.readSession(xid, withBranchSessions); + if (globalSession != null) { + globalSessions.add(globalSession); + } + } + }); + } + return globalSessions; + } + + /** + * query and sort existing values + * @param statusKeys + * @return + */ + private Map calculateStatuskeysHasData(List statusKeys) { + Map resultMap = new LinkedHashMap<>(); + Map keysMap = new HashMap<>(statusKeys.size()); + try (Jedis jedis = JedisPooledFactory.getJedisInstance(); Pipeline pipelined = jedis.pipelined()) { + statusKeys.forEach(key -> pipelined.llen(key)); + List counts = (List) pipelined.syncAndReturnAll(); + for (int i = 0; i < counts.size(); i++) { + if (counts.get(i) > 0) { + keysMap.put(statusKeys.get(i), counts.get(i).intValue()); + } + } + } + + // sort + List> list = new ArrayList<>(keysMap.entrySet()); + list.sort((o1, o2) -> o2.getValue() - o1.getValue()); + list.forEach(e -> resultMap.put(e.getKey(), e.getValue())); + + return resultMap; + } + + /** + * count GlobalSession total by status + * @param values + * @return Long + */ + public Long countByGlobalSessions(GlobalStatus[] values) { + List statusKeys = new ArrayList<>(); + Long total = 0L; + for (GlobalStatus status : values) { + statusKeys.add(buildGlobalStatus(status.getCode())); + } + try (Jedis jedis = JedisPooledFactory.getJedisInstance(); Pipeline pipelined = jedis.pipelined()) { + statusKeys.stream().forEach(statusKey -> pipelined.llen(statusKey)); + List list = (List) (List) pipelined.syncAndReturnAll(); + if (list.size() > 0) { + total = list.stream().mapToLong(value -> value).sum(); + } + return total; + } + } + + private List convertStatusKeys(GlobalStatus... statuses) { + List statusKeys = new ArrayList<>(); + for (int i = 0; i < statuses.length; i++) { + statusKeys.add(buildGlobalStatus(statuses[i].getCode())); + } + return statusKeys; + } + + private void dogetXidsForTargetMapRecursive(Map targetMap, long start, long end, long queryCount, + List> listList) { + + long total = listList.stream().mapToLong(List::size).sum(); + + if (total >= queryCount) { + return; + } + // when start greater than offset(totalCount) + if (start >= queryCount) { + return; + } + + if (targetMap.size() == 0) { + return; + } + + try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { + Iterator> iterator = targetMap.entrySet().iterator(); + while (iterator.hasNext()) { + String key = iterator.next().getKey(); + final long sum = listList.stream().mapToLong(List::size).sum(); + final long diffCount = queryCount - sum; + if (diffCount <= 0) { + return; + } + List list; + if (end - start >= diffCount) { + long endNew = start + diffCount - 1; + list = jedis.lrange(key, start, endNew); + } + else { + list = jedis.lrange(key, start, end); + } + + if (list.size() > 0) { + listList.add(list); + } + else { + iterator.remove(); + } + } + } + long startNew = end + 1; + long endNew = startNew + end - start; + dogetXidsForTargetMapRecursive(targetMap, startNew, endNew, queryCount, listList); + } + + private List> dogetXidsForTargetMap(Map targetMap, int start, int end, + int totalCount) { + List> listList = new ArrayList<>(); + try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { + for (String key : targetMap.keySet()) { + final List list = jedis.lrange(key, start, end); + final long sum = listList.stream().mapToLong(List::size).sum(); + if (list.size() > 0 && sum < totalCount) { + listList.add(list); + } + else { + start = 0; + end = totalCount - 1; + } + } + } + return listList; + } + private String buildBranchListKeyByXid(String xid) { return REDIS_SEATA_BRANCHES_PREFIX + xid; } @@ -493,4 +828,12 @@ public class RedisTransactionStoreManager extends AbstractTransactionStoreManage return REDIS_SEATA_STATUS_PREFIX + status; } + /** + * Sets log query limit. + * @param logQueryLimit the log query limit + */ + public void setLogQueryLimit(int logQueryLimit) { + this.logQueryLimit = logQueryLimit; + } + } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/AbstractTransactionStoreManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/AbstractTransactionStoreManager.java index 98c769408..1dda47e05 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/AbstractTransactionStoreManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/AbstractTransactionStoreManager.java @@ -15,9 +15,11 @@ */ package io.seata.server.store; +import io.seata.core.model.GlobalStatus; import io.seata.server.session.GlobalSession; import io.seata.server.session.SessionCondition; +import java.util.Collections; import java.util.List; /** @@ -37,9 +39,19 @@ public abstract class AbstractTransactionStoreManager implements TransactionStor return null; } + @Override + public List readSortByTimeoutBeginSessions(boolean withBranchSessions) { + return Collections.emptyList(); + } + + @Override + public List readSession(GlobalStatus[] statuses, boolean withBranchSessions) { + return Collections.emptyList(); + } + @Override public List readSession(SessionCondition sessionCondition) { - return null; + return Collections.emptyList(); } @Override diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/DruidDataSourceProvider.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/DruidDataSourceProvider.java index 9cb7a7229..9df43e463 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/DruidDataSourceProvider.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/DruidDataSourceProvider.java @@ -51,6 +51,8 @@ public class DruidDataSourceProvider extends AbstractDataSourceProvider { ds.setMaxPoolPreparedStatementPerConnectionSize(20); ds.setValidationQuery(getValidationQuery(getDBType())); ds.setDefaultAutoCommit(true); + // fix issue 5030 + ds.setUseOracleImplicitCache(false); return ds; } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/StoreConfig.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/StoreConfig.java index 11cfcfa4d..f9aaeece1 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/StoreConfig.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/StoreConfig.java @@ -15,10 +15,14 @@ */ package io.seata.server.store; +import io.seata.common.util.StringUtils; import io.seata.config.Configuration; import io.seata.config.ConfigurationFactory; +import io.seata.core.constants.ConfigurationKeys; +import io.seata.server.env.ContainerHelper; import io.seata.server.storage.file.FlushDiskMode; +import static io.seata.common.DefaultValues.SERVER_DEFAULT_STORE_MODE; import static io.seata.core.constants.ConfigurationKeys.STORE_FILE_PREFIX; /** @@ -28,6 +32,30 @@ public class StoreConfig { private static final Configuration CONFIGURATION = ConfigurationFactory.getInstance(); + private static StoreMode storeMode; + + private static SessionMode sessionMode; + + private static LockMode lockMode; + + /** + * set storeMode sessionMode lockMode from StartupParameter + * @param storeMode storeMode + * @param sessionMode sessionMode + * @param lockMode lockMode + */ + public static void setStartupParameter(String storeMode, String sessionMode, String lockMode) { + if (StringUtils.isNotBlank(storeMode)) { + StoreConfig.storeMode = StoreMode.get(storeMode); + } + if (StringUtils.isNotBlank(sessionMode)) { + StoreConfig.sessionMode = SessionMode.get(sessionMode); + } + if (StringUtils.isNotBlank(lockMode)) { + StoreConfig.lockMode = LockMode.get(lockMode); + } + } + /** * Default 16kb. */ @@ -59,4 +87,169 @@ public class StoreConfig { return FlushDiskMode.findDiskMode(CONFIGURATION.getConfig(STORE_FILE_PREFIX + "flushDiskMode")); } + /** + * only for inner call + * @return + */ + private static StoreMode getStoreMode() { + // startup + if (null != storeMode) { + return storeMode; + } + // env + String storeModeEnv = ContainerHelper.getStoreMode(); + if (StringUtils.isNotBlank(storeModeEnv)) { + return StoreMode.get(storeModeEnv); + } + // config + String storeModeConfig = CONFIGURATION.getConfig(ConfigurationKeys.STORE_MODE, SERVER_DEFAULT_STORE_MODE); + return StoreMode.get(storeModeConfig); + } + + public static SessionMode getSessionMode() { + // startup + if (null != sessionMode) { + return sessionMode; + } + // env + String sessionModeEnv = ContainerHelper.getSessionStoreMode(); + if (StringUtils.isNotBlank(sessionModeEnv)) { + return SessionMode.get(sessionModeEnv); + } + // config + String sessionModeConfig = CONFIGURATION.getConfig(ConfigurationKeys.STORE_SESSION_MODE); + if (StringUtils.isNotBlank(sessionModeConfig)) { + return SessionMode.get(sessionModeConfig); + } + // complication old config + return SessionMode.get(getStoreMode().name()); + } + + public static LockMode getLockMode() { + // startup + if (null != lockMode) { + return lockMode; + } + // env + String lockModeEnv = ContainerHelper.getLockStoreMode(); + if (StringUtils.isNotBlank(lockModeEnv)) { + return LockMode.get(lockModeEnv); + } + // config + String lockModeConfig = CONFIGURATION.getConfig(ConfigurationKeys.STORE_LOCK_MODE); + if (StringUtils.isNotBlank(lockModeConfig)) { + return LockMode.get(lockModeConfig); + } + // complication old config + return LockMode.get(getStoreMode().name()); + } + + public enum StoreMode { + + /** + * The File store mode. + */ + FILE("file"), + /** + * The Db store mode. + */ + DB("db"), + /** + * The Redis store mode. + */ + REDIS("redis"); + + private String name; + + StoreMode(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public static StoreMode get(String name) { + for (StoreMode mode : StoreMode.values()) { + if (mode.getName().equalsIgnoreCase(name)) { + return mode; + } + } + throw new IllegalArgumentException("unknown store mode:" + name); + } + + } + + public enum SessionMode { + + /** + * The File store mode. + */ + FILE("file"), + /** + * The Db store mode. + */ + DB("db"), + /** + * The Redis store mode. + */ + REDIS("redis"); + + private String name; + + SessionMode(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public static SessionMode get(String name) { + for (SessionMode mode : SessionMode.values()) { + if (mode.getName().equalsIgnoreCase(name)) { + return mode; + } + } + throw new IllegalArgumentException("unknown session mode:" + name); + } + + } + + public enum LockMode { + + /** + * The File store mode. + */ + FILE("file"), + /** + * The Db store mode. + */ + DB("db"), + /** + * The Redis store mode. + */ + REDIS("redis"); + + private String name; + + LockMode(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public static LockMode get(String name) { + for (LockMode mode : LockMode.values()) { + if (mode.getName().equalsIgnoreCase(name)) { + return mode; + } + } + throw new IllegalArgumentException("unknown lock mode:" + name); + } + + } + } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/TransactionStoreManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/TransactionStoreManager.java index 3410c0200..197582ad4 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/TransactionStoreManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/TransactionStoreManager.java @@ -15,6 +15,7 @@ */ package io.seata.server.store; +import io.seata.core.model.GlobalStatus; import io.seata.server.session.GlobalSession; import io.seata.server.session.SessionCondition; @@ -50,6 +51,21 @@ public interface TransactionStoreManager { */ GlobalSession readSession(String xid, boolean withBranchSessions); + /** + * Read session global session by sort by timeout begin status. + * @param withBranchSessions the withBranchSessions + * @return the global session + */ + List readSortByTimeoutBeginSessions(boolean withBranchSessions); + + /** + * Read session global session. + * @param statuses the statuses + * @param withBranchSessions the withBranchSessions + * @return the global session list + */ + List readSession(GlobalStatus[] statuses, boolean withBranchSessions); + /** * Read session by status list. * @param sessionCondition the session condition diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/transaction/at/ATCore.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/transaction/at/ATCore.java index bc69a1ab2..fcc098509 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/transaction/at/ATCore.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/transaction/at/ATCore.java @@ -15,6 +15,9 @@ */ package io.seata.server.transaction.at; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.seata.common.exception.StoreException; +import io.seata.common.util.StringUtils; import io.seata.core.exception.BranchTransactionException; import io.seata.core.exception.TransactionException; import io.seata.core.model.BranchType; @@ -23,6 +26,12 @@ import io.seata.server.coordinator.AbstractCore; import io.seata.server.session.BranchSession; import io.seata.server.session.GlobalSession; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import static io.seata.common.Constants.AUTO_COMMIT; +import static io.seata.common.Constants.SKIP_CHECK_LOCK; import static io.seata.core.exception.TransactionExceptionCode.LockKeyConflict; /** @@ -32,6 +41,8 @@ import static io.seata.core.exception.TransactionExceptionCode.LockKeyConflict; */ public class ATCore extends AbstractCore { + private final ObjectMapper objectMapper = new ObjectMapper(); + public ATCore(RemotingServer remotingServer) { super(remotingServer); } @@ -44,10 +55,39 @@ public class ATCore extends AbstractCore { @Override protected void branchSessionLock(GlobalSession globalSession, BranchSession branchSession) throws TransactionException { - if (!branchSession.lock()) { - throw new BranchTransactionException(LockKeyConflict, - String.format("Global lock acquire failed xid = %s branchId = %s", globalSession.getXid(), - branchSession.getBranchId())); + String applicationData = branchSession.getApplicationData(); + boolean autoCommit = true; + boolean skipCheckLock = false; + if (StringUtils.isNotBlank(applicationData)) { + try { + Map data = objectMapper.readValue(applicationData, HashMap.class); + Object clientAutoCommit = data.get(AUTO_COMMIT); + if (clientAutoCommit != null && !(boolean) clientAutoCommit) { + autoCommit = (boolean) clientAutoCommit; + } + Object clientSkipCheckLock = data.get(SKIP_CHECK_LOCK); + if (clientSkipCheckLock instanceof Boolean) { + skipCheckLock = (boolean) clientSkipCheckLock; + } + } + catch (IOException e) { + LOGGER.error("failed to get application data: {}", e.getMessage(), e); + } + } + try { + if (!branchSession.lock(autoCommit, skipCheckLock)) { + throw new BranchTransactionException(LockKeyConflict, + String.format("Global lock acquire failed xid = %s branchId = %s", globalSession.getXid(), + branchSession.getBranchId())); + } + } + catch (StoreException e) { + if (e.getCause() instanceof BranchTransactionException) { + throw new BranchTransactionException(((BranchTransactionException) e.getCause()).getCode(), + String.format("Global lock acquire failed xid = %s branchId = %s", globalSession.getXid(), + branchSession.getBranchId())); + } + throw e; } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/transaction/saga/SagaCore.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/transaction/saga/SagaCore.java index 6fa31b097..edeccde55 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/transaction/saga/SagaCore.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/transaction/saga/SagaCore.java @@ -15,11 +15,6 @@ */ package io.seata.server.transaction.saga; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Map; -import java.util.concurrent.TimeoutException; - import io.netty.channel.Channel; import io.seata.common.util.CollectionUtils; import io.seata.core.exception.GlobalTransactionException; @@ -31,14 +26,18 @@ import io.seata.core.protocol.transaction.BranchCommitRequest; import io.seata.core.protocol.transaction.BranchCommitResponse; import io.seata.core.protocol.transaction.BranchRollbackRequest; import io.seata.core.protocol.transaction.BranchRollbackResponse; -import io.seata.core.rpc.netty.ChannelManager; import io.seata.core.rpc.RemotingServer; +import io.seata.core.rpc.netty.ChannelManager; import io.seata.server.coordinator.AbstractCore; import io.seata.server.session.BranchSession; import io.seata.server.session.GlobalSession; import io.seata.server.session.SessionHelper; import io.seata.server.session.SessionHolder; +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.TimeoutException; + /** * The type saga core. * @@ -107,13 +106,13 @@ public class SagaCore extends AbstractCore { switch (branchStatus) { case PhaseTwo_Committed: - removeAllBranches(globalSession); + SessionHelper.removeAllBranch(globalSession, !retrying); LOGGER.info("Successfully committed SAGA global[" + globalSession.getXid() + "]"); break; case PhaseTwo_Rollbacked: LOGGER.info("Successfully rollbacked SAGA global[" + globalSession.getXid() + "]"); - removeAllBranches(globalSession); - SessionHelper.endRollbacked(globalSession); + SessionHelper.removeAllBranch(globalSession, !retrying); + SessionHelper.endRollbacked(globalSession, retrying); return false; case PhaseTwo_RollbackFailed_Retryable: LOGGER.error("By [{}], failed to rollback SAGA global [{}], will retry later.", branchStatus, @@ -123,8 +122,8 @@ public class SagaCore extends AbstractCore { return false; case PhaseOne_Failed: LOGGER.error("By [{}], finish SAGA global [{}]", branchStatus, globalSession.getXid()); - removeAllBranches(globalSession); - globalSession.changeStatus(GlobalStatus.Finished); + SessionHelper.removeAllBranch(globalSession, !retrying); + globalSession.changeGlobalStatus(GlobalStatus.Finished); globalSession.end(); return false; case PhaseTwo_CommitFailed_Unretryable: @@ -134,19 +133,18 @@ public class SagaCore extends AbstractCore { break; } else { - SessionHelper.endCommitFailed(globalSession); + SessionHelper.endCommitFailed(globalSession, retrying); LOGGER.error("Finally, failed to commit SAGA global[{}]", globalSession.getXid()); return false; } default: if (!retrying) { globalSession.queueToRetryCommit(); - return false; } else { LOGGER.error("Failed to commit SAGA global[{}], will retry later.", globalSession.getXid()); - return false; } + return false; } } catch (Exception ex) { @@ -168,11 +166,11 @@ public class SagaCore extends AbstractCore { switch (branchStatus) { case PhaseTwo_Rollbacked: - removeAllBranches(globalSession); + SessionHelper.removeAllBranch(globalSession, !retrying); LOGGER.info("Successfully rollbacked SAGA global[{}]", globalSession.getXid()); break; case PhaseTwo_RollbackFailed_Unretryable: - SessionHelper.endRollbackFailed(globalSession); + SessionHelper.endRollbackFailed(globalSession, retrying); LOGGER.error("Failed to rollback SAGA global[{}]", globalSession.getXid()); return false; case PhaseTwo_CommitFailed_Retryable: @@ -203,17 +201,17 @@ public class SagaCore extends AbstractCore { public void doGlobalReport(GlobalSession globalSession, String xid, GlobalStatus globalStatus) throws TransactionException { if (GlobalStatus.Committed.equals(globalStatus)) { - removeAllBranches(globalSession); - SessionHelper.endCommitted(globalSession); + SessionHelper.removeAllBranch(globalSession, false); + SessionHelper.endCommitted(globalSession, false); LOGGER.info("Global[{}] committed", globalSession.getXid()); } else if (GlobalStatus.Rollbacked.equals(globalStatus) || GlobalStatus.Finished.equals(globalStatus)) { - removeAllBranches(globalSession); - SessionHelper.endRollbacked(globalSession); + SessionHelper.removeAllBranch(globalSession, false); + SessionHelper.endRollbacked(globalSession, false); LOGGER.info("Global[{}] rollbacked", globalSession.getXid()); } else { - globalSession.changeStatus(globalStatus); + globalSession.changeGlobalStatus(globalStatus); LOGGER.info("Global[{}] reporting is successfully done. status[{}]", globalSession.getXid(), globalSession.getStatus()); @@ -230,18 +228,6 @@ public class SagaCore extends AbstractCore { } } - /** - * remove all branches - * @param globalSession the globalSession - * @throws TransactionException the TransactionException - */ - private void removeAllBranches(GlobalSession globalSession) throws TransactionException { - ArrayList branchSessions = globalSession.getSortedBranches(); - for (BranchSession branchSession : branchSessions) { - globalSession.removeBranch(branchSession); - } - } - /** * get saga ResourceId * @param globalSession the globalSession diff --git a/pigx-visual/pigx-seata-server/src/main/resources/META-INF/services/io.seata.core.store.DistributedLocker b/pigx-visual/pigx-seata-server/src/main/resources/META-INF/services/io.seata.core.store.DistributedLocker new file mode 100644 index 000000000..874e8b91c --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/resources/META-INF/services/io.seata.core.store.DistributedLocker @@ -0,0 +1,2 @@ +io.seata.server.storage.redis.lock.RedisDistributedLocker +io.seata.server.storage.db.lock.DataBaseDistributedLocker \ No newline at end of file diff --git a/pigx-visual/pigx-seata-server/src/main/resources/META-INF/spring-configuration-metadata.json b/pigx-visual/pigx-seata-server/src/main/resources/META-INF/spring-configuration-metadata.json new file mode 100644 index 000000000..054491775 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/resources/META-INF/spring-configuration-metadata.json @@ -0,0 +1,22 @@ +{ + "groups": [], + "properties": [ + { + "name": "logging.extend.kafka-appender.bootstrap-servers", + "type": "java.lang.String", + "defaultValue": "localhost:9092" + }, + { + "name": "logging.extend.kafka-appender.topic", + "type": "java.lang.String", + "defaultValue": "logback_to_logstash" + }, + { + "name": "logging.extend.logstash-appender.destination", + "type": "java.lang.String", + "defaultValue": "localhost:4560" + } + ], + "hints": [ + ] +} \ No newline at end of file diff --git a/pigx-visual/pigx-seata-server/src/main/resources/META-INF/spring.factories b/pigx-visual/pigx-seata-server/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000..915bf1b78 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.context.ApplicationListener=\ +io.seata.server.ServerApplicationListener \ No newline at end of file diff --git a/pigx-visual/pigx-seata-server/src/main/resources/README-zh.md b/pigx-visual/pigx-seata-server/src/main/resources/README-zh.md new file mode 100644 index 000000000..05d1620e8 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/resources/README-zh.md @@ -0,0 +1,32 @@ +# 脚本说明 + +## [client](https://github.com/seata/seata/tree/develop/script/client) + +> 存放用于客户端的配置和SQL + +- at: AT模式下的 `undo_log` 建表语句 +- conf: 客户端的配置文件 +- saga: SAGA 模式下所需表的建表语句 +- spring: SpringBoot 应用支持的配置文件 + +## [server](https://github.com/seata/seata/tree/develop/script/server) + +> 存放server侧所需SQL和部署脚本 + +- db: server 侧的保存模式为 `db` 时所需表的建表语句 +- docker-compose: server 侧通过 docker-compose 部署的脚本 +- helm: server 侧通过 Helm 部署的脚本 +- kubernetes: server 侧通过 Kubernetes 部署的脚本 + +## [config-center](https://github.com/seata/seata/tree/develop/script/config-center) + +> 用于存放各种配置中心的初始化脚本,执行时都会读取 `config.txt`配置文件,并写入配置中心 + +- nacos: 用于向 Nacos 中添加配置 +- zk: 用于向 Zookeeper 中添加配置,脚本依赖 Zookeeper 的相关脚本,需要手动下载;ZooKeeper相关的配置可以写在 `zk-params.txt` 中,也可以在执行的时候输入 +- apollo: 向 Apollo 中添加配置,Apollo 的地址端口等可以写在 `apollo-params.txt`,也可以在执行的时候输入 +- etcd3: 用于向 Etcd3 中添加配置 +- consul: 用于向 consul 中添加配置 + +## 打包 +./mvnw -Prelease-seata -Dmaven.test.skip=true clean install -U diff --git a/pigx-visual/pigx-seata-server/src/main/resources/README.md b/pigx-visual/pigx-seata-server/src/main/resources/README.md new file mode 100644 index 000000000..ddabd559c --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/resources/README.md @@ -0,0 +1,33 @@ +# Script Description + +## [client](https://github.com/seata/seata/tree/develop/script/client) + +> Store configuration and SQL for client side + +- at: Script of create table `undo_log` for AT mode. +- conf: Configuration which client need. +- saga: Script of create table in SAGA mode +- spring: Configuration for Spring Boot + +## [server](https://github.com/seata/seata/tree/develop/script/server) + +> Store SQL and deploy script for server side + +- db: Create table script for server when store mode is `db` +- docker-compose: Script for deploy server by docker-compose +- helm: Script for deploy server by Helm +- kubernetes: Script for deploy server by Kubernetes + +## [config-center](https://github.com/seata/seata/tree/develop/script/config-center) + +> Store initialize script for configuration center, will use `config.txt` as configuration when initial + +- nacos: Initialize script for Nacos +- zk: Initialize script for ZooKeeper, the script need related script in Zookeeper, you need download yourself. You can modify `zk-params.txt` to change the ZooKeeper server configuration, or input when execute also +- apollo: Initialize script for Apollo. You can modify `apollo-params.txt` to change the Apollo server configuration, or input when execute also +- etcd3: Initialize script for Etcd3 +- consul: Initialize script for consul + +## build packege +./mvnw -Prelease-seata -Dmaven.test.skip=true clean install -U + diff --git a/pigx-visual/pigx-seata-server/src/main/resources/application.yml b/pigx-visual/pigx-seata-server/src/main/resources/application.yml new file mode 100644 index 000000000..16822c55e --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/resources/application.yml @@ -0,0 +1,54 @@ +# 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. + +server: + port: 7091 + +spring: + application: + name: seata-server + +logging: + config: classpath:logback-spring.xml + file: + path: ${user.home}/logs/seata + extend: + logstash-appender: + destination: 127.0.0.1:4560 + kafka-appender: + bootstrap-servers: 127.0.0.1:9092 + topic: logback_to_logstash + +console: + user: + username: seata + password: seata + +seata: + config: + # support: nacos, consul, apollo, zk, etcd3 + type: file + registry: + # support: nacos, eureka, redis, zk, consul, etcd3, sofa + type: file + store: + # support: file 、 db 、 redis + mode: file +# server: +# service-port: 8091 #If not configured, the default is '${server.port} + 1000' + security: + secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017 + tokenValidityInMilliseconds: 1800000 + ignore: + urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/api/v1/auth/login \ No newline at end of file diff --git a/pigx-visual/pigx-seata-server/src/main/resources/banner.txt b/pigx-visual/pigx-seata-server/src/main/resources/banner.txt new file mode 100644 index 000000000..e561e763b --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/resources/banner.txt @@ -0,0 +1,7 @@ +███████╗███████╗ █████╗ ████████╗ █████╗ +██╔════╝██╔════╝██╔══██╗╚══██╔══╝██╔══██╗ +███████╗█████╗ ███████║ ██║ ███████║ +╚════██║██╔══╝ ██╔══██║ ██║ ██╔══██║ +███████║███████╗██║ ██║ ██║ ██║ ██║ +╚══════╝╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ + diff --git a/pigx-visual/pigx-seata-server/src/main/resources/file.conf b/pigx-visual/pigx-seata-server/src/main/resources/file.conf deleted file mode 100644 index bf50039f3..000000000 --- a/pigx-visual/pigx-seata-server/src/main/resources/file.conf +++ /dev/null @@ -1,65 +0,0 @@ -## transaction log store, only used in seata-server -store { - ## store mode: file、db、redis - mode = "file" - ## rsa decryption public key - publicKey = "" - ## file store property - file { - ## store location dir - dir = "sessionStore" - # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions - maxBranchSessionSize = 16384 - # globe session size , if exceeded throws exceptions - maxGlobalSessionSize = 512 - # file buffer size , if exceeded allocate new buffer - fileWriteBufferCacheSize = 16384 - # when recover batch read size - sessionReloadReadSize = 100 - # async, sync - flushDiskMode = async - } - - ## database store property - db { - ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc. - datasource = "druid" - ## mysql/oracle/postgresql/h2/oceanbase etc. - dbType = "mysql" - driverClassName = "com.mysql.jdbc.Driver" - ## if using mysql to store the data, recommend add rewriteBatchedStatements=true in jdbc connection param - url = "jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true" - user = "mysql" - password = "mysql" - minConn = 5 - maxConn = 100 - globalTable = "global_table" - branchTable = "branch_table" - lockTable = "lock_table" - queryLimit = 100 - maxWait = 5000 - } - - ## redis store property - redis { - ## redis mode: single、sentinel - mode = "single" - ## single mode property - single { - host = "127.0.0.1" - port = "6379" - } - ## sentinel mode property - sentinel { - masterName = "" - ## such as "10.28.235.65:26379,10.28.235.65:26380,10.28.235.65:26381" - sentinelHosts = "" - } - password = "" - database = "0" - minConn = 1 - maxConn = 10 - maxTotal = 100 - queryLimit = 100 - } -} diff --git a/pigx-visual/pigx-seata-server/src/main/resources/logback.xml b/pigx-visual/pigx-seata-server/src/main/resources/logback-spring.xml similarity index 67% rename from pigx-visual/pigx-seata-server/src/main/resources/logback.xml rename to pigx-visual/pigx-seata-server/src/main/resources/logback-spring.xml index 02399e5dd..7d7c2cf21 100644 --- a/pigx-visual/pigx-seata-server/src/main/resources/logback.xml +++ b/pigx-visual/pigx-seata-server/src/main/resources/logback-spring.xml @@ -19,13 +19,16 @@ - - - - + + + + + + - + + diff --git a/pigx-visual/pigx-seata-server/src/main/resources/logback/console-appender.xml b/pigx-visual/pigx-seata-server/src/main/resources/logback/console-appender.xml index 682b1969a..e18e06823 100644 --- a/pigx-visual/pigx-seata-server/src/main/resources/logback/console-appender.xml +++ b/pigx-visual/pigx-seata-server/src/main/resources/logback/console-appender.xml @@ -1,7 +1,24 @@ + + - + diff --git a/pigx-visual/pigx-seata-server/src/main/resources/logback/file-appender.xml b/pigx-visual/pigx-seata-server/src/main/resources/logback/file-appender.xml index cb54dd4e6..bfd2d0326 100644 --- a/pigx-visual/pigx-seata-server/src/main/resources/logback/file-appender.xml +++ b/pigx-visual/pigx-seata-server/src/main/resources/logback/file-appender.xml @@ -1,15 +1,33 @@ + + - - + + - ${LOG_HOME}/${APPLICATION_NAME:-}.${PORT}.all.log + ${LOG_FILE_PATH}/${APPLICATION_NAME:-seata-server}.${RPC_PORT}.all.log true - ${LOG_HOME}/history/${APPLICATION_NAME:-}.${PORT}.all.%d{yyyy-MM-dd}.%i.log.gz + ${LOG_FILE_PATH}/history/${APPLICATION_NAME:-seata-server}.${RPC_PORT}.all.%d{yyyy-MM-dd}.%i.log.gz 2GB 7 7GB @@ -28,10 +46,10 @@ ACCEPT DENY - ${LOG_HOME}/${APPLICATION_NAME:-}.${PORT}.warn.log + ${LOG_FILE_PATH}/${APPLICATION_NAME:-seata-server}.${RPC_PORT}.warn.log true - ${LOG_HOME}/history/${APPLICATION_NAME:-}.${PORT}.warn.%d{yyyy-MM-dd}.%i.log.gz + ${LOG_FILE_PATH}/history/${APPLICATION_NAME:-seata-server}.${RPC_PORT}.warn.%d{yyyy-MM-dd}.%i.log.gz 2GB 7 7GB @@ -50,10 +68,10 @@ ACCEPT DENY - ${LOG_HOME}/${APPLICATION_NAME:-}.${PORT}.error.log + ${LOG_FILE_PATH}/${APPLICATION_NAME:-seata-server}.${RPC_PORT}.error.log true - ${LOG_HOME}/history/${APPLICATION_NAME:-}.${PORT}.error.%d{yyyy-MM-dd}.%i.log.gz + ${LOG_FILE_PATH}/history/${APPLICATION_NAME:-seata-server}.${RPC_PORT}.error.%d{yyyy-MM-dd}.%i.log.gz 2GB 7 7GB diff --git a/pigx-visual/pigx-seata-server/src/main/resources/logback/kafka-appender.xml b/pigx-visual/pigx-seata-server/src/main/resources/logback/kafka-appender.xml index 2b30a7598..a13000a23 100644 --- a/pigx-visual/pigx-seata-server/src/main/resources/logback/kafka-appender.xml +++ b/pigx-visual/pigx-seata-server/src/main/resources/logback/kafka-appender.xml @@ -1,17 +1,35 @@ + + - - + + - + { "@timestamp": "%d{yyyy-MM-dd HH:mm:ss.SSS}", "level":"%p", - "app_name":"${APPLICATION_NAME:-}", - "PORT": ${PORT:-0}, + "app_name":"${APPLICATION_NAME:-seata-server}", + "PORT": ${RPC_PORT:-0}, "thread_name": "%t", "logger_name": "%logger", "X-TX-XID": "%X{X-TX-XID:-}", diff --git a/pigx-visual/pigx-seata-server/src/main/resources/logback/logstash-appender.xml b/pigx-visual/pigx-seata-server/src/main/resources/logback/logstash-appender.xml index 18b9c25c0..2c2f97215 100644 --- a/pigx-visual/pigx-seata-server/src/main/resources/logback/logstash-appender.xml +++ b/pigx-visual/pigx-seata-server/src/main/resources/logback/logstash-appender.xml @@ -1,7 +1,24 @@ + + - + @@ -12,13 +29,13 @@ { - "app_name": "${APPLICATION_NAME:-}" + "app_name": "${APPLICATION_NAME:-seata-server}" } - + net.logstash.logback.composite.LogstashVersionJsonProvider - + net.logstash.logback.composite.loggingevent.JsonMessageJsonProvider net.logstash.logback.composite.loggingevent.TagsJsonProvider net.logstash.logback.composite.loggingevent.LogstashMarkersJsonProvider diff --git a/pigx-visual/pigx-seata-server/src/main/resources/redislocker/redislock.lua b/pigx-visual/pigx-seata-server/src/main/resources/redislocker/redislock.lua new file mode 100644 index 000000000..b004408c9 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/resources/redislocker/redislock.lua @@ -0,0 +1,52 @@ +-- +-- User: tianyu.li +-- Date: 2021/1/19 +-- +-- init data +local array = {}; local result; local keySize = ARGV[1]; local argSize = ARGV[2]; +-- Loop through all keys to see if they can be used , when a key is not available, exit +for i= 1, keySize do + -- search lock xid + result = redis.call('HGET',KEYS[i],'xid'); + -- if lock xid is nil + if (not result) + -- set 'no' mean There is need to store lock information + then array[i]='no' + else + if (result ~= ARGV[3]) + then + -- return fail + return result + else + -- set 'yes' mean There is not need to store lock information + array[i]= 'yes' + end + end +end +-- Loop through array +for i =1, keySize do + -- if is no ,The lock information is stored + if(array[i] == 'no') + then + -- set xid + redis.call('HSET',KEYS[i],'xid',ARGV[3]); + -- set transactionId + redis.call('HSET',KEYS[i],'transactionId',ARGV[(i-1)*6+4]); + -- set branchId + redis.call('HSET',KEYS[i],'branchId',ARGV[(i-1)*6+5]); + -- set resourceId + redis.call('HSET',KEYS[i],'resourceId',ARGV[(i-1)*6+6]); + -- set tableName + redis.call('HSET',KEYS[i],'tableName',ARGV[(i-1)*6+7]); + -- set rowKey + redis.call('HSET',KEYS[i],'rowKey',ARGV[(i-1)*6+8]); + -- set pk + redis.call('HSET',KEYS[i],'pk',ARGV[(i-1)*6+9]); + -- exit if + end +-- exit for +end +-- set SEATA_GLOBAL_LOCK +redis.call('HSET',KEYS[(keySize+1)],KEYS[(keySize+2)],ARGV[(argSize+0)]); +-- return success +return ARGV[3] diff --git a/pigx-visual/pigx-seata-server/src/main/resources/registry.conf b/pigx-visual/pigx-seata-server/src/main/resources/registry.conf deleted file mode 100644 index 8fb49b27a..000000000 --- a/pigx-visual/pigx-seata-server/src/main/resources/registry.conf +++ /dev/null @@ -1,96 +0,0 @@ -registry { - # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa - type = "file" - - nacos { - application = "seata-server" - serverAddr = "127.0.0.1:8848" - group = "SEATA_GROUP" - namespace = "" - cluster = "default" - username = "" - password = "" - } - eureka { - serviceUrl = "http://localhost:8761/eureka" - application = "default" - weight = "1" - } - redis { - serverAddr = "localhost:6379" - db = 0 - password = "" - cluster = "default" - timeout = 0 - } - zk { - cluster = "default" - serverAddr = "127.0.0.1:2181" - sessionTimeout = 6000 - connectTimeout = 2000 - username = "" - password = "" - } - consul { - cluster = "default" - serverAddr = "127.0.0.1:8500" - aclToken = "" - } - etcd3 { - cluster = "default" - serverAddr = "http://localhost:2379" - } - sofa { - serverAddr = "127.0.0.1:9603" - application = "default" - region = "DEFAULT_ZONE" - datacenter = "DefaultDataCenter" - cluster = "default" - group = "SEATA_GROUP" - addressWaitTime = "3000" - } - file { - name = "file.conf" - } -} - -config { - # file、nacos 、apollo、zk、consul、etcd3 - type = "file" - - nacos { - serverAddr = "127.0.0.1:8848" - namespace = "" - group = "SEATA_GROUP" - username = "" - password = "" - dataId = "seataServer.properties" - } - consul { - serverAddr = "127.0.0.1:8500" - aclToken = "" - } - apollo { - appId = "seata-server" - ## apolloConfigService will cover apolloMeta - apolloMeta = "http://192.168.1.204:8801" - apolloConfigService = "http://192.168.1.204:8080" - namespace = "application" - apolloAccesskeySecret = "" - cluster = "seata" - } - zk { - serverAddr = "127.0.0.1:2181" - sessionTimeout = 6000 - connectTimeout = 2000 - username = "" - password = "" - nodePath = "/seata/seata.properties" - } - etcd3 { - serverAddr = "http://localhost:2379" - } - file { - name = "file.conf" - } -}