mirror of
http://git.pig4cloud.com/pig/pigx.git
synced 2026-11-12 05:39:00 +08:00
Merge remote-tracking branch 'origin/lei_dev' into leng_dev
This commit is contained in:
commit
dc9f05d975
101 files changed
+6131
-2030
No files matched your search
@@ -28,111 +28,171 @@
|
||||
<name>pigx-seata-server</name>
|
||||
|
||||
<properties>
|
||||
<seata.version>1.4.2</seata.version>
|
||||
<seata.version>1.6.1</seata.version>
|
||||
<logstash-logback-encoder.version>6.5</logstash-logback-encoder.version>
|
||||
<kafka-appender.version>0.2.0-RC2</kafka-appender.version>
|
||||
<jcommander.version>1.72</jcommander.version>
|
||||
<guava.version>27.0.1-jre</guava.version>
|
||||
</properties>
|
||||
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.seata</groupId>
|
||||
<artifactId>seata-dependencies</artifactId>
|
||||
<version>${seata.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.seata</groupId>
|
||||
<artifactId>seata-core</artifactId>
|
||||
<version>${seata.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.seata</groupId>
|
||||
<artifactId>seata-config-all</artifactId>
|
||||
<version>${seata.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.seata</groupId>
|
||||
<artifactId>seata-discovery-all</artifactId>
|
||||
<version>${seata.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.seata</groupId>
|
||||
<artifactId>seata-serializer-all</artifactId>
|
||||
<version>${seata.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.seata</groupId>
|
||||
<artifactId>seata-compressor-all</artifactId>
|
||||
<version>${seata.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.seata</groupId>
|
||||
<artifactId>seata-spring-autoconfigure-server</artifactId>
|
||||
<version>${seata.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.seata</groupId>
|
||||
<artifactId>seata-metrics-all</artifactId>
|
||||
<version>${seata.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.seata</groupId>
|
||||
<artifactId>seata-core</artifactId>
|
||||
<version>${seata.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.seata</groupId>
|
||||
<artifactId>seata-config-all</artifactId>
|
||||
<version>${seata.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>log4j</artifactId>
|
||||
<groupId>log4j</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.seata</groupId>
|
||||
<artifactId>seata-discovery-all</artifactId>
|
||||
<version>${seata.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.seata</groupId>
|
||||
<artifactId>seata-serializer-all</artifactId>
|
||||
<version>${seata.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.seata</groupId>
|
||||
<artifactId>seata-compressor-all</artifactId>
|
||||
<version>${seata.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- for database -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-dbcp2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.zaxxer</groupId>
|
||||
<artifactId>HikariCP</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.beust</groupId>
|
||||
<artifactId>jcommander</artifactId>
|
||||
<version>${jcommander.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.seata</groupId>
|
||||
<artifactId>seata-metrics-all</artifactId>
|
||||
<version>${seata.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- only for event bus -->
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>${guava.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.seata</groupId>
|
||||
<artifactId>seata-console</artifactId>
|
||||
<version>${seata.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- logback -->
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
</dependency>
|
||||
<!-- for database -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-dbcp2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.zaxxer</groupId>
|
||||
<artifactId>HikariCP</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
</dependency>
|
||||
<!-- if you run seata-server in IDE and use mysql8 as session store, please rewrite version to ${mysql8.jdbc.version}-->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
</dependency>
|
||||
<!-- Copyright restrictions, do not reference this dependency.
|
||||
You can add this dependency to the '/seata/lib' directory of the seata-server when necessary.
|
||||
<dependency>
|
||||
<groupId>com.oracle.ojdbc</groupId>
|
||||
<artifactId>ojdbc8</artifactId>
|
||||
<version>${ojdbc.version}</version>
|
||||
</dependency>-->
|
||||
|
||||
<!-- jedis -->
|
||||
<dependency>
|
||||
<groupId>redis.clients</groupId>
|
||||
<artifactId>jedis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.beust</groupId>
|
||||
<artifactId>jcommander</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- only for event bus -->
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- jedis -->
|
||||
<dependency>
|
||||
<groupId>redis.clients</groupId>
|
||||
<artifactId>jedis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.microwww</groupId>
|
||||
<artifactId>redis-server</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- logback -->
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-core</artifactId>
|
||||
</dependency>
|
||||
<!-- logback appenders -->
|
||||
<dependency>
|
||||
<groupId>net.logstash.logback</groupId>
|
||||
<artifactId>logstash-logback-encoder</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.danielwegener</groupId>
|
||||
<artifactId>logback-kafka-appender</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--web 模块-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<!--undertow容器-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-undertow</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.logstash.logback</groupId>
|
||||
<artifactId>logstash-logback-encoder</artifactId>
|
||||
<version>${logstash-logback-encoder.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.danielwegener</groupId>
|
||||
<artifactId>logback-kafka-appender</artifactId>
|
||||
<version>${kafka-appender.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
+1
-19
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
+28
-45
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
+129
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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> 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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-1
@@ -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;
|
||||
|
||||
-1
@@ -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;
|
||||
|
||||
+36
@@ -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;
|
||||
|
||||
}
|
||||
+51
@@ -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<GlobalLockVO> query(@ModelAttribute GlobalLockParam param) {
|
||||
return globalLockService.query(param);
|
||||
}
|
||||
|
||||
}
|
||||
+51
@@ -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<GlobalSessionVO> query(@ModelAttribute GlobalSessionParam param) {
|
||||
return globalSessionService.query(param);
|
||||
}
|
||||
|
||||
}
|
||||
+104
@@ -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<BranchSessionVO> 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<BranchSessionVO> 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);
|
||||
}
|
||||
|
||||
}
|
||||
+146
@@ -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<GlobalLockVO> query(GlobalLockParam param) {
|
||||
PageUtil.checkParam(param.getPageNum(), param.getPageSize());
|
||||
|
||||
List<Object> 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<GlobalLockVO> 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<Object> 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");
|
||||
}
|
||||
|
||||
}
|
||||
+162
@@ -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<GlobalSessionVO> query(GlobalSessionParam param) {
|
||||
PageUtil.checkParam(param.getPageNum(), param.getPageSize());
|
||||
|
||||
List<Object> 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<GlobalSessionVO> 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<BranchSessionVO> 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<Object> 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");
|
||||
}
|
||||
|
||||
}
|
||||
+40
@@ -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<BranchSessionVO> queryByXid(String xid) {
|
||||
throw new NotSupportYetException();
|
||||
}
|
||||
|
||||
}
|
||||
+159
@@ -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<GlobalLockVO> query(GlobalLockParam param) {
|
||||
checkParam(param);
|
||||
|
||||
final Collection<GlobalSession> allSessions = SessionHolder.getRootSessionManager().allSessions();
|
||||
|
||||
final AtomicInteger total = new AtomicInteger();
|
||||
List<RowLock> 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<RowLock> filterAndMap(GlobalLockParam param, BranchSession branchSession) {
|
||||
|
||||
final String tableName = param.getTableName();
|
||||
|
||||
// get rowLock from branchSession
|
||||
final List<RowLock> 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<? super BranchSession> 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<? super GlobalSession> 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());
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
+98
@@ -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<GlobalSessionVO> query(GlobalSessionParam param) {
|
||||
if (param.getPageSize() <= 0 || param.getPageNum() <= 0) {
|
||||
throw new IllegalArgumentException("wrong pageSize or pageNum");
|
||||
}
|
||||
|
||||
final Collection<GlobalSession> allSessions = SessionHolder.getRootSessionManager().allSessions();
|
||||
|
||||
final List<GlobalSession> 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<? super GlobalSession> 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());
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
+66
@@ -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<BranchSessionVO> queryByXid(String xid) {
|
||||
if (StringUtils.isBlank(xid)) {
|
||||
return PageResult.success();
|
||||
}
|
||||
|
||||
List<BranchSessionVO> branchSessionVos = new ArrayList<>();
|
||||
|
||||
RedisTransactionStoreManager instance = RedisTransactionStoreManager.getInstance();
|
||||
|
||||
List<BranchTransactionDO> 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());
|
||||
}
|
||||
|
||||
}
|
||||
+121
@@ -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<GlobalLockVO> query(GlobalLockParam param) {
|
||||
|
||||
int total = 0;
|
||||
List<GlobalLockVO> 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<GlobalLockVO> 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<GlobalLockVO> queryGlobalByXid(String xid) {
|
||||
return readGlobalLockByXid(DEFAULT_REDIS_SEATA_GLOBAL_LOCK_PREFIX + xid);
|
||||
}
|
||||
|
||||
private List<GlobalLockVO> readGlobalLockByXid(String key) {
|
||||
List<GlobalLockVO> vos = new ArrayList<>();
|
||||
try (Jedis jedis = JedisPooledFactory.getJedisInstance()) {
|
||||
Map<String, String> mapGlobalKeys = jedis.hgetAll(key);
|
||||
if (CollectionUtils.isNotEmpty(mapGlobalKeys)) {
|
||||
List<String> rowLockKeys = new ArrayList<>();
|
||||
mapGlobalKeys.forEach((k, v) -> rowLockKeys.addAll(Arrays.asList(v.split(ROW_LOCK_KEY_SPLIT_CHAR))));
|
||||
for (String rowLoclKey : rowLockKeys) {
|
||||
Map<String, String> mapRowLockKey = jedis.hgetAll(rowLoclKey);
|
||||
GlobalLockVO vo = (GlobalLockVO) BeanUtils.mapToObject(mapRowLockKey, GlobalLockVO.class);
|
||||
if (vo != null) {
|
||||
vos.add(vo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return vos;
|
||||
}
|
||||
|
||||
private List<GlobalLockVO> readGlobalLockByRowKey(String key) {
|
||||
List<GlobalLockVO> vos = new ArrayList<>();
|
||||
try (Jedis jedis = JedisPooledFactory.getJedisInstance()) {
|
||||
Map<String, String> map = jedis.hgetAll(key);
|
||||
GlobalLockVO vo = (GlobalLockVO) BeanUtils.mapToObject(map, GlobalLockVO.class);
|
||||
if (vo != null) {
|
||||
vos.add(vo);
|
||||
}
|
||||
}
|
||||
return vos;
|
||||
}
|
||||
|
||||
}
|
||||
+116
@@ -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<GlobalSessionVO> query(GlobalSessionParam param) {
|
||||
List<GlobalSessionVO> 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<GlobalSession> 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<GlobalSession> 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());
|
||||
}
|
||||
|
||||
}
|
||||
+116
@@ -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 + '\'' + '}';
|
||||
}
|
||||
|
||||
}
|
||||
+102
@@ -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 + '}';
|
||||
}
|
||||
|
||||
}
|
||||
+11
-8
@@ -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<BranchSessionVO> queryByXid(String xid);
|
||||
|
||||
}
|
||||
+12
-41
@@ -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<GlobalLockVO> query(GlobalLockParam param);
|
||||
|
||||
}
|
||||
+36
@@ -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<GlobalSessionVO> query(GlobalSessionParam param);
|
||||
|
||||
}
|
||||
+213
@@ -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 + '}';
|
||||
}
|
||||
|
||||
}
|
||||
+188
@@ -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<GlobalLockVO> convert(List<RowLock> rowLocks) {
|
||||
if (CollectionUtils.isEmpty(rowLocks)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final List<GlobalLockVO> 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 + '}';
|
||||
}
|
||||
|
||||
}
|
||||
+203
@@ -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<BranchSessionVO> branchSessionVOs;
|
||||
|
||||
public GlobalSessionVO() {
|
||||
|
||||
}
|
||||
|
||||
public GlobalSessionVO(String xid, Long transactionId, Integer status, String applicationId,
|
||||
String transactionServiceGroup, String transactionName, Long timeout, Long beginTime,
|
||||
String applicationData, Set<BranchSessionVO> 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<BranchSessionVO> getBranchSessionVOs() {
|
||||
return branchSessionVOs;
|
||||
}
|
||||
|
||||
public void setBranchSessionVOs(Set<BranchSessionVO> 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 + '}';
|
||||
}
|
||||
|
||||
}
|
||||
+44
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+5
-9
@@ -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);
|
||||
|
||||
|
||||
+258
-171
@@ -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<GlobalSession> allSessions = SessionHolder.getRootSessionManager().allSessions();
|
||||
if (CollectionUtils.isEmpty(allSessions)) {
|
||||
protected void timeoutCheck() {
|
||||
SessionCondition sessionCondition = new SessionCondition(GlobalStatus.Begin);
|
||||
sessionCondition.setLazyLoadBranch(true);
|
||||
Collection<GlobalSession> 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<GlobalSession> rollbackingSessions = SessionHolder.getRetryRollbackingSessionManager().allSessions();
|
||||
SessionCondition sessionCondition = new SessionCondition(rollbackingStatuses);
|
||||
sessionCondition.setLazyLoadBranch(true);
|
||||
Collection<GlobalSession> 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<GlobalSession> committingSessions = SessionHolder.getRetryCommittingSessionManager().allSessions();
|
||||
SessionCondition retryCommittingSessionCondition = new SessionCondition(retryCommittingStatuses);
|
||||
retryCommittingSessionCondition.setLazyLoadBranch(true);
|
||||
Collection<GlobalSession> 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<GlobalSession> 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+73
-78
@@ -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<BranchType, AbstractCore> 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+21
-27
@@ -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<String> 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));
|
||||
}
|
||||
|
||||
}
|
||||
+100
-8
@@ -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<String, Object> yamlMap = new Yaml().load(inputStream);
|
||||
Map<String, Object> 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;
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
+26
-17
@@ -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<RowLock> collectRowLocks(BranchSession branchSession) {
|
||||
List<RowLock> locks = new ArrayList<>();
|
||||
@Override
|
||||
public List<RowLock> 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<RowLock> collectRowLocks(String lockKey, String resourceId, String xid, Long transactionId,
|
||||
Long branchID) {
|
||||
List<RowLock> locks = new ArrayList<RowLock>();
|
||||
List<RowLock> 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<RowLock> 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;
|
||||
|
||||
}
|
||||
+29
-3
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+71
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -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;
|
||||
}
|
||||
|
||||
-93
@@ -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<ILoggingEvent> {
|
||||
|
||||
private static final Map<String, AnsiElement> ELEMENTS;
|
||||
|
||||
private static final String DISABLE_PROPERTY_NAME = "logback.color.disable-for-bat";
|
||||
|
||||
static {
|
||||
Map<String, AnsiElement> 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<Integer, AnsiElement> LEVELS;
|
||||
|
||||
static {
|
||||
Map<Integer, AnsiElement> 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);
|
||||
}
|
||||
|
||||
}
|
||||
+1
-2
@@ -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 {
|
||||
|
||||
|
||||
-37
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
-126
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
-184
@@ -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
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -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
|
||||
*
|
||||
|
||||
+44
-1
@@ -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);
|
||||
|
||||
}
|
||||
+7
-5
@@ -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) {
|
||||
|
||||
+96
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
+106
-21
@@ -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<GlobalStatus, Consumer<GlobalTransactionEvent>> consumers;
|
||||
private final Map<String, Consumer<GlobalTransactionEvent>> 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();
|
||||
}
|
||||
|
||||
}
|
||||
+16
-7
@@ -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)) {
|
||||
|
||||
+29
-12
@@ -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<BranchSession>, Sessi
|
||||
|
||||
private String applicationData;
|
||||
|
||||
private LockStatus lockStatus = Locked;
|
||||
|
||||
private ConcurrentMap<FileLocker.BucketLockMap, Set<String>> lockHolder = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
@@ -252,8 +257,12 @@ public class BranchSession implements Lockable, Comparable<BranchSession>, 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<BranchSession>, 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<BranchSession>, 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<BranchSession>, Sessi
|
||||
this.branchType = BranchType.values()[branchTypeId];
|
||||
}
|
||||
this.status = BranchStatus.get(byteBuffer.get());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+117
-47
@@ -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<BranchSession> branchSessions = new ArrayList<>();
|
||||
private List<BranchSession> 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<BranchSession> 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<BranchSession> 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<BranchSession> 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<BranchSession> 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<BranchSession> getSortedBranches() {
|
||||
return new ArrayList<>(branchSessions);
|
||||
public List<BranchSession> getSortedBranches() {
|
||||
return new ArrayList<>(getBranchSessions());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets reverse sorted branches.
|
||||
* @return the reverse sorted branches
|
||||
*/
|
||||
public ArrayList<BranchSession> getReverseSortedBranches() {
|
||||
ArrayList<BranchSession> reversed = new ArrayList<>(branchSessions);
|
||||
public List<BranchSession> getReverseSortedBranches() {
|
||||
List<BranchSession> 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<BranchSession> getBranchSessions() {
|
||||
public List<BranchSession> 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 + '}';
|
||||
}
|
||||
|
||||
}
|
||||
+17
-6
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+162
-27
@@ -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<GlobalSession> 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<BranchSession> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+158
-154
@@ -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<GlobalSession> allSessions = ROOT_SESSION_MANAGER.allSessions();
|
||||
if (CollectionUtils.isNotEmpty(allSessions)) {
|
||||
List<GlobalSession> removeGlobalSessions = new ArrayList<>();
|
||||
Iterator<GlobalSession> 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<GlobalSession> 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<GlobalSession> 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<BranchSession> branchSessions) {
|
||||
private static void lockBranchSessions(List<BranchSession> 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();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -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;
|
||||
|
||||
+8
-1
@@ -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;
|
||||
|
||||
}
|
||||
+4
-20
@@ -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> T lockAndExecute(GlobalSession globalSession, GlobalSession.LockCallable<T> 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;
|
||||
}
|
||||
|
||||
}
|
||||
+77
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+84
-2
@@ -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<GlobalSessionVO> result, List<GlobalSession> 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<BranchSessionVO> converToBranchSession(List<BranchSession> branchSessions) {
|
||||
Set<BranchSessionVO> 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<GlobalSessionVO> convertGlobalSession(List<GlobalSession> filteredSessions) {
|
||||
|
||||
if (CollectionUtils.isEmpty(filteredSessions)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final ArrayList<GlobalSessionVO> 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<BranchSessionVO> convertBranchSession(List<BranchSession> branchSessions) {
|
||||
|
||||
if (CollectionUtils.isEmpty(branchSessions)) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
final Set<BranchSessionVO> 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;
|
||||
}
|
||||
|
||||
}
|
||||
+263
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
+4
-13
@@ -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<BranchSession> branchSessions = globalSession.getBranchSessions();
|
||||
if (CollectionUtils.isEmpty(branchSessions)) {
|
||||
return true;
|
||||
}
|
||||
List<Long> 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;
|
||||
}
|
||||
}
|
||||
|
||||
+19
-13
@@ -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<RowLock> locks) {
|
||||
return acquireLock(locks, true, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acquireLock(List<RowLock> 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<Long> 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
|
||||
|
||||
+105
-82
@@ -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<LockDO> lockDOs) {
|
||||
return acquireLock(lockDOs, true, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acquireLock(List<LockDO> 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<LockDO> 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<LockDO> 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<Long> 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<LockDO> lockDOs) {
|
||||
protected boolean doAcquireLocks(Connection conn, List<LockDO> 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());
|
||||
|
||||
+12
-17
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+36
-25
@@ -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<GlobalSession> readSortByTimeoutBeginSessions(boolean withBranchSessions) {
|
||||
return readSession(new GlobalStatus[] { GlobalStatus.Begin }, withBranchSessions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read session list.
|
||||
* @param statuses the statuses
|
||||
* @return the list
|
||||
*/
|
||||
public List<GlobalSession> readSession(GlobalStatus[] statuses) {
|
||||
@Override
|
||||
public List<GlobalSession> 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<GlobalTransactionDO> globalTransactionDOs = logStore.queryGlobalTransactionDO(states, logQueryLimit);
|
||||
if (CollectionUtils.isEmpty(globalTransactionDOs)) {
|
||||
return null;
|
||||
Map<String, List<BranchTransactionDO>> branchTransactionDOsMap = Collections.emptyMap();
|
||||
if (CollectionUtils.isNotEmpty(globalTransactionDOs)) {
|
||||
List<String> xids = globalTransactionDOs.stream().map(GlobalTransactionDO::getXid)
|
||||
.collect(Collectors.toList());
|
||||
if (withBranchSessions) {
|
||||
List<BranchTransactionDO> branchTransactionDOs = logStore.queryBranchTransactionDO(xids);
|
||||
branchTransactionDOsMap = branchTransactionDOs.stream().collect(
|
||||
Collectors.groupingBy(BranchTransactionDO::getXid, LinkedHashMap::new, Collectors.toList()));
|
||||
}
|
||||
}
|
||||
List<String> xids = globalTransactionDOs.stream().map(GlobalTransactionDO::getXid).collect(Collectors.toList());
|
||||
List<BranchTransactionDO> branchTransactionDOs = logStore.queryBranchTransactionDO(xids);
|
||||
Map<String, List<BranchTransactionDO>> 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<String, List<BranchTransactionDO>> 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<BranchTransactionDO> branchTransactionDOs) {
|
||||
GlobalSession globalSession = SessionConverter.convertGlobalSession(globalTransactionDO);
|
||||
return getGlobalSession(globalTransactionDO, branchTransactionDOs, true);
|
||||
}
|
||||
|
||||
private GlobalSession getGlobalSession(GlobalTransactionDO globalTransactionDO,
|
||||
List<BranchTransactionDO> branchTransactionDOs, boolean withBranchSessions) {
|
||||
GlobalSession globalSession = SessionConverter.convertGlobalSession(globalTransactionDO, !withBranchSessions);
|
||||
// branch transactions
|
||||
if (CollectionUtils.isNotEmpty(branchTransactionDOs)) {
|
||||
for (BranchTransactionDO branchTransactionDO : branchTransactionDOs) {
|
||||
|
||||
+43
-36
@@ -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()) {
|
||||
|
||||
+2
-2
@@ -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.
|
||||
*
|
||||
|
||||
+3
-3
@@ -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<BranchSession> branchSessions = globalSession.getBranchSessions();
|
||||
List<BranchSession> branchSessions = globalSession.getBranchSessions();
|
||||
boolean releaseLockResult = true;
|
||||
for (BranchSession branchSession : branchSessions) {
|
||||
try {
|
||||
|
||||
+50
-22
@@ -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<RowLock> rowLocks) {
|
||||
return acquireLock(rowLocks, true, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acquireLock(List<RowLock> rowLocks, boolean autoCommit, boolean skipCheckLock) {
|
||||
if (CollectionUtils.isEmpty(rowLocks)) {
|
||||
// no lock
|
||||
return true;
|
||||
@@ -69,7 +78,8 @@ public class FileLocker extends AbstractLocker {
|
||||
ConcurrentMap<BucketLockMap, Set<String>> bucketHolder = branchSession.getLockHolder();
|
||||
ConcurrentMap<String, ConcurrentMap<Integer, BucketLockMap>> 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<String> 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<String> 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<String/* pk */, Long/* transactionId */> bucketLockMap = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<String/* pk */, BranchSession/* branchSession */> bucketLockMap = new ConcurrentHashMap<>();
|
||||
|
||||
ConcurrentHashMap<String, Long> get() {
|
||||
ConcurrentHashMap<String, BranchSession> get() {
|
||||
return bucketLockMap;
|
||||
}
|
||||
|
||||
|
||||
+62
-23
@@ -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<GlobalSession> findGlobalSessions(SessionCondition condition) {
|
||||
List<GlobalSession> found = new ArrayList<>();
|
||||
|
||||
List<GlobalStatus> 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;
|
||||
}
|
||||
|
||||
+28
-30
@@ -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;
|
||||
|
||||
+20
-25
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+90
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+2
-12
@@ -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<BranchSession> branchSessions = globalSession.getBranchSessions();
|
||||
if (CollectionUtils.isEmpty(branchSessions)) {
|
||||
return true;
|
||||
}
|
||||
List<Long> 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;
|
||||
}
|
||||
}
|
||||
|
||||
+249
-99
@@ -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<RowLock> rowLocks) {
|
||||
return acquireLock(rowLocks, true, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acquireLock(List<RowLock> 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<RowLock> rowLocks, boolean autoCommit,
|
||||
boolean skipCheckLock) {
|
||||
String needLockXid = rowLocks.get(0).getXid();
|
||||
Long branchId = rowLocks.get(0).getBranchId();
|
||||
List<LockDO> needLockDOS = convertToLockDO(rowLocks);
|
||||
if (needLockDOS.size() > 1) {
|
||||
needLockDOS = needLockDOS.stream().filter(LambdaUtils.distinctByKey(LockDO::getRowKey))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
List<String> needLockKeys = new ArrayList<>();
|
||||
needLockDOS.forEach(lockDO -> needLockKeys.add(buildLockKey(lockDO.getRowKey())));
|
||||
Map<String, LockDO> needAddLock = new HashMap<>(needLockKeys.size(), 1);
|
||||
|
||||
try (Jedis jedis = JedisPooledFactory.getJedisInstance()) {
|
||||
List<LockDO> needLockDOS = convertToLockDO(rowLocks);
|
||||
if (needLockDOS.size() > 1) {
|
||||
needLockDOS = needLockDOS.stream().filter(LambdaUtils.distinctByKey(LockDO::getRowKey))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
List<String> 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<String> existedLockInfos = (List<String>) (List) pipeline1.syncAndReturnAll();
|
||||
Map<String, LockDO> needAddLock = new HashMap<>(needLockKeys.size(), 1);
|
||||
needLockKeys.stream().forEachOrdered(needLockKey -> {
|
||||
pipeline1.hget(needLockKey, XID);
|
||||
if (!autoCommit) {
|
||||
pipeline1.hget(needLockKey, STATUS);
|
||||
}
|
||||
});
|
||||
List<List<String>> existedLockInfos = Lists.partition((List<String>) (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<String> 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<String> 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<Integer> results = (List<Integer>) (List) pipeline.syncAndReturnAll();
|
||||
List<List<Integer>> partitions = Lists.partition(results, 7);
|
||||
}
|
||||
|
||||
ArrayList<String> 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<String> 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<Integer> results = (List<Integer>) (List) pipeline.syncAndReturnAll();
|
||||
List<List<Integer>> 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<String> 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<RowLock> rowLocks) {
|
||||
String needLockXid = rowLocks.get(0).getXid();
|
||||
Long branchId = rowLocks.get(0).getBranchId();
|
||||
List<LockDO> needLockDOs = rowLocks.stream().map(this::convertToLockDO)
|
||||
.filter(LambdaUtils.distinctByKey(LockDO::getRowKey)).collect(Collectors.toList());
|
||||
ArrayList<String> keys = new ArrayList<>();
|
||||
ArrayList<String> 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<Long> 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<RowLock> 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<LockDO> locks = convertToLockDO(rowLocks);
|
||||
Set<String> lockKeys = new HashSet<>();
|
||||
for (LockDO rowlock : locks) {
|
||||
lockKeys.add(buildLockKey(rowlock.getRowKey()));
|
||||
}
|
||||
List<String> rowKeys = jedis.hmget(xidLockKey, branchIdsArray);
|
||||
|
||||
String xid = rowLocks.get(0).getXid();
|
||||
try (Pipeline pipeline = jedis.pipelined()) {
|
||||
lockKeys.forEach(key -> pipeline.hget(key, XID));
|
||||
List<String> existedXids = (List<String>) (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<String, String> 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<String> rowKeys = new ArrayList<>();
|
||||
if (null == branchId) {
|
||||
Map<String, String> 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<Long> branchIds = new ArrayList<>();
|
||||
branchIds.add(branchId);
|
||||
return releaseLock(xid, branchIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLockable(List<RowLock> rowLocks) {
|
||||
if (CollectionUtils.isEmpty(rowLocks)) {
|
||||
return true;
|
||||
}
|
||||
try (Jedis jedis = JedisPooledFactory.getJedisInstance()) {
|
||||
List<LockDO> locks = convertToLockDO(rowLocks);
|
||||
Set<String> 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<String> existedXids = (List<String>) (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;
|
||||
}
|
||||
|
||||
+10
-14
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+439
-96
@@ -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<LogOperation, Function<GlobalTransactionDO, Boolean>> globalMap;
|
||||
|
||||
/**
|
||||
* Map for LogOperation Branch Operation
|
||||
*/
|
||||
public static volatile ImmutableMap<LogOperation, Function<BranchTransactionDO, Boolean>> branchMap;
|
||||
|
||||
/**
|
||||
* init globalMap
|
||||
*
|
||||
*/
|
||||
public void initGlobalMap() {
|
||||
if (CollectionUtils.isEmpty(branchMap)) {
|
||||
globalMap = ImmutableMap.<LogOperation, Function<GlobalTransactionDO, Boolean>>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.<LogOperation, Function<BranchTransactionDO, Boolean>>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<String, String> 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<String, String> map = new HashMap<>(2, 1);
|
||||
Map<String, String> 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<String, String> 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<Object> 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<GlobalSession> readSession(GlobalStatus[] statuses) {
|
||||
List<String> statusKeys = new ArrayList<>();
|
||||
for (int i = 0; i < statuses.length; i++) {
|
||||
statusKeys.add(buildGlobalStatus(statuses[i].getCode()));
|
||||
@Override
|
||||
public List<GlobalSession> readSession(GlobalStatus[] statuses, boolean withBranchSessions) {
|
||||
List<GlobalSession> globalSessions = Collections.synchronizedList(new ArrayList<>());
|
||||
List<String> statusKeys = convertStatusKeys(statuses);
|
||||
Map<String, Integer> 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<String>> list = (List<List<String>>) (List) pipelined.syncAndReturnAll();
|
||||
List<String> xids = new ArrayList<>();
|
||||
if (CollectionUtils.isNotEmpty(list)) {
|
||||
xids = list.stream().flatMap(ll -> ll.stream()).collect(Collectors.toList());
|
||||
}
|
||||
List<GlobalSession> 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<String>> list = new ArrayList<>();
|
||||
dogetXidsForTargetMapRecursive(targetMap, 0L, perStatusLimit - 1, queryCount, list);
|
||||
if (CollectionUtils.isNotEmpty(list)) {
|
||||
List<String> 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<GlobalSession> readSortByTimeoutBeginSessions(boolean withBranchSessions) {
|
||||
List<GlobalSession> list = Collections.emptyList();
|
||||
List<String> statusKeys = convertStatusKeys(GlobalStatus.Begin);
|
||||
Map<String, Integer> 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<String> values = jedis.zrangeByScore(REDIS_SEATA_BEGIN_TRANSACTIONS_KEY, 0, System.currentTimeMillis(),
|
||||
0, (int) queryCount);
|
||||
List<Map<String, String>> rep;
|
||||
try (Pipeline pipeline = jedis.pipelined()) {
|
||||
for (String value : values) {
|
||||
pipeline.hgetAll(value);
|
||||
}
|
||||
rep = (List<Map<String, String>>) (List) pipeline.syncAndReturnAll();
|
||||
}
|
||||
list = rep.stream().map(map -> {
|
||||
GlobalTransactionDO globalTransactionDO = (GlobalTransactionDO) BeanUtils.mapToObject(map,
|
||||
GlobalTransactionDO.class);
|
||||
if (globalTransactionDO != null) {
|
||||
String xid = globalTransactionDO.getXid();
|
||||
List<BranchTransactionDO> 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<String, Integer> 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<GlobalSession> readSession(SessionCondition sessionCondition) {
|
||||
List<GlobalSession> 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<GlobalSession>
|
||||
*/
|
||||
public List<GlobalSession> readSessionStatusByPage(GlobalSessionParam param) {
|
||||
List<GlobalSession> 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<String> 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<BranchTransactionDO> branchTransactionDOs) {
|
||||
GlobalSession globalSession = SessionConverter.convertGlobalSession(globalTransactionDO);
|
||||
List<BranchTransactionDO> 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<BranchTransactionDO> readBranchSessionByXid(Jedis jedis, String xid) {
|
||||
List<BranchTransactionDO> branchTransactionDOs = new ArrayList<>();
|
||||
String branchListKey = buildBranchListKeyByXid(xid);
|
||||
List<String> branchKeys = jedis.lrange(branchListKey, 0, -1);
|
||||
Pipeline pipeline = jedis.pipelined();
|
||||
List<String> branchKeys = lRange(jedis, branchListKey);
|
||||
if (CollectionUtils.isNotEmpty(branchKeys)) {
|
||||
branchKeys.stream().forEachOrdered(branchKey -> pipeline.hgetAll(branchKey));
|
||||
List<Object> branchInfos = pipeline.syncAndReturnAll();
|
||||
for (Object branchInfo : branchInfos) {
|
||||
if (branchInfo != null) {
|
||||
Map<String, String> branchInfoMap = (Map<String, String>) branchInfo;
|
||||
Optional<BranchTransactionDO> 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<Object> branchInfos = pipeline.syncAndReturnAll();
|
||||
for (Object branchInfo : branchInfos) {
|
||||
if (branchInfo != null) {
|
||||
Map<String, String> branchInfoMap = (Map<String, String>) branchInfo;
|
||||
Optional<BranchTransactionDO> 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<String> lRange(Jedis jedis, String key) {
|
||||
List<String> keys = new ArrayList<>();
|
||||
List<String> 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<BranchTransactionDO> findBranchSessionByXid(String xid) {
|
||||
try (Jedis jedis = JedisPooledFactory.getJedisInstance()) {
|
||||
return readBranchSessionByXid(jedis, xid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* query globalSession by page
|
||||
* @param pageNum
|
||||
* @param pageSize
|
||||
* @param withBranchSessions
|
||||
* @return List<GlobalSession>
|
||||
*/
|
||||
public List<GlobalSession> findGlobalSessionByPage(int pageNum, int pageSize, boolean withBranchSessions) {
|
||||
List<GlobalSession> globalSessions = new ArrayList<>();
|
||||
int start = Math.max((pageNum - 1) * pageSize, 0);
|
||||
int end = pageNum * pageSize - 1;
|
||||
|
||||
List<String> statusKeys = convertStatusKeys(GlobalStatus.values());
|
||||
Map<String, Integer> stringLongMap = calculateStatuskeysHasData(statusKeys);
|
||||
|
||||
List<List<String>> list = dogetXidsForTargetMap(stringLongMap, start, end, pageSize);
|
||||
|
||||
if (CollectionUtils.isNotEmpty(list)) {
|
||||
List<String> 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<String, Integer> calculateStatuskeysHasData(List<String> statusKeys) {
|
||||
Map<String, Integer> resultMap = new LinkedHashMap<>();
|
||||
Map<String, Integer> keysMap = new HashMap<>(statusKeys.size());
|
||||
try (Jedis jedis = JedisPooledFactory.getJedisInstance(); Pipeline pipelined = jedis.pipelined()) {
|
||||
statusKeys.forEach(key -> pipelined.llen(key));
|
||||
List<Long> 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<Map.Entry<String, Integer>> 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<String> 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<Long> list = (List<Long>) (List) pipelined.syncAndReturnAll();
|
||||
if (list.size() > 0) {
|
||||
total = list.stream().mapToLong(value -> value).sum();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> convertStatusKeys(GlobalStatus... statuses) {
|
||||
List<String> statusKeys = new ArrayList<>();
|
||||
for (int i = 0; i < statuses.length; i++) {
|
||||
statusKeys.add(buildGlobalStatus(statuses[i].getCode()));
|
||||
}
|
||||
return statusKeys;
|
||||
}
|
||||
|
||||
private void dogetXidsForTargetMapRecursive(Map<String, Integer> targetMap, long start, long end, long queryCount,
|
||||
List<List<String>> 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<Map.Entry<String, Integer>> 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<String> 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<List<String>> dogetXidsForTargetMap(Map<String, Integer> targetMap, int start, int end,
|
||||
int totalCount) {
|
||||
List<List<String>> listList = new ArrayList<>();
|
||||
try (Jedis jedis = JedisPooledFactory.getJedisInstance()) {
|
||||
for (String key : targetMap.keySet()) {
|
||||
final List<String> 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;
|
||||
}
|
||||
|
||||
}
|
||||
+13
-1
@@ -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<GlobalSession> readSortByTimeoutBeginSessions(boolean withBranchSessions) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GlobalSession> readSession(GlobalStatus[] statuses, boolean withBranchSessions) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GlobalSession> readSession(SessionCondition sessionCondition) {
|
||||
return null;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+16
@@ -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<GlobalSession> readSortByTimeoutBeginSessions(boolean withBranchSessions);
|
||||
|
||||
/**
|
||||
* Read session global session.
|
||||
* @param statuses the statuses
|
||||
* @param withBranchSessions the withBranchSessions
|
||||
* @return the global session list
|
||||
*/
|
||||
List<GlobalSession> readSession(GlobalStatus[] statuses, boolean withBranchSessions);
|
||||
|
||||
/**
|
||||
* Read session by status list.
|
||||
* @param sessionCondition the session condition
|
||||
|
||||
+44
-4
@@ -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<String, Object> 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+19
-33
@@ -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<BranchSession> branchSessions = globalSession.getSortedBranches();
|
||||
for (BranchSession branchSession : branchSessions) {
|
||||
globalSession.removeBranch(branchSession);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get saga ResourceId
|
||||
* @param globalSession the globalSession
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
io.seata.server.storage.redis.lock.RedisDistributedLocker
|
||||
io.seata.server.storage.db.lock.DataBaseDistributedLocker
|
||||
+22
@@ -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": [
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.context.ApplicationListener=\
|
||||
io.seata.server.ServerApplicationListener
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,7 @@
|
||||
███████╗███████╗ █████╗ ████████╗ █████╗
|
||||
██╔════╝██╔════╝██╔══██╗╚══██╔══╝██╔══██╗
|
||||
███████╗█████╗ ███████║ ██║ ███████║
|
||||
╚════██║██╔══╝ ██╔══██║ ██║ ██╔══██║
|
||||
███████║███████╗██║ ██║ ██║ ██║ ██║
|
||||
╚══════╝╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
+8
-5
@@ -19,13 +19,16 @@
|
||||
<!-- Context listeners -->
|
||||
<contextListener class="io.seata.server.logging.listener.SystemPropertyLoggerContextListener"/>
|
||||
|
||||
<!-- Copied from spring-boot.jar -->
|
||||
<conversionRule conversionWord="clr" converterClass="io.seata.server.logging.logback.ColorConverter"/>
|
||||
<conversionRule conversionWord="wex" converterClass="io.seata.server.logging.logback.WhitespaceThrowableProxyConverter"/>
|
||||
<conversionRule conversionWord="wEx" converterClass="io.seata.server.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
|
||||
<!-- The conversion rules are copied from `defaults.xml` in the `spring-boot-xxx.jar` -->
|
||||
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
|
||||
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
|
||||
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
|
||||
<!-- The custom conversion rules -->
|
||||
<conversionRule conversionWord="wEx2" converterClass="io.seata.server.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
|
||||
|
||||
<!-- common properties -->
|
||||
<property name="APPLICATION_NAME" value="seata-server"/>
|
||||
<springProperty name="PORT" source="server.port" defaultValue="7091"/>
|
||||
<springProperty name="APPLICATION_NAME" source="spring.application.name" defaultValue="seata-server"/>
|
||||
|
||||
|
||||
<!-- console-appender -->
|
||||
@@ -1,7 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<included>
|
||||
<!-- console-appender properties -->
|
||||
<property name="CONSOLE_LOG_PATTERN" value="%clr(%d{HH:mm:ss.SSS}){faint} %clr(%5p) %clr(---){faint} %clr([%25.25t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n%wEx"/>
|
||||
<springProperty name="CONSOLE_LOG_PATTERN" source="logging.pattern.console"
|
||||
defaultValue="%clr(%d{HH:mm:ss.SSS}){faint} %clr(%5p) %clr(---){faint} %clr([%25.25t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n%wEx2"/>
|
||||
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
|
||||
@@ -1,15 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<included>
|
||||
<!-- file-appender properties -->
|
||||
<property name="LOG_HOME" value="${user.home}/logs/seata"/>
|
||||
<property name="FILE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %5p --- [%30.30t] %-40.40logger{39} : %m%n%wEx"/>
|
||||
<springProperty name="LOG_FILE_PATH" source="logging.file.path"
|
||||
defaultValue="${user.home}/logs/seata"/>
|
||||
<springProperty name="FILE_LOG_PATTERN" source="logging.pattern.file"
|
||||
defaultValue="%d{yyyy-MM-dd HH:mm:ss.SSS} %5p --- [%30.30t] %-40.40logger{39} : %m%n%wEx2"/>
|
||||
|
||||
<!--ALL-->
|
||||
<appender name="FILE_ALL" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${LOG_HOME}/${APPLICATION_NAME:-}.${PORT}.all.log</file>
|
||||
<file>${LOG_FILE_PATH}/${APPLICATION_NAME:-seata-server}.${RPC_PORT}.all.log</file>
|
||||
<append>true</append>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<fileNamePattern>${LOG_HOME}/history/${APPLICATION_NAME:-}.${PORT}.all.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
|
||||
<fileNamePattern>${LOG_FILE_PATH}/history/${APPLICATION_NAME:-seata-server}.${RPC_PORT}.all.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
|
||||
<maxFileSize>2GB</maxFileSize>
|
||||
<MaxHistory>7</MaxHistory>
|
||||
<totalSizeCap>7GB</totalSizeCap>
|
||||
@@ -28,10 +46,10 @@
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
<file>${LOG_HOME}/${APPLICATION_NAME:-}.${PORT}.warn.log</file>
|
||||
<file>${LOG_FILE_PATH}/${APPLICATION_NAME:-seata-server}.${RPC_PORT}.warn.log</file>
|
||||
<append>true</append>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<fileNamePattern>${LOG_HOME}/history/${APPLICATION_NAME:-}.${PORT}.warn.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
|
||||
<fileNamePattern>${LOG_FILE_PATH}/history/${APPLICATION_NAME:-seata-server}.${RPC_PORT}.warn.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
|
||||
<maxFileSize>2GB</maxFileSize>
|
||||
<MaxHistory>7</MaxHistory>
|
||||
<totalSizeCap>7GB</totalSizeCap>
|
||||
@@ -50,10 +68,10 @@
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
<file>${LOG_HOME}/${APPLICATION_NAME:-}.${PORT}.error.log</file>
|
||||
<file>${LOG_FILE_PATH}/${APPLICATION_NAME:-seata-server}.${RPC_PORT}.error.log</file>
|
||||
<append>true</append>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<fileNamePattern>${LOG_HOME}/history/${APPLICATION_NAME:-}.${PORT}.error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
|
||||
<fileNamePattern>${LOG_FILE_PATH}/history/${APPLICATION_NAME:-seata-server}.${RPC_PORT}.error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
|
||||
<maxFileSize>2GB</maxFileSize>
|
||||
<MaxHistory>7</MaxHistory>
|
||||
<totalSizeCap>7GB</totalSizeCap>
|
||||
|
||||
@@ -1,17 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<included>
|
||||
<!-- kafka-appender properties -->
|
||||
<property name="KAFKA_BOOTSTRAP_SERVERS" value="localhost:9092"/>
|
||||
<property name="KAFKA_TOPIC" value="logback_to_logstash"/>
|
||||
<springProperty name="KAFKA_BOOTSTRAP_SERVERS" source="logging.extend.kafka-appender.bootstrap-servers"
|
||||
defaultValue="127.0.0.1:9092"/>
|
||||
<springProperty name="KAFKA_TOPIC" source="logging.extend.kafka-appender.topic"
|
||||
defaultValue="logback_to_logstash"/>
|
||||
|
||||
<appender name="KAFKA" class="com.github.danielwegener.logback.kafka.KafkaAppender">
|
||||
<encoder>
|
||||
<!--<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS}|%p|seata-server|${PORT:-0}|%t|%logger|%X{X-TX-XID:-}|%X{X-TX-BRANCH-ID:-}|%m|%wex</pattern>-->
|
||||
<!--<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS}|%p|${APPLICATION_NAME:-seata-server}|${RPC_PORT:-0}|%t|%logger|%X{X-TX-XID:-}|%X{X-TX-BRANCH-ID:-}|%m|%wex</pattern>-->
|
||||
<pattern>{
|
||||
"@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:-}",
|
||||
|
||||
@@ -1,7 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<included>
|
||||
<!-- logstash-appender properties -->
|
||||
<property name="LOGSTASH_DESTINATION" value="localhost:4560"/>
|
||||
<springProperty name="LOGSTASH_DESTINATION" source="logging.extend.logstash-appender.destination"
|
||||
defaultValue="127.0.0.1:4560"/>
|
||||
|
||||
<appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
|
||||
<!-- the TCP address of the logstash -->
|
||||
@@ -12,13 +29,13 @@
|
||||
<!-- the global custom fields -->
|
||||
<customFields>
|
||||
{
|
||||
"app_name": "${APPLICATION_NAME:-}"
|
||||
"app_name": "${APPLICATION_NAME:-seata-server}"
|
||||
}
|
||||
</customFields>
|
||||
|
||||
<!-- exclude the provider of data `@version` -->
|
||||
<!-- Exclude the provider of data `@version` -->
|
||||
<excludeProvider>net.logstash.logback.composite.LogstashVersionJsonProvider</excludeProvider>
|
||||
<!-- exclude providers not currently needed, reduce some performance loss. -->
|
||||
<!-- Exclude providers that are not currently needed, reduce some performance loss. -->
|
||||
<excludeProvider>net.logstash.logback.composite.loggingevent.JsonMessageJsonProvider</excludeProvider>
|
||||
<excludeProvider>net.logstash.logback.composite.loggingevent.TagsJsonProvider</excludeProvider>
|
||||
<excludeProvider>net.logstash.logback.composite.loggingevent.LogstashMarkersJsonProvider</excludeProvider>
|
||||
|
||||
@@ -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]
|
||||
Loaded 100 of 101 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user