update 优化 使用 seata-server 官方依赖简化seata集成方式

This commit is contained in:
疯狂的狮子Li
2024-08-08 13:27:55 +08:00
parent 2d55ddcfc0
commit e63861c028
110 changed files with 3 additions and 16045 deletions

View File

@@ -28,10 +28,7 @@
<properties>
<seata.version>1.7.1</seata.version>
<jcommander.version>1.82</jcommander.version>
<druid.version>1.2.12</druid.version>
<spring-boot.version>2.7.18</spring-boot.version>
<native-build-tools-plugin.version>0.9.20</native-build-tools-plugin.version>
<logstash-logback-encoder.version>7.2</logstash-logback-encoder.version>
<jedis.version>3.8.0</jedis.version>
</properties>
@@ -46,20 +43,6 @@
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-bom</artifactId>
<version>${seata.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-dependencies</artifactId>
<version>${seata.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
@@ -77,93 +60,15 @@
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-autoconfigure-server</artifactId>
<artifactId>seata-server</artifactId>
<version>${seata.version}</version>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-core</artifactId>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-config-all</artifactId>
<exclusions>
<exclusion>
<artifactId>log4j</artifactId>
<groupId>log4j</groupId>
<artifactId>slf4j-reload4j</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-discovery-all</artifactId>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-serializer-all</artifactId>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-compressor-all</artifactId>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-metrics-all</artifactId>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-console</artifactId>
<version>${seata.version}</version>
</dependency>
<!-- for database -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</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>com.mysql</groupId>
<artifactId>mysql-connector-j</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>-->
<dependency>
<groupId>com.beust</groupId>
<artifactId>jcommander</artifactId>
<version>${jcommander.version}</version>
</dependency>
<!-- only for event bus -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<!-- jedis -->
<dependency>
@@ -172,30 +77,12 @@
<version>${jedis.version}</version>
</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>
<version>${logstash-logback-encoder.version}</version>
</dependency>
<dependency>
<groupId>org.codehaus.janino</groupId>
<artifactId>janino</artifactId>
</dependency>
</dependencies>
<build>

View File

@@ -1,327 +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;
import io.seata.common.exception.StoreException;
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.*;
import io.seata.core.rpc.RpcContext;
import io.seata.server.session.GlobalSession;
import io.seata.server.session.SessionHolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The type Abstract tc inbound handler.
*
* @author sharajava
*/
public abstract class AbstractTCInboundHandler extends AbstractExceptionHandler implements TCInboundHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractTCInboundHandler.class);
@Override
public GlobalBeginResponse handle(GlobalBeginRequest request, final RpcContext rpcContext) {
GlobalBeginResponse response = new GlobalBeginResponse();
exceptionHandleTemplate(new AbstractCallback<GlobalBeginRequest, GlobalBeginResponse>() {
@Override
public void execute(GlobalBeginRequest request, GlobalBeginResponse response) throws TransactionException {
try {
doGlobalBegin(request, response, rpcContext);
} catch (StoreException e) {
throw new TransactionException(TransactionExceptionCode.FailedStore,
String.format("begin global request failed. xid=%s, msg=%s", response.getXid(), e.getMessage()),
e);
}
}
}, request, response);
return response;
}
/**
* Do global begin.
*
* @param request the request
* @param response the response
* @param rpcContext the rpc context
* @throws TransactionException the transaction exception
*/
protected abstract void doGlobalBegin(GlobalBeginRequest request, GlobalBeginResponse response,
RpcContext rpcContext) throws TransactionException;
@Override
public GlobalCommitResponse handle(GlobalCommitRequest request, final RpcContext rpcContext) {
GlobalCommitResponse response = new GlobalCommitResponse();
response.setGlobalStatus(GlobalStatus.Committing);
exceptionHandleTemplate(new AbstractCallback<GlobalCommitRequest, GlobalCommitResponse>() {
@Override
public void execute(GlobalCommitRequest request, GlobalCommitResponse response)
throws TransactionException {
try {
doGlobalCommit(request, response, rpcContext);
} catch (StoreException e) {
throw new TransactionException(TransactionExceptionCode.FailedStore,
String.format("global commit request failed. xid=%s, msg=%s", request.getXid(), e.getMessage()),
e);
}
}
@Override
public void onTransactionException(GlobalCommitRequest request, GlobalCommitResponse response,
TransactionException tex) {
super.onTransactionException(request, response, tex);
checkTransactionStatus(request, response);
}
@Override
public void onException(GlobalCommitRequest request, GlobalCommitResponse response, Exception rex) {
super.onException(request, response, rex);
checkTransactionStatus(request, response);
}
}, request, response);
return response;
}
/**
* Do global commit.
*
* @param request the request
* @param response the response
* @param rpcContext the rpc context
* @throws TransactionException the transaction exception
*/
protected abstract void doGlobalCommit(GlobalCommitRequest request, GlobalCommitResponse response,
RpcContext rpcContext) throws TransactionException;
@Override
public GlobalRollbackResponse handle(GlobalRollbackRequest request, final RpcContext rpcContext) {
GlobalRollbackResponse response = new GlobalRollbackResponse();
response.setGlobalStatus(GlobalStatus.Rollbacking);
exceptionHandleTemplate(new AbstractCallback<GlobalRollbackRequest, GlobalRollbackResponse>() {
@Override
public void execute(GlobalRollbackRequest request, GlobalRollbackResponse response)
throws TransactionException {
try {
doGlobalRollback(request, response, rpcContext);
} catch (StoreException e) {
throw new TransactionException(TransactionExceptionCode.FailedStore, String
.format("global rollback request failed. xid=%s, msg=%s", request.getXid(), e.getMessage()), e);
}
}
@Override
public void onTransactionException(GlobalRollbackRequest request, GlobalRollbackResponse response,
TransactionException tex) {
super.onTransactionException(request, response, tex);
// may be appears StoreException outer layer method catch
checkTransactionStatus(request, response);
}
@Override
public void onException(GlobalRollbackRequest request, GlobalRollbackResponse response, Exception rex) {
super.onException(request, response, rex);
// may be appears StoreException outer layer method catch
checkTransactionStatus(request, response);
}
}, request, response);
return response;
}
/**
* Do global rollback.
*
* @param request the request
* @param response the response
* @param rpcContext the rpc context
* @throws TransactionException the transaction exception
*/
protected abstract void doGlobalRollback(GlobalRollbackRequest request, GlobalRollbackResponse response,
RpcContext rpcContext) throws TransactionException;
@Override
public BranchRegisterResponse handle(BranchRegisterRequest request, final RpcContext rpcContext) {
BranchRegisterResponse response = new BranchRegisterResponse();
exceptionHandleTemplate(new AbstractCallback<BranchRegisterRequest, BranchRegisterResponse>() {
@Override
public void execute(BranchRegisterRequest request, BranchRegisterResponse response)
throws TransactionException {
try {
doBranchRegister(request, response, rpcContext);
} catch (StoreException e) {
throw new TransactionException(TransactionExceptionCode.FailedStore, String
.format("branch register request failed. xid=%s, msg=%s", request.getXid(), e.getMessage()), e);
}
}
}, request, response);
return response;
}
/**
* Do branch register.
*
* @param request the request
* @param response the response
* @param rpcContext the rpc context
* @throws TransactionException the transaction exception
*/
protected abstract void doBranchRegister(BranchRegisterRequest request, BranchRegisterResponse response,
RpcContext rpcContext) throws TransactionException;
@Override
public BranchReportResponse handle(BranchReportRequest request, final RpcContext rpcContext) {
BranchReportResponse response = new BranchReportResponse();
exceptionHandleTemplate(new AbstractCallback<BranchReportRequest, BranchReportResponse>() {
@Override
public void execute(BranchReportRequest request, BranchReportResponse response)
throws TransactionException {
try {
doBranchReport(request, response, rpcContext);
} catch (StoreException e) {
throw new TransactionException(TransactionExceptionCode.FailedStore, String
.format("branch report request failed. xid=%s, branchId=%s, msg=%s", request.getXid(),
request.getBranchId(), e.getMessage()), e);
}
}
}, request, response);
return response;
}
/**
* Do branch report.
*
* @param request the request
* @param rpcContext the rpc context
* @throws TransactionException the transaction exception
*/
protected abstract void doBranchReport(BranchReportRequest request, BranchReportResponse response,
RpcContext rpcContext) throws TransactionException;
@Override
public GlobalLockQueryResponse handle(GlobalLockQueryRequest request, final RpcContext rpcContext) {
GlobalLockQueryResponse response = new GlobalLockQueryResponse();
exceptionHandleTemplate(new AbstractCallback<GlobalLockQueryRequest, GlobalLockQueryResponse>() {
@Override
public void execute(GlobalLockQueryRequest request, GlobalLockQueryResponse response)
throws TransactionException {
try {
doLockCheck(request, response, rpcContext);
} catch (StoreException e) {
throw new TransactionException(TransactionExceptionCode.FailedStore, String
.format("global lock query request failed. xid=%s, msg=%s", request.getXid(), e.getMessage()),
e);
}
}
}, request, response);
return response;
}
/**
* Do lock check.
*
* @param request the request
* @param response the response
* @param rpcContext the rpc context
* @throws TransactionException the transaction exception
*/
protected abstract void doLockCheck(GlobalLockQueryRequest request, GlobalLockQueryResponse response,
RpcContext rpcContext) throws TransactionException;
@Override
public GlobalStatusResponse handle(GlobalStatusRequest request, final RpcContext rpcContext) {
GlobalStatusResponse response = new GlobalStatusResponse();
response.setGlobalStatus(GlobalStatus.UnKnown);
exceptionHandleTemplate(new AbstractCallback<GlobalStatusRequest, GlobalStatusResponse>() {
@Override
public void execute(GlobalStatusRequest request, GlobalStatusResponse response)
throws TransactionException {
try {
doGlobalStatus(request, response, rpcContext);
} catch (StoreException e) {
throw new TransactionException(TransactionExceptionCode.FailedStore,
String.format("global status request failed. xid=%s, msg=%s", request.getXid(), e.getMessage()),
e);
}
}
@Override
public void onTransactionException(GlobalStatusRequest request, GlobalStatusResponse response,
TransactionException tex) {
super.onTransactionException(request, response, tex);
checkTransactionStatus(request, response);
}
@Override
public void onException(GlobalStatusRequest request, GlobalStatusResponse response, Exception rex) {
super.onException(request, response, rex);
checkTransactionStatus(request, response);
}
}, request, response);
return response;
}
/**
* Do global status.
*
* @param request the request
* @param response the response
* @param rpcContext the rpc context
* @throws TransactionException the transaction exception
*/
protected abstract void doGlobalStatus(GlobalStatusRequest request, GlobalStatusResponse response,
RpcContext rpcContext) throws TransactionException;
@Override
public GlobalReportResponse handle(GlobalReportRequest request, final RpcContext rpcContext) {
GlobalReportResponse response = new GlobalReportResponse();
response.setGlobalStatus(request.getGlobalStatus());
exceptionHandleTemplate(new AbstractCallback<GlobalReportRequest, GlobalReportResponse>() {
@Override
public void execute(GlobalReportRequest request, GlobalReportResponse response)
throws TransactionException {
doGlobalReport(request, response, rpcContext);
}
}, request, response);
return response;
}
/**
* Do global report.
*
* @param request the request
* @param response the response
* @param rpcContext the rpc context
* @throws TransactionException the transaction exception
*/
protected abstract void doGlobalReport(GlobalReportRequest request, GlobalReportResponse response,
RpcContext rpcContext) throws TransactionException;
private void checkTransactionStatus(AbstractGlobalEndRequest request, AbstractGlobalEndResponse response) {
try {
GlobalSession globalSession = SessionHolder.findGlobalSession(request.getXid(), false);
if (globalSession != null) {
response.setGlobalStatus(globalSession.getStatus());
} else {
response.setGlobalStatus(GlobalStatus.Finished);
}
} catch (Exception exx) {
LOGGER.error("check transaction status error,{}]", exx.getMessage());
}
}
}

View File

@@ -1,200 +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;
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.server.env.ContainerHelper;
import io.seata.server.store.StoreConfig;
import static io.seata.config.ConfigurationFactory.ENV_PROPERTY_KEY;
/**
* The type Parameter parser.
*
* @author xingfudeshi @gmail.com
*/
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;
@Parameter(names = {"--host", "-h"}, description = "The ip to register to registry center.", order = 1)
private String host;
@Parameter(names = {"--port", "-p"}, description = "The port to listen.", order = 2)
private int port;
@Parameter(names = {"--storeMode", "-m"}, description = "log store mode : file, db, redis", order = 3)
private String storeMode;
@Parameter(names = {"--serverNode", "-n"}, description = "server node id, such as 1, 2, 3.it will be generated according to the snowflake by default", order = 4)
private Long serverNode;
@Parameter(names = {"--seataEnv", "-e"}, description = "The name used for multi-configuration isolation.",
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) {
this.init(args);
}
/**
* startup args > docker env
* @param args
*/
private void init(String[] args) {
try {
getCommandParameters(args);
getEnvParameters();
if (StringUtils.isNotBlank(seataEnv)) {
System.setProperty(ENV_PROPERTY_KEY, seataEnv);
}
StoreConfig.setStartupParameter(storeMode, sessionStoreMode, lockStoreMode);
} catch (ParameterException e) {
printError(e);
}
}
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);
e.usage();
System.exit(0);
}
/**
* Gets host.
*
* @return the host
*/
public String getHost() {
return host;
}
/**
* Gets port.
*
* @return the port
*/
public int getPort() {
return port;
}
/**
* Gets store mode.
*
* @return the store mode
*/
public String getStoreMode() {
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
*/
public boolean isHelp() {
return help;
}
/**
* Gets server node.
*
* @return the server node
*/
public Long getServerNode() {
return serverNode;
}
/**
* Gets seata env
*
* @return the name used for multi-configuration isolation.
*/
public String getSeataEnv() {
return seataEnv;
}
/**
* Clean up.
*/
public void cleanUp() {
if (null != System.getProperty(ENV_PROPERTY_KEY)) {
System.clearProperty(ENV_PROPERTY_KEY);
}
}
}

View File

@@ -1,89 +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;
import io.seata.common.XID;
import io.seata.common.thread.NamedThreadFactory;
import io.seata.common.util.NetUtil;
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.lock.LockerManagerFactory;
import io.seata.server.metrics.MetricsManager;
import io.seata.server.session.SessionHolder;
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 Server {
/**
* The entry point of application.
*
* @param args the input arguments
*/
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.
ParameterParser parameterParser = new ParameterParser(args);
//initialize the metrics
MetricsManager.get().init();
ThreadPoolExecutor workingThreads = new ThreadPoolExecutor(NettyServerConfig.getMinServerPoolSize(),
NettyServerConfig.getMaxServerPoolSize(), NettyServerConfig.getKeepAliveTime(), TimeUnit.SECONDS,
new LinkedBlockingQueue<>(NettyServerConfig.getMaxTaskQueueSize()),
new NamedThreadFactory("ServerHandlerThread", NettyServerConfig.getMaxServerPoolSize()), new ThreadPoolExecutor.CallerRunsPolicy());
//127.0.0.1 and 0.0.0.0 are not valid here.
if (NetUtil.isValidIp(parameterParser.getHost(), false)) {
XID.setIpAddress(parameterParser.getHost());
} else {
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);
// let ServerRunner do destroy instead ShutdownHook, see https://github.com/seata/seata/issues/4028
ServerRunner.addDisposable(coordinator);
nettyRemotingServer.init();
}
}

View File

@@ -1,105 +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;
import io.seata.core.rpc.Disposable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.web.context.WebServerInitializedEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* @author spilledyear@outlook.com
*/
@Component
public class ServerRunner implements CommandLineRunner, DisposableBean,
ApplicationListener<ApplicationEvent>, Ordered {
private static final Logger LOGGER = LoggerFactory.getLogger(ServerRunner.class);
private boolean started = Boolean.FALSE;
private int port;
@Value("${logging.file.path}")
private String logPath;
private static final List<Disposable> 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("\r\n you can visit seata console UI on http://127.0.0.1:{}. \r\n log path: {}.", this.port, this.logPath);
LOGGER.info("seata server started in {} millSeconds", cost);
} catch (Throwable e) {
started = Boolean.FALSE;
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");
}
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof WebServerInitializedEvent) {
this.port = ((WebServerInitializedEvent)event).getWebServer().getPort();
}
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}

View File

@@ -1,51 +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;
import io.seata.common.util.IdWorker;
/**
* The type Uuid generator.
*
* @author sharajava
*/
public class UUIDGenerator {
private static volatile IdWorker idWorker;
/**
* generate UUID using snowflake algorithm
* @return UUID
*/
public static long generateUUID() {
if (idWorker == null) {
synchronized (UUIDGenerator.class) {
if (idWorker == null) {
init(null);
}
}
}
return idWorker.nextId();
}
/**
* init IdWorker
* @param serverNode the server node id, consider as machine id in snowflake
*/
public static void init(Long serverNode) {
idWorker = new IdWorker(serverNode);
}
}

View File

@@ -1,53 +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.auth;
import io.seata.config.ConfigurationFactory;
import io.seata.core.constants.ConfigurationKeys;
import io.seata.core.protocol.RegisterRMRequest;
import io.seata.core.protocol.RegisterTMRequest;
import io.seata.core.rpc.RegisterCheckAuthHandler;
import static io.seata.common.DefaultValues.DEFAULT_SERVER_ENABLE_CHECK_AUTH;
/**
* @author slievrly
*/
public abstract class AbstractCheckAuthHandler implements RegisterCheckAuthHandler {
private static final Boolean ENABLE_CHECK_AUTH = ConfigurationFactory.getInstance().getBoolean(
ConfigurationKeys.SERVER_ENABLE_CHECK_AUTH, DEFAULT_SERVER_ENABLE_CHECK_AUTH);
@Override
public boolean regTransactionManagerCheckAuth(RegisterTMRequest request) {
if (!ENABLE_CHECK_AUTH) {
return true;
}
return doRegTransactionManagerCheck(request);
}
public abstract boolean doRegTransactionManagerCheck(RegisterTMRequest request);
@Override
public boolean regResourceManagerCheckAuth(RegisterRMRequest request) {
if (!ENABLE_CHECK_AUTH) {
return true;
}
return doRegResourceManagerCheck(request);
}
public abstract boolean doRegResourceManagerCheck(RegisterRMRequest request);
}

View File

@@ -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.auth;
import io.seata.common.loader.LoadLevel;
import io.seata.core.protocol.RegisterRMRequest;
import io.seata.core.protocol.RegisterTMRequest;
/**
* @author slievrly
*/
@LoadLevel(name = "defaultCheckAuthHandler", order = 100)
public class DefaultCheckAuthHandler extends AbstractCheckAuthHandler {
@Override
public boolean doRegTransactionManagerCheck(RegisterTMRequest request) {
return true;
}
@Override
public boolean doRegResourceManagerCheck(RegisterRMRequest request) {
return true;
}
}

View File

@@ -1,36 +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.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;
}

View File

@@ -1,52 +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.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);
}
}

View File

@@ -1,52 +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.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);
}
}

View File

@@ -1,101 +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.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);
}
}

View File

@@ -1,145 +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.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");
}
}

View File

@@ -1,160 +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.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");
}
}

View File

@@ -1,39 +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.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();
}
}

View File

@@ -1,173 +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.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) {
if (CollectionUtils.isEmpty(branchSession.getLockHolder())) {
return Stream.empty();
}
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());
};
}
}

View File

@@ -1,102 +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.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());
};
}
}

View File

@@ -1,65 +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.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());
}
}

View File

@@ -1,119 +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.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;
}
}

View File

@@ -1,110 +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.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());
}
}

View File

@@ -1,114 +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.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 + '\'' +
'}';
}
}

View File

@@ -1,102 +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.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 +
'}';
}
}

View File

@@ -1,34 +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.console.service;
import io.seata.console.result.PageResult;
import io.seata.server.console.vo.BranchSessionVO;
/**
* Branch session service
* @author wangzhongxiang
*/
public interface BranchSessionService {
/**
* Query branch session by xid
* @param xid the xid
* @return the BranchSessionVO list
*/
PageResult<BranchSessionVO> queryByXid(String xid);
}

View File

@@ -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.console.service;
import io.seata.console.result.PageResult;
import io.seata.server.console.param.GlobalLockParam;
import io.seata.server.console.vo.GlobalLockVO;
/**
* Global lock service
* @author wangzhongxiang
*/
public interface GlobalLockService {
/**
* Query locks by param
* @param param the param
* @return the list of GlobalLockVO
*/
PageResult<GlobalLockVO> query(GlobalLockParam param);
}

View File

@@ -1,35 +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.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);
}

View File

@@ -1,241 +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.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 +
'}';
}
}

View File

@@ -1,196 +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.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 +
'}';
}
}

View File

@@ -1,217 +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.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 +
'}';
}
}

View File

@@ -1,43 +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.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;
}
}

View File

@@ -1,241 +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.coordinator;
import io.seata.core.context.RootContext;
import io.seata.core.exception.BranchTransactionException;
import io.seata.core.exception.GlobalTransactionException;
import io.seata.core.exception.TransactionException;
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.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.RemotingServer;
import io.seata.server.lock.LockManager;
import io.seata.server.lock.LockerManagerFactory;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import static io.seata.core.exception.TransactionExceptionCode.*;
/**
* The type abstract core.
*
* @author ph3636
*/
public abstract class AbstractCore implements Core {
protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractCore.class);
protected LockManager lockManager = LockerManagerFactory.getLockManager();
protected RemotingServer remotingServer;
public AbstractCore(RemotingServer remotingServer) {
if (remotingServer == null) {
throw new IllegalArgumentException("remotingServer must be not null");
}
this.remotingServer = remotingServer;
}
public abstract BranchType getHandleBranchType();
@Override
public Long branchRegister(BranchType branchType, String resourceId, String clientId, String xid,
String applicationData, String lockKeys) throws TransactionException {
GlobalSession globalSession = assertGlobalSessionNotNull(xid, false);
return SessionHolder.lockAndExecute(globalSession, () -> {
globalSessionStatusCheck(globalSession);
globalSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager());
BranchSession branchSession = SessionHelper.newBranchByGlobal(globalSession, branchType, resourceId,
applicationData, lockKeys, clientId);
MDC.put(RootContext.MDC_KEY_BRANCH_ID, String.valueOf(branchSession.getBranchId()));
branchSessionLock(globalSession, branchSession);
try {
globalSession.addBranch(branchSession);
} catch (RuntimeException ex) {
branchSessionUnlock(branchSession);
throw new BranchTransactionException(FailedToAddBranch, String
.format("Failed to store branch xid = %s branchId = %s", globalSession.getXid(),
branchSession.getBranchId()), ex);
}
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Register branch successfully, xid = {}, branchId = {}, resourceId = {} ,lockKeys = {}",
globalSession.getXid(), branchSession.getBranchId(), resourceId, lockKeys);
}
return branchSession.getBranchId();
});
}
protected void globalSessionStatusCheck(GlobalSession globalSession) throws GlobalTransactionException {
if (!globalSession.isActive()) {
throw new GlobalTransactionException(GlobalTransactionNotActive, String.format(
"Could not register branch into global session xid = %s status = %s, cause by globalSession not active",
globalSession.getXid(), globalSession.getStatus()));
}
if (globalSession.getStatus() != GlobalStatus.Begin) {
throw new GlobalTransactionException(GlobalTransactionStatusInvalid, String
.format("Could not register branch into global session xid = %s status = %s while expecting %s",
globalSession.getXid(), globalSession.getStatus(), GlobalStatus.Begin));
}
}
protected void branchSessionLock(GlobalSession globalSession, BranchSession branchSession) throws TransactionException {
}
protected void branchSessionUnlock(BranchSession branchSession) throws TransactionException {
}
private GlobalSession assertGlobalSessionNotNull(String xid, boolean withBranchSessions)
throws TransactionException {
GlobalSession globalSession = SessionHolder.findGlobalSession(xid, withBranchSessions);
if (globalSession == null) {
throw new GlobalTransactionException(TransactionExceptionCode.GlobalTransactionNotExist,
String.format("Could not found global transaction xid = %s, may be has finished.", xid));
}
return globalSession;
}
@Override
public void branchReport(BranchType branchType, String xid, long branchId, BranchStatus status,
String applicationData) throws TransactionException {
GlobalSession globalSession = assertGlobalSessionNotNull(xid, true);
BranchSession branchSession = globalSession.getBranch(branchId);
if (branchSession == null) {
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);
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Report branch status successfully, xid = {}, branchId = {}", globalSession.getXid(),
branchSession.getBranchId());
}
}
@Override
public boolean lockQuery(BranchType branchType, String resourceId, String xid, String lockKeys)
throws TransactionException {
return true;
}
@Override
public BranchStatus branchCommit(GlobalSession globalSession, BranchSession branchSession) throws TransactionException {
try {
BranchCommitRequest request = new BranchCommitRequest();
request.setXid(branchSession.getXid());
request.setBranchId(branchSession.getBranchId());
request.setResourceId(branchSession.getResourceId());
request.setApplicationData(branchSession.getApplicationData());
request.setBranchType(branchSession.getBranchType());
return branchCommitSend(request, globalSession, branchSession);
} catch (IOException | TimeoutException e) {
throw new BranchTransactionException(FailedToSendBranchCommitRequest,
String.format("Send branch commit failed, xid = %s branchId = %s", branchSession.getXid(),
branchSession.getBranchId()), e);
}
}
protected BranchStatus branchCommitSend(BranchCommitRequest request, GlobalSession globalSession,
BranchSession branchSession) throws IOException, TimeoutException {
BranchCommitResponse response = (BranchCommitResponse) remotingServer.sendSyncRequest(
branchSession.getResourceId(), branchSession.getClientId(), request, branchSession.isAT());
return response.getBranchStatus();
}
@Override
public BranchStatus branchRollback(GlobalSession globalSession, BranchSession branchSession) throws TransactionException {
try {
BranchRollbackRequest request = new BranchRollbackRequest();
request.setXid(branchSession.getXid());
request.setBranchId(branchSession.getBranchId());
request.setResourceId(branchSession.getResourceId());
request.setApplicationData(branchSession.getApplicationData());
request.setBranchType(branchSession.getBranchType());
return branchRollbackSend(request, globalSession, branchSession);
} catch (IOException | TimeoutException e) {
throw new BranchTransactionException(FailedToSendBranchRollbackRequest,
String.format("Send branch rollback failed, xid = %s branchId = %s",
branchSession.getXid(), branchSession.getBranchId()), e);
}
}
protected BranchStatus branchRollbackSend(BranchRollbackRequest request, GlobalSession globalSession,
BranchSession branchSession) throws IOException, TimeoutException {
BranchRollbackResponse response = (BranchRollbackResponse) remotingServer.sendSyncRequest(
branchSession.getResourceId(), branchSession.getClientId(), request, branchSession.isAT());
return response.getBranchStatus();
}
@Override
public String begin(String applicationId, String transactionServiceGroup, String name, int timeout)
throws TransactionException {
return null;
}
@Override
public GlobalStatus commit(String xid) throws TransactionException {
return null;
}
@Override
public boolean doGlobalCommit(GlobalSession globalSession, boolean retrying) throws TransactionException {
return true;
}
@Override
public GlobalStatus globalReport(String xid, GlobalStatus globalStatus) throws TransactionException {
return null;
}
@Override
public GlobalStatus rollback(String xid) throws TransactionException {
return null;
}
@Override
public boolean doGlobalRollback(GlobalSession globalSession, boolean retrying) throws TransactionException {
return true;
}
@Override
public GlobalStatus getStatus(String xid) throws TransactionException {
return null;
}
@Override
public void doGlobalReport(GlobalSession globalSession, String xid, GlobalStatus globalStatus) throws TransactionException {
}
}

View File

@@ -1,59 +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.coordinator;
import io.seata.core.exception.TransactionException;
import io.seata.core.model.GlobalStatus;
import io.seata.server.session.GlobalSession;
/**
* The interface Core.
*
* @author sharajava
*/
public interface Core extends TransactionCoordinatorInbound, TransactionCoordinatorOutbound {
/**
* Do global commit.
*
* @param globalSession the global session
* @param retrying the retrying
* @return is global commit.
* @throws TransactionException the transaction exception
*/
boolean doGlobalCommit(GlobalSession globalSession, boolean retrying) throws TransactionException;
/**
* Do global rollback.
*
* @param globalSession the global session
* @param retrying the retrying
* @return is global rollback.
* @throws TransactionException the transaction exception
*/
boolean doGlobalRollback(GlobalSession globalSession, boolean retrying) throws TransactionException;
/**
* Do global report.
*
* @param globalSession the global session
* @param xid Transaction id.
* @param param the global status
* @throws TransactionException the transaction exception
*/
void doGlobalReport(GlobalSession globalSession, String xid, GlobalStatus param) throws TransactionException;
}

View File

@@ -1,609 +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.coordinator;
import io.netty.channel.Channel;
import io.seata.common.thread.NamedThreadFactory;
import io.seata.common.util.CollectionUtils;
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.GlobalStatus;
import io.seata.core.protocol.AbstractMessage;
import io.seata.core.protocol.AbstractResultMessage;
import io.seata.core.protocol.transaction.*;
import io.seata.core.rpc.Disposable;
import io.seata.core.rpc.RemotingServer;
import io.seata.core.rpc.RpcContext;
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.metrics.MetricsPublisher;
import io.seata.server.session.*;
import io.seata.server.store.StoreConfig;
import org.apache.commons.lang.time.DateFormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import java.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.
*/
public class DefaultCoordinator extends AbstractTCInboundHandler implements TransactionMessageHandler, Disposable {
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultCoordinator.class);
private static final int TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS = 5000;
/**
* The constant COMMITTING_RETRY_PERIOD.
*/
protected static final long COMMITTING_RETRY_PERIOD = CONFIG.getLong(ConfigurationKeys.COMMITING_RETRY_PERIOD,
DEFAULT_COMMITING_RETRY_PERIOD);
/**
* The constant ASYNC_COMMITTING_RETRY_PERIOD.
*/
protected static final long ASYNC_COMMITTING_RETRY_PERIOD = CONFIG.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,
DEFAULT_ROLLBACKING_RETRY_PERIOD);
/**
* The constant TIMEOUT_RETRY_PERIOD.
*/
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, DEFAULT_UNDO_LOG_DELETE_PERIOD);
/**
* The Transaction undo log delay delete period
*/
protected static final long UNDO_LOG_DELAY_DELETE_PERIOD = 3 * 60 * 1000;
private static final int ALWAYS_RETRY_BOUNDARY = 0;
/**
* default branch async queue size
*/
private static final int DEFAULT_BRANCH_ASYNC_QUEUE_SIZE = 5000;
/**
* the pool size of branch asynchronous remove thread pool
*/
private static final int BRANCH_ASYNC_POOL_SIZE = Runtime.getRuntime().availableProcessors() * 2;
private static final long MAX_COMMIT_RETRY_TIMEOUT = ConfigurationFactory.getInstance().getLong(
ConfigurationKeys.MAX_COMMIT_RETRY_TIMEOUT, DEFAULT_MAX_COMMIT_RETRY_TIMEOUT);
private static final long MAX_ROLLBACK_RETRY_TIMEOUT = ConfigurationFactory.getInstance().getLong(
ConfigurationKeys.MAX_ROLLBACK_RETRY_TIMEOUT, DEFAULT_MAX_ROLLBACK_RETRY_TIMEOUT);
private static final boolean ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE = ConfigurationFactory.getInstance().getBoolean(
ConfigurationKeys.ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE, DEFAULT_ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE);
private final ScheduledThreadPoolExecutor retryRollbacking =
new ScheduledThreadPoolExecutor(1, new NamedThreadFactory(RETRY_ROLLBACKING, 1));
private final ScheduledThreadPoolExecutor retryCommitting =
new ScheduledThreadPoolExecutor(1, new NamedThreadFactory(RETRY_COMMITTING, 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, GlobalStatus.Committed};
private final ThreadPoolExecutor branchRemoveExecutor;
private RemotingServer remotingServer;
private final DefaultCore core;
private static volatile DefaultCoordinator instance;
/**
* Instantiates a new Default coordinator.
*
* @param remotingServer the remoting server
*/
private DefaultCoordinator(RemotingServer remotingServer) {
if (remotingServer == null) {
throw new IllegalArgumentException("RemotingServer not allowed be null.");
}
this.remotingServer = remotingServer;
this.core = new DefaultCore(remotingServer);
boolean enableBranchAsyncRemove = CONFIG.getBoolean(
ConfigurationKeys.ENABLE_BRANCH_ASYNC_REMOVE, DEFAULT_ENABLE_BRANCH_ASYNC_REMOVE);
// create branchRemoveExecutor
if (enableBranchAsyncRemove && StoreConfig.getSessionMode() != StoreConfig.SessionMode.FILE) {
branchRemoveExecutor = new ThreadPoolExecutor(BRANCH_ASYNC_POOL_SIZE, BRANCH_ASYNC_POOL_SIZE,
Integer.MAX_VALUE, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(
CONFIG.getInt(ConfigurationKeys.SESSION_BRANCH_ASYNC_QUEUE_SIZE, DEFAULT_BRANCH_ASYNC_QUEUE_SIZE)
), new NamedThreadFactory("branchSessionRemove", BRANCH_ASYNC_POOL_SIZE),
new ThreadPoolExecutor.CallerRunsPolicy());
} else {
branchRemoveExecutor = null;
}
}
public static DefaultCoordinator getInstance(RemotingServer remotingServer) {
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 {
response.setXid(core.begin(rpcContext.getApplicationId(), rpcContext.getTransactionServiceGroup(),
request.getTransactionName(), request.getTimeout()));
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Begin new global transaction applicationId: {},transactionServiceGroup: {}, transactionName: {},timeout:{},xid:{}",
rpcContext.getApplicationId(), rpcContext.getTransactionServiceGroup(), request.getTransactionName(), request.getTimeout(), response.getXid());
}
}
@Override
protected void doGlobalCommit(GlobalCommitRequest request, GlobalCommitResponse response, RpcContext rpcContext)
throws TransactionException {
MDC.put(RootContext.MDC_KEY_XID, request.getXid());
response.setGlobalStatus(core.commit(request.getXid()));
}
@Override
protected void doGlobalRollback(GlobalRollbackRequest request, GlobalRollbackResponse response,
RpcContext rpcContext) throws TransactionException {
MDC.put(RootContext.MDC_KEY_XID, request.getXid());
response.setGlobalStatus(core.rollback(request.getXid()));
}
@Override
protected void doGlobalStatus(GlobalStatusRequest request, GlobalStatusResponse response, RpcContext rpcContext)
throws TransactionException {
MDC.put(RootContext.MDC_KEY_XID, request.getXid());
response.setGlobalStatus(core.getStatus(request.getXid()));
}
@Override
protected void doGlobalReport(GlobalReportRequest request, GlobalReportResponse response, RpcContext rpcContext)
throws TransactionException {
MDC.put(RootContext.MDC_KEY_XID, request.getXid());
response.setGlobalStatus(core.globalReport(request.getXid(), request.getGlobalStatus()));
}
@Override
protected void doBranchRegister(BranchRegisterRequest request, BranchRegisterResponse response,
RpcContext rpcContext) throws TransactionException {
MDC.put(RootContext.MDC_KEY_XID, request.getXid());
response.setBranchId(
core.branchRegister(request.getBranchType(), request.getResourceId(), rpcContext.getClientId(),
request.getXid(), request.getApplicationData(), request.getLockKey()));
}
@Override
protected void doBranchReport(BranchReportRequest request, BranchReportResponse response, RpcContext rpcContext)
throws TransactionException {
MDC.put(RootContext.MDC_KEY_XID, request.getXid());
MDC.put(RootContext.MDC_KEY_BRANCH_ID, String.valueOf(request.getBranchId()));
core.branchReport(request.getBranchType(), request.getXid(), request.getBranchId(), request.getStatus(),
request.getApplicationData());
}
@Override
protected void doLockCheck(GlobalLockQueryRequest request, GlobalLockQueryResponse response, RpcContext rpcContext)
throws TransactionException {
MDC.put(RootContext.MDC_KEY_XID, request.getXid());
response.setLockable(
core.lockQuery(request.getBranchType(), request.getResourceId(), request.getXid(), request.getLockKey()));
}
/**
* Timeout check.
*/
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 (!beginGlobalsessions.isEmpty() && LOGGER.isDebugEnabled()) {
LOGGER.debug("Global transaction timeout check begin, size: {}", beginGlobalsessions.size());
}
SessionHelper.forEach(beginGlobalsessions, globalSession -> {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(
globalSession.getXid() + " " + globalSession.getStatus() + " " + globalSession.getBeginTime() + " "
+ globalSession.getTimeout());
}
SessionHolder.lockAndExecute(globalSession, () -> {
if (globalSession.getStatus() != GlobalStatus.Begin || !globalSession.isTimeout()) {
return false;
}
LOGGER.warn("Global transaction[{}] is timeout and will be rollback,transaction begin time:{} and now:{}", globalSession.getXid(),
DateFormatUtils.ISO_DATE_FORMAT.format(globalSession.getBeginTime()), DateFormatUtils.ISO_DATE_FORMAT.format(System.currentTimeMillis()));
globalSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager());
globalSession.close();
globalSession.setStatus(GlobalStatus.TimeoutRollbacking);
globalSession.addSessionLifecycleListener(SessionHolder.getRetryRollbackingSessionManager());
SessionHolder.getRetryRollbackingSessionManager().addGlobalSession(globalSession);
// transaction timeout and start rollbacking event
MetricsPublisher.postSessionDoingEvent(globalSession, GlobalStatus.TimeoutRollbacking.name(), false, false);
return true;
});
});
if (!beginGlobalsessions.isEmpty() && LOGGER.isDebugEnabled()) {
LOGGER.debug("Global transaction timeout check end. ");
}
}
/**
* Handle retry rollbacking.
*/
protected void handleRetryRollbacking() {
SessionCondition sessionCondition = new SessionCondition(rollbackingStatuses);
sessionCondition.setLazyLoadBranch(true);
Collection<GlobalSession> rollbackingSessions =
SessionHolder.getRetryRollbackingSessionManager().findGlobalSessions(sessionCondition);
if (CollectionUtils.isEmpty(rollbackingSessions)) {
return;
}
long now = System.currentTimeMillis();
SessionHelper.forEach(rollbackingSessions, rollbackingSession -> {
try {
// prevent repeated rollback
if (rollbackingSession.getStatus() == GlobalStatus.Rollbacking
&& !rollbackingSession.isDeadSession()) {
// The function of this 'return' is 'continue'.
return;
}
if (isRetryTimeout(now, MAX_ROLLBACK_RETRY_TIMEOUT, rollbackingSession.getBeginTime())) {
if (ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE) {
rollbackingSession.clean();
}
SessionHelper.endRollbackFailed(rollbackingSession, true, true);
//The function of this 'return' is 'continue'.
return;
}
rollbackingSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager());
core.doGlobalRollback(rollbackingSession, true);
} catch (TransactionException ex) {
LOGGER.error("Failed to retry rollbacking [{}] {} {}", rollbackingSession.getXid(), ex.getCode(), ex.getMessage());
}
});
}
/**
* Handle retry committing.
*/
protected void handleRetryCommitting() {
SessionCondition retryCommittingSessionCondition = new SessionCondition(retryCommittingStatuses);
retryCommittingSessionCondition.setLazyLoadBranch(true);
Collection<GlobalSession> committingSessions =
SessionHolder.getRetryCommittingSessionManager().findGlobalSessions(retryCommittingSessionCondition);
if (CollectionUtils.isEmpty(committingSessions)) {
return;
}
long now = System.currentTimeMillis();
SessionHelper.forEach(committingSessions, committingSession -> {
try {
// prevent repeated commit
if (GlobalStatus.Committing.equals(committingSession.getStatus()) && !committingSession.isDeadSession()) {
// The function of this 'return' is 'continue'.
return;
}
if (isRetryTimeout(now, MAX_COMMIT_RETRY_TIMEOUT, committingSession.getBeginTime())) {
// commit retry timeout event
SessionHelper.endCommitFailed(committingSession, true, true);
//The function of this 'return' is 'continue'.
return;
}
if (GlobalStatus.Committed.equals(committingSession.getStatus())
&& committingSession.getBranchSessions().isEmpty()) {
SessionHelper.endCommitted(committingSession,true);
}
committingSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager());
core.doGlobalCommit(committingSession, true);
} catch (TransactionException ex) {
LOGGER.error("Failed to retry committing [{}] {} {}", committingSession.getXid(), ex.getCode(), ex.getMessage());
}
});
}
/**
* Handle async committing.
*/
protected void handleAsyncCommitting() {
SessionCondition sessionCondition = new SessionCondition(GlobalStatus.AsyncCommitting);
Collection<GlobalSession> asyncCommittingSessions =
SessionHolder.getAsyncCommittingSessionManager().findGlobalSessions(sessionCondition);
if (CollectionUtils.isEmpty(asyncCommittingSessions)) {
return;
}
SessionHelper.forEach(asyncCommittingSessions, asyncCommittingSession -> {
try {
asyncCommittingSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager());
core.doGlobalCommit(asyncCommittingSession, true);
} catch (TransactionException ex) {
LOGGER.error("Failed to async committing [{}] {} {}", asyncCommittingSession.getXid(), ex.getCode(), ex.getMessage(), ex);
}
});
}
/**
* Undo log delete.
*/
protected void undoLogDelete() {
Map<String, Channel> rmChannels = ChannelManager.getRmChannels();
if (rmChannels == null || rmChannels.isEmpty()) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("no active rm channels to delete undo log");
}
return;
}
short saveDays = CONFIG.getShort(ConfigurationKeys.TRANSACTION_UNDO_LOG_SAVE_DAYS,
UndoLogDeleteRequest.DEFAULT_SAVE_DAYS);
for (Map.Entry<String, Channel> channelEntry : rmChannels.entrySet()) {
String resourceId = channelEntry.getKey();
UndoLogDeleteRequest deleteRequest = new UndoLogDeleteRequest();
deleteRequest.setResourceId(resourceId);
deleteRequest.setSaveDays(saveDays > 0 ? saveDays : UndoLogDeleteRequest.DEFAULT_SAVE_DAYS);
try {
remotingServer.sendAsyncRequest(channelEntry.getValue(), deleteRequest);
} catch (Exception e) {
LOGGER.error("Failed to async delete undo log resourceId = {}, exception: {}", resourceId, e.getMessage());
}
}
}
private boolean isRetryTimeout(long now, long timeout, long beginTime) {
return timeout >= ALWAYS_RETRY_BOUNDARY && now - beginTime > timeout;
}
/**
* Init.
*/
public void init() {
retryRollbacking.scheduleAtFixedRate(
() -> SessionHolder.distributedLockAndExecute(RETRY_ROLLBACKING, this::handleRetryRollbacking), 0,
ROLLBACKING_RETRY_PERIOD, TimeUnit.MILLISECONDS);
retryCommitting.scheduleAtFixedRate(
() -> SessionHolder.distributedLockAndExecute(RETRY_COMMITTING, this::handleRetryCommitting), 0,
COMMITTING_RETRY_PERIOD, TimeUnit.MILLISECONDS);
asyncCommitting.scheduleAtFixedRate(
() -> SessionHolder.distributedLockAndExecute(ASYNC_COMMITTING, this::handleAsyncCommitting), 0,
ASYNC_COMMITTING_RETRY_PERIOD, TimeUnit.MILLISECONDS);
timeoutCheck.scheduleAtFixedRate(
() -> SessionHolder.distributedLockAndExecute(TX_TIMEOUT_CHECK, this::timeoutCheck), 0,
TIMEOUT_RETRY_PERIOD, TimeUnit.MILLISECONDS);
undoLogDelete.scheduleAtFixedRate(
() -> SessionHolder.distributedLockAndExecute(UNDOLOG_DELETE, this::undoLogDelete),
UNDO_LOG_DELAY_DELETE_PERIOD, UNDO_LOG_DELETE_PERIOD, TimeUnit.MILLISECONDS);
}
@Override
public AbstractResultMessage onRequest(AbstractMessage request, RpcContext context) {
if (!(request instanceof AbstractTransactionRequestToTC)) {
throw new IllegalArgumentException();
}
AbstractTransactionRequestToTC transactionRequest = (AbstractTransactionRequestToTC) request;
transactionRequest.setTCInboundHandler(this);
return transactionRequest.handle(context);
}
@Override
public void onResponse(AbstractResultMessage response, RpcContext context) {
if (!(response instanceof AbstractTransactionResponse)) {
throw new IllegalArgumentException();
}
}
@Override
public void destroy() {
// 1. first shutdown timed task
retryRollbacking.shutdown();
retryCommitting.shutdown();
asyncCommitting.shutdown();
timeoutCheck.shutdown();
undoLogDelete.shutdown();
if (branchRemoveExecutor != null) {
branchRemoveExecutor.shutdown();
}
try {
retryRollbacking.awaitTermination(TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS, TimeUnit.MILLISECONDS);
retryCommitting.awaitTermination(TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS, TimeUnit.MILLISECONDS);
asyncCommitting.awaitTermination(TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS, TimeUnit.MILLISECONDS);
timeoutCheck.awaitTermination(TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS, TimeUnit.MILLISECONDS);
undoLogDelete.awaitTermination(TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS, TimeUnit.MILLISECONDS);
if (branchRemoveExecutor != null) {
branchRemoveExecutor.awaitTermination(TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS, TimeUnit.MILLISECONDS);
}
} catch (InterruptedException ignore) {
}
// 2. second close netty flow
if (remotingServer instanceof NettyRemotingServer) {
((NettyRemotingServer) remotingServer).destroy();
}
// 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;
if (branchSession == null) {
throw new IllegalArgumentException("BranchSession can`t be null!");
}
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);
}
}
}
}

View File

@@ -1,399 +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.coordinator;
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.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.metrics.MetricsPublisher;
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 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;
/**
* The type Default core.
*
* @author sharajava
*/
public class DefaultCore implements Core {
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultCore.class);
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<>();
/**
* get the Default core.
*
* @param remotingServer the remoting server
*/
public DefaultCore(RemotingServer remotingServer) {
List<AbstractCore> allCore = EnhancedServiceLoader.loadAll(AbstractCore.class,
new Class[] {RemotingServer.class}, new Object[] {remotingServer});
if (CollectionUtils.isNotEmpty(allCore)) {
for (AbstractCore core : allCore) {
coreMap.put(core.getHandleBranchType(), core);
}
}
}
/**
* get core
*
* @param branchType the branchType
* @return the core
*/
public AbstractCore getCore(BranchType branchType) {
AbstractCore core = coreMap.get(branchType);
if (core == null) {
throw new NotSupportYetException("unsupported type:" + branchType.name());
}
return core;
}
/**
* only for mock
*
* @param branchType the branchType
* @param core the core
*/
public void mockCore(BranchType branchType, AbstractCore core) {
coreMap.put(branchType, core);
}
@Override
public Long branchRegister(BranchType branchType, String resourceId, String clientId, String xid,
String applicationData, String lockKeys) throws TransactionException {
return getCore(branchType).branchRegister(branchType, resourceId, clientId, xid,
applicationData, lockKeys);
}
@Override
public void branchReport(BranchType branchType, String xid, long branchId, BranchStatus status,
String applicationData) throws TransactionException {
getCore(branchType).branchReport(branchType, xid, branchId, status, applicationData);
}
@Override
public boolean lockQuery(BranchType branchType, String resourceId, String xid, String lockKeys)
throws TransactionException {
return getCore(branchType).lockQuery(branchType, resourceId, xid, lockKeys);
}
@Override
public BranchStatus branchCommit(GlobalSession globalSession, BranchSession branchSession) throws TransactionException {
return getCore(branchSession.getBranchType()).branchCommit(globalSession, branchSession);
}
@Override
public BranchStatus branchRollback(GlobalSession globalSession, BranchSession branchSession) throws TransactionException {
return getCore(branchSession.getBranchType()).branchRollback(globalSession, branchSession);
}
@Override
public String begin(String applicationId, String transactionServiceGroup, String name, int timeout)
throws TransactionException {
GlobalSession session = GlobalSession.createGlobalSession(applicationId, transactionServiceGroup, name, timeout);
MDC.put(RootContext.MDC_KEY_XID, session.getXid());
session.addSessionLifecycleListener(SessionHolder.getRootSessionManager());
session.begin();
// transaction start event
MetricsPublisher.postSessionDoingEvent(session, false);
return session.getXid();
}
@Override
public GlobalStatus commit(String xid) throws TransactionException {
GlobalSession globalSession = SessionHolder.findGlobalSession(xid);
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, () -> {
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.changeGlobalStatus(GlobalStatus.Committing);
return true;
}
}
return false;
});
if (shouldCommit) {
boolean success = doGlobalCommit(globalSession, false);
//If successful and all remaining branches can be committed asynchronously, do async commit.
if (success && globalSession.hasBranch() && globalSession.canBeCommittedAsync()) {
globalSession.asyncCommit();
return GlobalStatus.Committed;
} else {
return globalSession.getStatus();
}
} else {
return globalSession.getStatus() == GlobalStatus.AsyncCommitting ? GlobalStatus.Committed : globalSession.getStatus();
}
}
@Override
public boolean doGlobalCommit(GlobalSession globalSession, boolean retrying) throws TransactionException {
boolean success = true;
// start committing event
MetricsPublisher.postSessionDoingEvent(globalSession, retrying);
if (globalSession.isSaga()) {
success = getCore(BranchType.SAGA).doGlobalCommit(globalSession, retrying);
} else {
Boolean result = SessionHelper.forEach(globalSession.getSortedBranches(), branchSession -> {
// if not retrying, skip the canBeCommittedAsync branches
if (!retrying && branchSession.canBeCommittedAsync()) {
return CONTINUE;
}
BranchStatus currentStatus = branchSession.getStatus();
if (currentStatus == BranchStatus.PhaseOne_Failed) {
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:
SessionHelper.removeBranch(globalSession, branchSession, !retrying);
LOGGER.info("Commit branch transaction successfully, xid = {} branchId = {}", globalSession.getXid(), branchSession.getBranchId());
return CONTINUE;
case PhaseTwo_CommitFailed_Unretryable:
//not at branch
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();
return false;
}
if (globalSession.canBeCommittedAsync()) {
LOGGER.error("Committing branch transaction[{}], status:{} and will retry later",
branchSession.getBranchId(), branchStatus);
return CONTINUE;
} else {
LOGGER.error(
"Committing global transaction[{}] failed, caused by branch transaction[{}] commit failed, will retry later.", globalSession.getXid(), branchSession.getBranchId());
return false;
}
}
} catch (Exception ex) {
StackTraceLogger.error(LOGGER, ex, "Committing branch transaction exception: {}",
new String[] {branchSession.toString()});
if (!retrying) {
globalSession.queueToRetryCommit();
throw new TransactionException(ex);
}
}
return CONTINUE;
});
// Return if the result is not null
if (result != null) {
return result;
}
//If has branch and not all remaining branches can be committed asynchronously,
//do print log and return false
if (globalSession.hasBranch() && !globalSession.canBeCommittedAsync()) {
LOGGER.info("Committing global transaction is NOT done, xid = {}.", globalSession.getXid());
return false;
}
}
// 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()) {
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;
}
@Override
public GlobalStatus rollback(String xid) throws TransactionException {
GlobalSession globalSession = SessionHolder.findGlobalSession(xid);
if (globalSession == null) {
return GlobalStatus.Finished;
}
globalSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager());
// just lock changeStatus
boolean shouldRollBack = SessionHolder.lockAndExecute(globalSession, () -> {
globalSession.close(); // Highlight: Firstly, close the session, then no more branch can be registered.
if (globalSession.getStatus() == GlobalStatus.Begin) {
globalSession.changeGlobalStatus(GlobalStatus.Rollbacking);
return true;
}
return false;
});
if (!shouldRollBack) {
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
MetricsPublisher.postSessionDoingEvent(globalSession, retrying);
if (globalSession.isSaga()) {
success = getCore(BranchType.SAGA).doGlobalRollback(globalSession, retrying);
} else {
Boolean result = SessionHelper.forEach(globalSession.getReverseSortedBranches(), branchSession -> {
BranchStatus currentBranchStatus = branchSession.getStatus();
if (currentBranchStatus == BranchStatus.PhaseOne_Failed) {
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:
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, retrying);
LOGGER.error("Rollback branch transaction fail and stop retry, xid = {} branchId = {}", globalSession.getXid(), branchSession.getBranchId());
return false;
default:
LOGGER.error("Rollback branch transaction fail and will retry, xid = {} branchId = {}", globalSession.getXid(), branchSession.getBranchId());
if (!retrying) {
globalSession.queueToRetryRollback();
}
return false;
}
} catch (Exception ex) {
StackTraceLogger.error(LOGGER, ex,
"Rollback branch transaction exception, xid = {} branchId = {} exception = {}",
new String[] {globalSession.getXid(), String.valueOf(branchSession.getBranchId()), ex.getMessage()});
if (!retrying) {
globalSession.queueToRetryRollback();
}
throw new TransactionException(ex);
}
});
// Return if the result is not null
if (result != null) {
return result;
}
}
// 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, retrying);
LOGGER.info("Rollback global transaction successfully, xid = {}.", globalSession.getXid());
}
return success;
}
@Override
public GlobalStatus getStatus(String xid) throws TransactionException {
GlobalSession globalSession = SessionHolder.findGlobalSession(xid, false);
if (globalSession == null) {
return GlobalStatus.Finished;
} else {
return globalSession.getStatus();
}
}
@Override
public GlobalStatus globalReport(String xid, GlobalStatus globalStatus) throws TransactionException {
GlobalSession globalSession = SessionHolder.findGlobalSession(xid);
if (globalSession == null) {
return globalStatus;
}
globalSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager());
doGlobalReport(globalSession, xid, globalStatus);
return globalSession.getStatus();
}
@Override
public void doGlobalReport(GlobalSession globalSession, String xid, GlobalStatus globalStatus) throws TransactionException {
if (globalSession.isSaga()) {
getCore(BranchType.SAGA).doGlobalReport(globalSession, xid, globalStatus);
}
}
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;
}
}
}

View File

@@ -1,29 +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.coordinator;
import io.seata.core.model.ResourceManagerOutbound;
import io.seata.core.model.TransactionManager;
/**
* receive inbound request from RM or TM.
*
* @author zhangchenghui.dev@gmail.com
* @since 1.1.0
*/
public interface TransactionCoordinatorInbound extends ResourceManagerOutbound, TransactionManager {
}

View File

@@ -1,54 +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.coordinator;
import io.seata.core.exception.TransactionException;
import io.seata.core.model.BranchStatus;
import io.seata.server.session.BranchSession;
import io.seata.server.session.GlobalSession;
/**
* send outbound request to RM.
*
* @author zhangchenghui.dev@gmail.com
* @since 1.1.0
*/
public interface TransactionCoordinatorOutbound {
/**
* Commit a branch transaction.
*
* @param globalSession the global session
* @param branchSession the branch session
* @return Status of the branch after committing.
* @throws TransactionException Any exception that fails this will be wrapped with TransactionException and thrown
* out.
*/
BranchStatus branchCommit(GlobalSession globalSession, BranchSession branchSession) throws TransactionException;
/**
* Rollback a branch transaction.
*
* @param globalSession the global session
* @param branchSession the branch session
* @return Status of the branch after rollbacking.
* @throws TransactionException Any exception that fails this will be wrapped with TransactionException and thrown
* out.
*/
BranchStatus branchRollback(GlobalSession globalSession, BranchSession branchSession) throws TransactionException;
}

View File

@@ -1,103 +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.env;
import io.seata.common.util.NumberUtils;
import io.seata.common.util.StringUtils;
import static io.seata.core.constants.ConfigurationKeys.ENV_SEATA_PORT_KEY;
/**
* @author xingfudeshi@gmail.com
* @author wang.liang
*/
public class ContainerHelper {
private static final String C_GROUP_PATH = "/proc/1/cgroup";
private static final String DOCKER_PATH = "/docker";
private static final String KUBEPODS_PATH = "/kubepods";
private static final String ENV_SYSTEM_KEY = "SEATA_ENV";
private static final String ENV_SEATA_IP_KEY = "SEATA_IP";
private static final String ENV_SERVER_NODE_KEY = "SERVER_NODE";
private static final String ENV_STORE_MODE_KEY = "STORE_MODE";
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.
*
* @return the env
*/
public static String getEnv() {
return StringUtils.trimToNull(System.getenv(ENV_SYSTEM_KEY));
}
/**
* Gets host from container.
*
* @return the env
*/
public static String getHost() {
return StringUtils.trimToNull(System.getenv(ENV_SEATA_IP_KEY));
}
/**
* Gets port from container.
*
* @return the env
*/
public static int getPort() {
return NumberUtils.toInt(System.getenv(ENV_SEATA_PORT_KEY), 0);
}
/**
* Gets server node from container.
*
* @return the env
*/
public static Long getServerNode() {
return NumberUtils.toLong(System.getenv(ENV_SERVER_NODE_KEY));
}
/**
* Gets store mode from container.
*
* @return the env
*/
public static String getStoreMode() {
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));
}
}

View File

@@ -1,130 +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.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 java.io.*;
import java.util.Map;
import java.util.Properties;
/**
* @author wang.liang
*/
public class PortHelper {
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) {
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;
}
}

View File

@@ -1,34 +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.event;
import io.seata.core.event.EventBus;
import io.seata.core.event.GuavaEventBus;
/**
* Manager hold the singleton event bus instance.
*
* @author zhengyangyong
*/
public class EventBusManager {
private static class SingletonHolder {
private static EventBus INSTANCE = new GuavaEventBus("tc",true);
}
public static EventBus get() {
return SingletonHolder.INSTANCE;
}
}

View File

@@ -1,198 +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.lock;
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.
*
* @author zhangsen
*/
public abstract class AbstractLockManager implements LockManager {
/**
* The constant LOGGER.
*/
protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractLockManager.class);
@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.");
}
String lockKey = branchSession.getLockKey();
if (StringUtils.isNullOrEmpty(lockKey)) {
// no lock
return true;
}
// get locks of branch
List<RowLock> locks = collectRowLocks(branchSession);
if (CollectionUtils.isEmpty(locks)) {
// no lock
return true;
}
return getLocker(branchSession).acquireLock(locks, autoCommit, skipCheckLock);
}
@Override
public boolean releaseLock(BranchSession branchSession) throws TransactionException {
if (branchSession == null) {
throw new IllegalArgumentException("branchSession can't be null for memory/file locker.");
}
List<RowLock> locks = collectRowLocks(branchSession);
try {
return getLocker(branchSession).releaseLock(locks);
} catch (Exception t) {
LOGGER.error("unLock error, branchSession:{}", branchSession, t);
return false;
}
}
@Override
public boolean isLockable(String xid, String resourceId, String lockKey) throws TransactionException {
if (StringUtils.isBlank(lockKey)) {
// no lock
return true;
}
List<RowLock> locks = collectRowLocks(lockKey, resourceId, xid);
try {
return getLocker().isLockable(locks);
} catch (Exception t) {
LOGGER.error("isLockable error, xid:{} resourceId:{}, lockKey:{}", xid, resourceId, lockKey, t);
return false;
}
}
@Override
public void cleanAllLocks() throws TransactionException {
getLocker().cleanAllLocks();
}
/**
* Gets locker.
*
* @return the locker
*/
protected Locker getLocker() {
return getLocker(null);
}
/**
* Gets locker.
*
* @param branchSession the branch session
* @return the locker
*/
protected abstract Locker getLocker(BranchSession branchSession);
@Override
public List<RowLock> collectRowLocks(BranchSession branchSession) {
if (branchSession == null || StringUtils.isBlank(branchSession.getLockKey())) {
return Collections.emptyList();
}
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, branchId);
}
/**
* Collect row locks list.
*
* @param lockKey the lock key
* @param resourceId the resource id
* @param xid the xid
* @return the list
*/
protected List<RowLock> collectRowLocks(String lockKey, String resourceId, String xid) {
return collectRowLocks(lockKey, resourceId, xid, XID.getTransactionId(xid), null);
}
/**
* Collect row locks list.
*
* @param lockKey the lock key
* @param resourceId the resource id
* @param xid the xid
* @param transactionId the transaction id
* @param branchID the branch id
* @return the list
*/
protected List<RowLock> collectRowLocks(String lockKey, String resourceId, String xid, Long transactionId,
Long branchID) {
List<RowLock> locks = new ArrayList<>();
String[] tableGroupedLockKeys = lockKey.split(";");
for (String tableGroupedLockKey : tableGroupedLockKeys) {
int idx = tableGroupedLockKey.indexOf(":");
if (idx < 0) {
return locks;
}
String tableName = tableGroupedLockKey.substring(0, idx);
String mergedPKs = tableGroupedLockKey.substring(idx + 1);
if (StringUtils.isBlank(mergedPKs)) {
return locks;
}
String[] pks = mergedPKs.split(",");
if (pks == null || pks.length == 0) {
return locks;
}
for (String pk : pks) {
if (StringUtils.isNotBlank(pk)) {
RowLock rowLock = new RowLock();
rowLock.setXid(xid);
rowLock.setTransactionId(transactionId);
rowLock.setBranchId(branchID);
rowLock.setTableName(tableName);
rowLock.setPk(pk);
rowLock.setResourceId(resourceId);
locks.add(rowLock);
}
}
}
return locks;
}
@Override
public void updateLockStatus(String xid, LockStatus lockStatus) {
this.getLocker().updateLockStatus(xid, lockStatus);
}
}

View File

@@ -1,106 +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.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.
*
* @author sharajava
*/
public interface LockManager {
/**
* Acquire lock boolean.
*
* @param branchSession the branch session
* @return the boolean
* @throws TransactionException the transaction exception
*/
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
* @return the boolean
* @throws TransactionException the transaction exception
*/
boolean releaseLock(BranchSession branchSession) throws TransactionException;
/**
* Un lock boolean.
*
* @param globalSession the global session
* @return the boolean
* @throws TransactionException the transaction exception
*/
boolean releaseGlobalSessionLock(GlobalSession globalSession) throws TransactionException;
/**
* Is lockable boolean.
*
* @param xid the xid
* @param resourceId the resource id
* @param lockKey the lock key
* @return the boolean
* @throws TransactionException the transaction exception
*/
boolean isLockable(String xid, String resourceId, String lockKey) throws TransactionException;
/**
* Clean all locks.
*
* @throws TransactionException the transaction exception
*/
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;
}

View File

@@ -1,74 +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.lock;
import io.seata.common.loader.EnhancedServiceLoader;
import io.seata.config.Configuration;
import io.seata.config.ConfigurationFactory;
import io.seata.server.store.StoreConfig;
import io.seata.server.store.StoreConfig.LockMode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The type Lock manager factory.
*
* @author sharajava
*/
public class LockerManagerFactory {
private static final Logger LOGGER = LoggerFactory.getLogger(LockerManagerFactory.class);
private static final Configuration CONFIG = ConfigurationFactory.getInstance();
/**
* the lock manager
*/
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();
}
LOGGER.info("use lock store mode: {}", lockMode.getName());
//if not exist the lock mode, throw exception
if (null != StoreConfig.StoreMode.get(lockMode.name())) {
LOCK_MANAGER = EnhancedServiceLoader.load(LockManager.class, lockMode.getName());
}
}
}
}
}
}

View File

@@ -1,69 +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.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;
}
}

View File

@@ -1,75 +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.listener;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.LoggerContextListener;
import ch.qos.logback.core.Context;
import ch.qos.logback.core.spi.ContextAwareBase;
import ch.qos.logback.core.spi.LifeCycle;
import io.seata.core.constants.ConfigurationKeys;
/**
* @author wang.liang
*/
public class SystemPropertyLoggerContextListener extends ContextAwareBase implements LoggerContextListener, LifeCycle {
private boolean started = false;
@Override
public void start() {
if (started) {
return;
}
Context context = getContext();
context.putProperty("RPC_PORT", System.getProperty(ConfigurationKeys.SERVER_SERVICE_PORT_CAMEL));
started = true;
}
@Override
public void stop() {
}
@Override
public boolean isStarted() {
return started;
}
@Override
public boolean isResetResistant() {
return true;
}
@Override
public void onStart(LoggerContext context) {
}
@Override
public void onReset(LoggerContext context) {
}
@Override
public void onStop(LoggerContext context) {
}
@Override
public void onLevelChange(Logger logger, Level level) {
}
}

View File

@@ -1,35 +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.ExtendedThrowableProxyConverter;
import ch.qos.logback.classic.spi.IThrowableProxy;
import ch.qos.logback.core.CoreConstants;
/**
* {@link ExtendedThrowableProxyConverter} that adds some additional whitespace around the
* stack trace.
* Copied from spring-boot-xxx.jar by wang.liang
* @author Phillip Webb
*/
public class ExtendedWhitespaceThrowableProxyConverter extends ExtendedThrowableProxyConverter {
@Override
protected String throwableProxyToString(IThrowableProxy tp) {
return "==>" + CoreConstants.LINE_SEPARATOR + super.throwableProxyToString(tp)
+ "<==" + CoreConstants.LINE_SEPARATOR + CoreConstants.LINE_SEPARATOR;
}
}

View File

@@ -1,45 +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.appender;
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
*
* @author wang.liang
* @since 1.5.0
*/
public class EnhancedLogstashEncoder extends LogstashEncoder {
/**
* set exclude provider
*
* @param excludedProviderClassName the excluded provider class name
*/
public void setExcludeProvider(String excludedProviderClassName) {
JsonProviders<?> providers = getFormatter().getProviders();
for (JsonProvider<?> provider : new ArrayList<>(providers.getProviders())) {
if (provider.getClass().getName().equals(excludedProviderClassName)) {
providers.removeProvider((JsonProvider) provider);
}
}
}
}

View File

@@ -1,108 +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.metrics;
import io.seata.metrics.Id;
import io.seata.metrics.IdConstants;
/**
* Constants for meter id in tc
*
* @author zhengyangyong
*/
public interface MeterIdConstants {
Id COUNTER_ACTIVE = 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_ACTIVE);
Id COUNTER_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_COMMITTED);
Id COUNTER_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_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)
.withTag(IdConstants.STATUS_KEY, IdConstants.STATUS_VALUE_COMMITTED);
Id SUMMARY_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_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);
Id TIMER_ROLLBACK = 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_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);
}

View File

@@ -1,65 +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.metrics;
import io.seata.config.ConfigurationFactory;
import io.seata.core.constants.ConfigurationKeys;
import io.seata.metrics.exporter.Exporter;
import io.seata.metrics.exporter.ExporterFactory;
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
*
* @author zhengyangyong
*/
public class MetricsManager {
private static class SingletonHolder {
private static MetricsManager INSTANCE = new MetricsManager();
}
public static final MetricsManager get() {
return SingletonHolder.INSTANCE;
}
private Registry registry;
public Registry getRegistry() {
return registry;
}
public void init() {
boolean enabled = ConfigurationFactory.getInstance().getBoolean(
ConfigurationKeys.METRICS_PREFIX + ConfigurationKeys.METRICS_ENABLED, DEFAULT_METRICS_ENABLED);
if (enabled) {
registry = RegistryFactory.getInstance();
if (registry != null) {
List<Exporter> exporters = ExporterFactory.getInstanceList();
//only at least one metrics exporter implement had imported in pom then need register MetricsSubscriber
if (exporters.size() != 0) {
exporters.forEach(exporter -> exporter.setRegistry(registry));
EventBusManager.get().register(new MetricsSubscriber(registry));
}
}
}
}
}

View File

@@ -1,97 +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.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));
}
}

View File

@@ -1,214 +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.metrics;
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.*;
/**
* Event subscriber for metrics
*
* @author zhengyangyong
*/
public class MetricsSubscriber {
private static final Logger LOGGER = LoggerFactory.getLogger(MetricsSubscriber.class);
private final Registry registry;
private final Map<String, Consumer<GlobalTransactionEvent>> consumers;
public MetricsSubscriber(Registry registry) {
this.registry = registry;
consumers = new HashMap<>();
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.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) {
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())
.withTag(GROUP_KEY, event.getGroup())).increase(1);
registry.getTimer(MeterIdConstants.TIMER_COMMITTED
.withTag(APP_ID_KEY, event.getApplicationId())
.withTag(GROUP_KEY, event.getGroup()))
.record(event.getEndTime() - event.getBeginTime(), TimeUnit.MILLISECONDS);
}
private void processGlobalStatusRollbacked(GlobalTransactionEvent event) {
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())
.withTag(GROUP_KEY, event.getGroup())).increase(1);
registry.getTimer(MeterIdConstants.TIMER_ROLLBACK
.withTag(APP_ID_KEY, event.getApplicationId())
.withTag(GROUP_KEY, event.getGroup()))
.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) {
decreaseActive(event);
reportFailed(event);
}
private void processGlobalStatusRollbackFailed(GlobalTransactionEvent event) {
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 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
public void recordGlobalTransactionEventForMetrics(GlobalTransactionEvent event) {
if (registry != null && consumers.containsKey(event.getStatus())) {
consumers.get(event.getStatus()).accept(event);
}
}
@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();
}
}

View File

@@ -1,199 +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.session;
import io.seata.core.exception.BranchTransactionException;
import io.seata.core.exception.GlobalTransactionException;
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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The type Abstract session manager.
*/
public abstract class AbstractSessionManager implements SessionManager, SessionLifecycleListener {
/**
* The constant LOGGER.
*/
protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractSessionManager.class);
/**
* The Transaction store manager.
*/
protected TransactionStoreManager transactionStoreManager;
/**
* The Name.
*/
protected String name;
/**
* Instantiates a new Abstract session manager.
*/
public AbstractSessionManager() {
}
/**
* Instantiates a new Abstract session manager.
*
* @param name the name
*/
public AbstractSessionManager(String name) {
this.name = name;
}
@Override
public void addGlobalSession(GlobalSession session) throws TransactionException {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("MANAGER[{}] SESSION[{}] {}", name, session, LogOperation.GLOBAL_ADD);
}
writeSession(LogOperation.GLOBAL_ADD, session);
}
@Override
public void updateGlobalSessionStatus(GlobalSession session, GlobalStatus status) throws TransactionException {
if (LOGGER.isDebugEnabled()) {
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);
}
@Override
public void removeGlobalSession(GlobalSession session) throws TransactionException {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("MANAGER[{}] SESSION[{}] {}", name, session, LogOperation.GLOBAL_REMOVE);
}
writeSession(LogOperation.GLOBAL_REMOVE, session);
}
@Override
public void addBranchSession(GlobalSession session, BranchSession branchSession) throws TransactionException {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("MANAGER[{}] SESSION[{}] {}", name, branchSession, LogOperation.BRANCH_ADD);
}
writeSession(LogOperation.BRANCH_ADD, branchSession);
}
@Override
public void updateBranchSessionStatus(BranchSession branchSession, BranchStatus status)
throws TransactionException {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("MANAGER[{}] SESSION[{}] {}", name, branchSession, LogOperation.BRANCH_UPDATE);
}
writeSession(LogOperation.BRANCH_UPDATE, branchSession);
}
@Override
public void removeBranchSession(GlobalSession globalSession, BranchSession branchSession)
throws TransactionException {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("MANAGER[{}] SESSION[{}] {}", name, branchSession, LogOperation.BRANCH_REMOVE);
}
writeSession(LogOperation.BRANCH_REMOVE, branchSession);
}
@Override
public void onBegin(GlobalSession globalSession) throws TransactionException {
addGlobalSession(globalSession);
}
@Override
public void onStatusChange(GlobalSession globalSession, GlobalStatus status) throws TransactionException {
updateGlobalSessionStatus(globalSession, status);
}
@Override
public void onBranchStatusChange(GlobalSession globalSession, BranchSession branchSession, BranchStatus status)
throws TransactionException {
updateBranchSessionStatus(branchSession, status);
}
@Override
public void onAddBranch(GlobalSession globalSession, BranchSession branchSession) throws TransactionException {
addBranchSession(globalSession, branchSession);
}
@Override
public void onRemoveBranch(GlobalSession globalSession, BranchSession branchSession) throws TransactionException {
removeBranchSession(globalSession, branchSession);
}
@Override
public void onClose(GlobalSession globalSession) throws TransactionException {
globalSession.setActive(false);
}
@Override
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)) {
throw new GlobalTransactionException(TransactionExceptionCode.FailedWriteSession,
"Fail to store global session");
} else if (LogOperation.GLOBAL_UPDATE.equals(logOperation)) {
throw new GlobalTransactionException(TransactionExceptionCode.FailedWriteSession,
"Fail to update global session");
} else if (LogOperation.GLOBAL_REMOVE.equals(logOperation)) {
throw new GlobalTransactionException(TransactionExceptionCode.FailedWriteSession,
"Fail to remove global session");
} else if (LogOperation.BRANCH_ADD.equals(logOperation)) {
throw new BranchTransactionException(TransactionExceptionCode.FailedWriteSession,
"Fail to store branch session");
} else if (LogOperation.BRANCH_UPDATE.equals(logOperation)) {
throw new BranchTransactionException(TransactionExceptionCode.FailedWriteSession,
"Fail to update branch session");
} else if (LogOperation.BRANCH_REMOVE.equals(logOperation)) {
throw new BranchTransactionException(TransactionExceptionCode.FailedWriteSession,
"Fail to remove branch session");
} else {
throw new BranchTransactionException(TransactionExceptionCode.FailedWriteSession,
"Unknown LogOperation:" + logOperation.name());
}
}
}
@Override
public void destroy() {
}
/**
* Sets transaction store manager.
*
* @param transactionStoreManager the transaction store manager
*/
public void setTransactionStoreManager(TransactionStoreManager transactionStoreManager) {
this.transactionStoreManager = transactionStoreManager;
}
}

View File

@@ -1,473 +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.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 static io.seata.core.model.LockStatus.Locked;
/**
* The type Branch session.
*
* @author sharajava
*/
public class BranchSession implements Lockable, Comparable<BranchSession>, SessionStorable {
private static final Logger LOGGER = LoggerFactory.getLogger(BranchSession.class);
private static final int MAX_BRANCH_SESSION_SIZE = StoreConfig.getMaxBranchSessionSize();
private static ThreadLocal<ByteBuffer> byteBufferThreadLocal = ThreadLocal.withInitial(() -> ByteBuffer.allocate(
MAX_BRANCH_SESSION_SIZE));
private String xid;
private long transactionId;
private long branchId;
private String resourceGroupId;
private String resourceId;
private String lockKey;
private BranchType branchType;
private BranchStatus status = BranchStatus.Unknown;
private String clientId;
private String applicationData;
private LockStatus lockStatus = Locked;
private ConcurrentMap<FileLocker.BucketLockMap, Set<String>> lockHolder
= new ConcurrentHashMap<>();
/**
* Gets application data.
*
* @return the application data
*/
public String getApplicationData() {
return applicationData;
}
/**
* Sets application data.
*
* @param applicationData the application data
*/
public void setApplicationData(String applicationData) {
this.applicationData = applicationData;
}
/**
* Gets resource group id.
*
* @return the resource group id
*/
public String getResourceGroupId() {
return resourceGroupId;
}
/**
* Sets resource group id.
*
* @param resourceGroupId the resource group id
*/
public void setResourceGroupId(String resourceGroupId) {
this.resourceGroupId = resourceGroupId;
}
/**
* Gets client id.
*
* @return the client id
*/
public String getClientId() {
return clientId;
}
/**
* Sets client id.
*
* @param clientId the client id
*/
public void setClientId(String clientId) {
this.clientId = clientId;
}
/**
* Gets resource id.
*
* @return the resource id
*/
public String getResourceId() {
return resourceId;
}
/**
* Sets resource id.
*
* @param resourceId the resource id
*/
public void setResourceId(String resourceId) {
this.resourceId = resourceId;
}
/**
* Gets lock key.
*
* @return the lock key
*/
public String getLockKey() {
return lockKey;
}
/**
* Sets lock key.
*
* @param lockKey the lock key
*/
public void setLockKey(String lockKey) {
this.lockKey = lockKey;
}
/**
* Gets branch type.
*
* @return the branch type
*/
public BranchType getBranchType() {
return branchType;
}
/**
* Sets branch type.
*
* @param branchType the branch type
*/
public void setBranchType(BranchType branchType) {
this.branchType = branchType;
}
/**
* Gets status.
*
* @return the status
*/
public BranchStatus getStatus() {
return status;
}
/**
* Sets status.
*
* @param status the status
*/
public void setStatus(BranchStatus status) {
this.status = status;
}
/**
* Gets transaction id.
*
* @return the transaction id
*/
public long getTransactionId() {
return transactionId;
}
/**
* Sets transaction id.
*
* @param transactionId the transaction id
*/
public void setTransactionId(long transactionId) {
this.transactionId = transactionId;
}
/**
* Gets branch id.
*
* @return the branch id
*/
public long getBranchId() {
return branchId;
}
/**
* Sets branch id.
*
* @param branchId the branch id
*/
public void setBranchId(long branchId) {
this.branchId = branchId;
}
/**
* Gets xid.
*
* @return the xid
*/
public String getXid() {
return xid;
}
/**
* Sets xid.
*
* @param xid the xid
*/
public void setXid(String xid) {
this.xid = xid;
}
@Override
public String toString() {
return "BR:" + branchId + "/" + transactionId;
}
@Override
public int compareTo(BranchSession o) {
return Long.compare(this.branchId, o.branchId);
}
public boolean canBeCommittedAsync() {
return branchType == BranchType.AT || status == BranchStatus.PhaseOne_Failed;
}
/**
* Gets lock holder.
*
* @return the lock holder
*/
public ConcurrentMap<FileLocker.BucketLockMap, Set<String>> getLockHolder() {
return lockHolder;
}
@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, autoCommit, skipCheckLock);
}
return true;
}
@Override
public boolean unlock() throws TransactionException {
if (this.getBranchType() == BranchType.AT) {
return LockerManagerFactory.getLockManager().releaseLock(this);
}
return true;
}
public boolean isAT() {
return this.getBranchType() == BranchType.AT;
}
public LockStatus getLockStatus() {
return lockStatus;
}
public void setLockStatus(LockStatus lockStatus) {
this.lockStatus = lockStatus;
}
@Override
public byte[] encode() {
byte[] resourceIdBytes = resourceId != null ? resourceId.getBytes() : null;
byte[] lockKeyBytes = lockKey != null ? lockKey.getBytes() : null;
byte[] clientIdBytes = clientId != null ? clientId.getBytes() : null;
byte[] applicationDataBytes = applicationData != null ? applicationData.getBytes() : null;
byte[] xidBytes = xid != null ? xid.getBytes() : null;
byte branchTypeByte = branchType != null ? (byte) branchType.ordinal() : -1;
int size = calBranchSessionSize(resourceIdBytes, lockKeyBytes, clientIdBytes, applicationDataBytes, xidBytes);
if (size > MAX_BRANCH_SESSION_SIZE) {
if (lockKeyBytes == null) {
throw new RuntimeException("branch session size exceeded, size : " + size + " maxBranchSessionSize : "
+ MAX_BRANCH_SESSION_SIZE);
}
// try compress lockkey
try {
size -= lockKeyBytes.length;
lockKeyBytes = CompressUtil.compress(lockKeyBytes);
} catch (IOException e) {
LOGGER.error("compress lockKey error", e);
} finally {
size += lockKeyBytes.length;
}
if (size > MAX_BRANCH_SESSION_SIZE) {
throw new RuntimeException(
"compress branch session size exceeded, compressSize : " + size + " maxBranchSessionSize : "
+ MAX_BRANCH_SESSION_SIZE);
}
}
ByteBuffer byteBuffer = byteBufferThreadLocal.get();
//recycle
byteBuffer.clear();
byteBuffer.putLong(transactionId);
byteBuffer.putLong(branchId);
if (resourceIdBytes != null) {
byteBuffer.putInt(resourceIdBytes.length);
byteBuffer.put(resourceIdBytes);
} else {
byteBuffer.putInt(0);
}
if (lockKeyBytes != null) {
byteBuffer.putInt(lockKeyBytes.length);
byteBuffer.put(lockKeyBytes);
} else {
byteBuffer.putInt(0);
}
if (clientIdBytes != null) {
byteBuffer.putShort((short)clientIdBytes.length);
byteBuffer.put(clientIdBytes);
} else {
byteBuffer.putShort((short)0);
}
if (applicationDataBytes != null) {
byteBuffer.putInt(applicationDataBytes.length);
byteBuffer.put(applicationDataBytes);
} else {
byteBuffer.putInt(0);
}
if (xidBytes != null) {
byteBuffer.putInt(xidBytes.length);
byteBuffer.put(xidBytes);
} else {
byteBuffer.putInt(0);
}
byteBuffer.put(branchTypeByte);
byteBuffer.put((byte)status.getCode());
byteBuffer.put((byte)lockStatus.getCode());
byteBuffer.flip();
byte[] result = new byte[byteBuffer.limit()];
byteBuffer.get(result);
return result;
}
private int calBranchSessionSize(byte[] resourceIdBytes, byte[] lockKeyBytes, byte[] clientIdBytes,
byte[] applicationDataBytes, byte[] xidBytes) {
final int size = 8 // trascationId
+ 8 // branchId
+ 4 // resourceIdBytes.length
+ 4 // lockKeyBytes.length
+ 2 // clientIdBytes.length
+ 4 // applicationDataBytes.length
+ 4 // xidBytes.size
+ 1 // statusCode
+ (resourceIdBytes == null ? 0 : resourceIdBytes.length)
+ (lockKeyBytes == null ? 0 : lockKeyBytes.length)
+ (clientIdBytes == null ? 0 : clientIdBytes.length)
+ (applicationDataBytes == null ? 0 : applicationDataBytes.length)
+ (xidBytes == null ? 0 : xidBytes.length)
+ 1; //branchType
return size;
}
@Override
public void decode(byte[] a) {
ByteBuffer byteBuffer = ByteBuffer.wrap(a);
this.transactionId = byteBuffer.getLong();
this.branchId = byteBuffer.getLong();
int resourceLen = byteBuffer.getInt();
if (resourceLen > 0) {
byte[] byResource = new byte[resourceLen];
byteBuffer.get(byResource);
this.resourceId = new String(byResource);
}
int lockKeyLen = byteBuffer.getInt();
if (lockKeyLen > 0) {
byte[] byLockKey = new byte[lockKeyLen];
byteBuffer.get(byLockKey);
if (CompressUtil.isCompressData(byLockKey)) {
try {
this.lockKey = new String(CompressUtil.uncompress(byLockKey));
} catch (IOException e) {
throw new RuntimeException("decompress lockKey error", e);
}
} else {
this.lockKey = new String(byLockKey);
}
}
short clientIdLen = byteBuffer.getShort();
if (clientIdLen > 0) {
byte[] byClientId = new byte[clientIdLen];
byteBuffer.get(byClientId);
this.clientId = new String(byClientId);
}
int applicationDataLen = byteBuffer.getInt();
if (applicationDataLen > 0) {
byte[] byApplicationData = new byte[applicationDataLen];
byteBuffer.get(byApplicationData);
this.applicationData = new String(byApplicationData);
}
int xidLen = byteBuffer.getInt();
if (xidLen > 0) {
byte[] xidBytes = new byte[xidLen];
byteBuffer.get(xidBytes);
this.xid = new String(xidBytes);
}
int branchTypeId = byteBuffer.get();
if (branchTypeId >= 0) {
this.branchType = BranchType.values()[branchTypeId];
}
this.status = BranchStatus.get(byteBuffer.get());
}
}

View File

@@ -1,39 +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.session;
import io.seata.core.exception.TransactionException;
/**
* The Functional Interface Branch session handler
*
* @author wang.liang
* @since 1.5.0
*/
@FunctionalInterface
public interface BranchSessionHandler {
Boolean CONTINUE = null;
/**
* Handle branch session.
*
* @param branchSession the branch session
* @return the handle result
* @throws TransactionException the transaction exception
*/
Boolean handle(BranchSession branchSession) throws TransactionException;
}

View File

@@ -1,771 +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.session;
import io.seata.common.Constants;
import io.seata.common.DefaultValues;
import io.seata.common.XID;
import io.seata.common.util.StringUtils;
import io.seata.config.ConfigurationFactory;
import io.seata.core.constants.ConfigurationKeys;
import io.seata.core.exception.GlobalTransactionException;
import io.seata.core.exception.TransactionException;
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;
import io.seata.server.store.StoreConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.
*
* @author sharajava
*/
public class GlobalSession implements SessionLifecycle, SessionStorable {
private static final Logger LOGGER = LoggerFactory.getLogger(GlobalSession.class);
private static final int MAX_GLOBAL_SESSION_SIZE = StoreConfig.getMaxGlobalSessionSize();
private static ThreadLocal<ByteBuffer> byteBufferThreadLocal = ThreadLocal.withInitial(() -> ByteBuffer.allocate(
MAX_GLOBAL_SESSION_SIZE));
/**
* If the global session's status is (Rollbacking or Committing) and currentTime - createTime >= RETRY_DEAD_THRESHOLD
* then the tx will be remand as need to retry rollback
*/
private static final int RETRY_DEAD_THRESHOLD = ConfigurationFactory.getInstance()
.getInt(ConfigurationKeys.RETRY_DEAD_THRESHOLD, DefaultValues.DEFAULT_RETRY_DEAD_THRESHOLD);
private String xid;
private long transactionId;
private volatile GlobalStatus status;
private String applicationId;
private String transactionServiceGroup;
private String transactionName;
private int timeout;
private long beginTime;
private String applicationData;
private final boolean lazyLoadBranch;
private volatile boolean active = true;
private List<BranchSession> branchSessions;
private GlobalSessionLock globalSessionLock = new GlobalSessionLock();
/**
* Add boolean.
*
* @param branchSession the branch session
* @return the boolean
*/
public boolean add(BranchSession branchSession) {
if (null != branchSessions) {
return branchSessions.add(branchSession);
} else {
// db and redis no need to deal with
return true;
}
}
/**
* Remove boolean.
*
* @param branchSession the branch session
* @return the boolean
*/
public boolean remove(BranchSession branchSession) {
return branchSessions.remove(branchSession);
}
private Set<SessionLifecycleListener> lifecycleListeners = new HashSet<>();
/**
* Can be committed async boolean.
*
* @return the boolean
*/
public boolean canBeCommittedAsync() {
List<BranchSession> branchSessions = getBranchSessions();
for (BranchSession branchSession : branchSessions) {
if (!branchSession.canBeCommittedAsync()) {
return false;
}
}
return true;
}
/**
* Has AT branch
*
* @return the boolean
*/
public boolean hasATBranch() {
List<BranchSession> branchSessions = getBranchSessions();
for (BranchSession branchSession : branchSessions) {
if (branchSession.getBranchType() == BranchType.AT) {
return true;
}
}
return false;
}
/**
* Is saga type transaction
*
* @return is saga
*/
public boolean isSaga() {
List<BranchSession> branchSessions = getBranchSessions();
if (branchSessions.size() > 0) {
return BranchType.SAGA == branchSessions.get(0).getBranchType();
} else {
return StringUtils.isNotBlank(transactionName)
&& transactionName.startsWith(Constants.SAGA_TRANS_NAME_PREFIX);
}
}
/**
* Is timeout boolean.
*
* @return the boolean
*/
public boolean isTimeout() {
return (System.currentTimeMillis() - beginTime) > timeout;
}
/**
* prevent could not handle committing and rollbacking transaction
* @return if true retry commit or roll back
*/
public boolean isDeadSession() {
return (System.currentTimeMillis() - beginTime) > RETRY_DEAD_THRESHOLD;
}
@Override
public void begin() throws TransactionException {
this.status = GlobalStatus.Begin;
this.beginTime = System.currentTimeMillis();
this.active = true;
for (SessionLifecycleListener lifecycleListener : lifecycleListeners) {
lifecycleListener.onBegin(this);
}
}
@Override
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);
}
}
@Override
public void changeBranchStatus(BranchSession branchSession, BranchStatus status)
throws TransactionException {
branchSession.setStatus(status);
for (SessionLifecycleListener lifecycleListener : lifecycleListeners) {
lifecycleListener.onBranchStatusChange(this, branchSession, status);
}
}
@Override
public boolean isActive() {
return active;
}
@Override
public void close() throws TransactionException {
if (active) {
for (SessionLifecycleListener lifecycleListener : lifecycleListeners) {
lifecycleListener.onClose(this);
}
}
}
@Override
public void end() throws TransactionException {
if (GlobalStatus.isTwoPhaseSuccess(status)) {
// Clean locks first
clean();
for (SessionLifecycleListener lifecycleListener : lifecycleListeners) {
lifecycleListener.onSuccessEnd(this);
}
} else {
for (SessionLifecycleListener lifecycleListener : lifecycleListeners) {
lifecycleListener.onFailEnd(this);
}
}
}
public void clean() throws TransactionException {
if (!LockerManagerFactory.getLockManager().releaseGlobalSessionLock(this)) {
throw new TransactionException("UnLock globalSession error, xid = " + this.xid);
}
}
/**
* Close and clean.
*
* @throws TransactionException the transaction exception
*/
public void closeAndClean() throws TransactionException {
close();
if (this.hasATBranch()) {
clean();
}
}
/**
* Add session lifecycle listener.
*
* @param sessionLifecycleListener the session lifecycle listener
*/
public void addSessionLifecycleListener(SessionLifecycleListener sessionLifecycleListener) {
lifecycleListeners.add(sessionLifecycleListener);
}
/**
* Remove session lifecycle listener.
*
* @param sessionLifecycleListener the session lifecycle listener
*/
public void removeSessionLifecycleListener(SessionLifecycleListener sessionLifecycleListener) {
lifecycleListeners.remove(sessionLifecycleListener);
}
@Override
public void addBranch(BranchSession branchSession) throws TransactionException {
for (SessionLifecycleListener lifecycleListener : lifecycleListeners) {
lifecycleListener.onAddBranch(this, branchSession);
}
branchSession.setStatus(BranchStatus.Registered);
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 unlockBranch(BranchSession branchSession) throws TransactionException {
// do not unlock if global status in (Committing, CommitRetrying, AsyncCommitting),
// because it's already unlocked in 'DefaultCore.commit()'
if (status != Committing && status != CommitRetrying && status != AsyncCommitting) {
if (!branchSession.unlock()) {
throw new TransactionException("Unlock branch lock failed, xid = " + this.xid + ", branchId = " + branchSession.getBranchId());
}
}
}
@Override
public void removeBranch(BranchSession branchSession) throws TransactionException {
for (SessionLifecycleListener lifecycleListener : lifecycleListeners) {
lifecycleListener.onRemoveBranch(this, branchSession);
}
remove(branchSession);
}
@Override
public void removeAndUnlockBranch(BranchSession branchSession) throws TransactionException {
unlockBranch(branchSession);
removeBranch(branchSession);
}
/**
* Gets branch.
*
* @param branchId the branch id
* @return the branch
*/
public BranchSession getBranch(long branchId) {
synchronized (this) {
List<BranchSession> branchSessions = getBranchSessions();
for (BranchSession branchSession : branchSessions) {
if (branchSession.getBranchId() == branchId) {
return branchSession;
}
}
return null;
}
}
/**
* Gets sorted branches.
*
* @return the sorted branches
*/
public List<BranchSession> getSortedBranches() {
return new ArrayList<>(getBranchSessions());
}
/**
* Gets reverse sorted branches.
*
* @return the reverse sorted branches
*/
public List<BranchSession> getReverseSortedBranches() {
List<BranchSession> reversed = new ArrayList<>(getBranchSessions());
Collections.reverse(reversed);
return reversed;
}
/**
* 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);
}
/**
* 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
*/
public GlobalSession(String applicationId, String transactionServiceGroup, String transactionName, int timeout) {
this(applicationId, transactionServiceGroup, transactionName, timeout, false);
}
/**
* Gets transaction id.
*
* @return the transaction id
*/
public long getTransactionId() {
return transactionId;
}
/**
* Sets transaction id.
*
* @param transactionId the transaction id
*/
public void setTransactionId(long transactionId) {
this.transactionId = transactionId;
}
/**
* Gets status.
*
* @return the status
*/
public GlobalStatus getStatus() {
return status;
}
/**
* Sets status.
*
* @param status the status
*/
public void setStatus(GlobalStatus status) {
this.status = status;
}
/**
* Gets xid.
*
* @return the xid
*/
public String getXid() {
return xid;
}
/**
* Sets xid.
*
* @param xid the xid
*/
public void setXid(String xid) {
this.xid = xid;
}
/**
* Gets application id.
*
* @return the application id
*/
public String getApplicationId() {
return applicationId;
}
/**
* Gets transaction service group.
*
* @return the transaction service group
*/
public String getTransactionServiceGroup() {
return transactionServiceGroup;
}
/**
* Gets transaction name.
*
* @return the transaction name
*/
public String getTransactionName() {
return transactionName;
}
/**
* Gets timeout.
*
* @return the timeout
*/
public int getTimeout() {
return timeout;
}
/**
* Gets begin time.
*
* @return the begin time
*/
public long getBeginTime() {
return beginTime;
}
/**
* Sets begin time.
*
* @param beginTime the begin time
*/
public void setBeginTime(long beginTime) {
this.beginTime = beginTime;
}
/**
* Gets application data.
*
* @return the application data
*/
public String getApplicationData() {
return applicationData;
}
/**
* Sets application data.
*
* @param applicationData the application data
*/
public void setApplicationData(String applicationData) {
this.applicationData = applicationData;
}
public boolean isLazyLoadBranch() {
return lazyLoadBranch;
}
/**
* Create global session global session.
*
* @param applicationId the application id
* @param txServiceGroup the tx service group
* @param txName the tx name
* @param timeout the timeout
* @return the global session
*/
public static GlobalSession createGlobalSession(String applicationId, String txServiceGroup, String txName,
int timeout) {
GlobalSession session = new GlobalSession(applicationId, txServiceGroup, txName, timeout, false);
return session;
}
/**
* Sets active.
*
* @param active the active
*/
public void setActive(boolean active) {
this.active = active;
}
@Override
public byte[] encode() {
byte[] byApplicationIdBytes = applicationId != null ? applicationId.getBytes() : null;
byte[] byServiceGroupBytes = transactionServiceGroup != null ? transactionServiceGroup.getBytes() : null;
byte[] byTxNameBytes = transactionName != null ? transactionName.getBytes() : null;
byte[] xidBytes = xid != null ? xid.getBytes() : null;
byte[] applicationDataBytes = applicationData != null ? applicationData.getBytes() : null;
int size = calGlobalSessionSize(byApplicationIdBytes, byServiceGroupBytes, byTxNameBytes, xidBytes,
applicationDataBytes);
if (size > MAX_GLOBAL_SESSION_SIZE) {
throw new RuntimeException("global session size exceeded, size : " + size + " maxBranchSessionSize : " +
MAX_GLOBAL_SESSION_SIZE);
}
ByteBuffer byteBuffer = byteBufferThreadLocal.get();
//recycle
byteBuffer.clear();
byteBuffer.putLong(transactionId);
byteBuffer.putInt(timeout);
if (byApplicationIdBytes != null) {
byteBuffer.putShort((short)byApplicationIdBytes.length);
byteBuffer.put(byApplicationIdBytes);
} else {
byteBuffer.putShort((short)0);
}
if (byServiceGroupBytes != null) {
byteBuffer.putShort((short)byServiceGroupBytes.length);
byteBuffer.put(byServiceGroupBytes);
} else {
byteBuffer.putShort((short)0);
}
if (byTxNameBytes != null) {
byteBuffer.putShort((short)byTxNameBytes.length);
byteBuffer.put(byTxNameBytes);
} else {
byteBuffer.putShort((short)0);
}
if (xidBytes != null) {
byteBuffer.putInt(xidBytes.length);
byteBuffer.put(xidBytes);
} else {
byteBuffer.putInt(0);
}
if (applicationDataBytes != null) {
byteBuffer.putInt(applicationDataBytes.length);
byteBuffer.put(applicationDataBytes);
} else {
byteBuffer.putInt(0);
}
byteBuffer.putLong(beginTime);
byteBuffer.put((byte)status.getCode());
byteBuffer.flip();
byte[] result = new byte[byteBuffer.limit()];
byteBuffer.get(result);
return result;
}
private int calGlobalSessionSize(byte[] byApplicationIdBytes, byte[] byServiceGroupBytes, byte[] byTxNameBytes,
byte[] xidBytes, byte[] applicationDataBytes) {
final int size = 8 // transactionId
+ 4 // timeout
+ 2 // byApplicationIdBytes.length
+ 2 // byServiceGroupBytes.length
+ 2 // byTxNameBytes.length
+ 4 // xidBytes.length
+ 4 // applicationDataBytes.length
+ 8 // beginTime
+ 1 // statusCode
+ (byApplicationIdBytes == null ? 0 : byApplicationIdBytes.length)
+ (byServiceGroupBytes == null ? 0 : byServiceGroupBytes.length)
+ (byTxNameBytes == null ? 0 : byTxNameBytes.length)
+ (xidBytes == null ? 0 : xidBytes.length)
+ (applicationDataBytes == null ? 0 : applicationDataBytes.length);
return size;
}
@Override
public void decode(byte[] a) {
this.branchSessions = new ArrayList<>();
ByteBuffer byteBuffer = ByteBuffer.wrap(a);
this.transactionId = byteBuffer.getLong();
this.timeout = byteBuffer.getInt();
short applicationIdLen = byteBuffer.getShort();
if (applicationIdLen > 0) {
byte[] byApplicationId = new byte[applicationIdLen];
byteBuffer.get(byApplicationId);
this.applicationId = new String(byApplicationId);
}
short serviceGroupLen = byteBuffer.getShort();
if (serviceGroupLen > 0) {
byte[] byServiceGroup = new byte[serviceGroupLen];
byteBuffer.get(byServiceGroup);
this.transactionServiceGroup = new String(byServiceGroup);
}
short txNameLen = byteBuffer.getShort();
if (txNameLen > 0) {
byte[] byTxName = new byte[txNameLen];
byteBuffer.get(byTxName);
this.transactionName = new String(byTxName);
}
int xidLen = byteBuffer.getInt();
if (xidLen > 0) {
byte[] xidBytes = new byte[xidLen];
byteBuffer.get(xidBytes);
this.xid = new String(xidBytes);
}
int applicationDataLen = byteBuffer.getInt();
if (applicationDataLen > 0) {
byte[] applicationDataLenBytes = new byte[applicationDataLen];
byteBuffer.get(applicationDataLenBytes);
this.applicationData = new String(applicationDataLenBytes);
}
this.beginTime = byteBuffer.getLong();
this.status = GlobalStatus.get(byteBuffer.get());
}
/**
* Has branch boolean.
*
* @return the boolean
*/
public boolean hasBranch() {
return getBranchSessions().size() > 0;
}
public void lock() throws TransactionException {
globalSessionLock.lock();
}
public void unlock() {
globalSessionLock.unlock();
}
private static class GlobalSessionLock {
private Lock globalSessionLock = new ReentrantLock();
private static final int GLOBAL_SESSION_LOCK_TIME_OUT_MILLS = 2 * 1000;
public void lock() throws TransactionException {
try {
if (globalSessionLock.tryLock(GLOBAL_SESSION_LOCK_TIME_OUT_MILLS, TimeUnit.MILLISECONDS)) {
return;
}
} catch (InterruptedException e) {
LOGGER.error("Interrupted error", e);
}
throw new GlobalTransactionException(TransactionExceptionCode.FailedLockGlobalTranscation, "Lock global session failed");
}
public void unlock() {
globalSessionLock.unlock();
}
}
@FunctionalInterface
public interface LockRunnable {
void run() throws TransactionException;
}
@FunctionalInterface
public interface LockCallable<V> {
V call() throws TransactionException;
}
public List<BranchSession> getBranchSessions() {
loadBranchs();
return branchSessions;
}
public void asyncCommit() throws TransactionException {
this.addSessionLifecycleListener(SessionHolder.getAsyncCommittingSessionManager());
this.setStatus(GlobalStatus.AsyncCommitting);
SessionHolder.getAsyncCommittingSessionManager().addGlobalSession(this);
}
public void queueToRetryCommit() throws TransactionException {
this.addSessionLifecycleListener(SessionHolder.getRetryCommittingSessionManager());
this.setStatus(GlobalStatus.CommitRetrying);
SessionHolder.getRetryCommittingSessionManager().addGlobalSession(this);
}
public void queueToRetryRollback() throws TransactionException {
this.addSessionLifecycleListener(SessionHolder.getRetryRollbackingSessionManager());
GlobalStatus currentStatus = this.getStatus();
if (SessionStatusValidator.isTimeoutGlobalStatus(currentStatus)) {
this.setStatus(GlobalStatus.TimeoutRollbackRetrying);
} else {
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 + '}';
}
}

View File

@@ -1,36 +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.session;
import io.seata.core.exception.TransactionException;
/**
* The Functional Interface Global session handler
*
* @author wang.liang
* @since 1.5.0
*/
@FunctionalInterface
public interface GlobalSessionHandler {
/**
* Handle global session.
*
* @param globalSession the global session
* @throws TransactionException the transaction exception
*/
void handle(GlobalSession globalSession) throws TransactionException;
}

View File

@@ -1,42 +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.session;
import io.seata.core.exception.TransactionException;
/**
* The interface Lockable.
*
* @author sharajava
*/
public interface Lockable {
/**
* Lock boolean.
*
* @return the boolean
* @throws TransactionException the transaction exception
*/
boolean lock() throws TransactionException;
/**
* Unlock boolean.
*
* @return the boolean
* @throws TransactionException the transaction exception
*/
boolean unlock() throws TransactionException;
}

View File

@@ -1,29 +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.session;
/**
* Service contains states which can be reloaded.
*
* @author sharajava
*/
public interface Reloadable {
/**
* Reload states.
*/
void reload();
}

View File

@@ -1,145 +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.session;
import io.seata.core.model.GlobalStatus;
/**
* The type Session condition.
*
* @author slievrly
*/
public class SessionCondition {
private Long transactionId;
private String xid;
private GlobalStatus status;
private GlobalStatus[] statuses;
private Long overTimeAliveMills;
private boolean lazyLoadBranch;
/**
* Instantiates a new Session condition.
*/
public SessionCondition() {
}
/**
* Instantiates a new Session condition.
*
* @param xid the xid
*/
public SessionCondition(String xid) {
this.xid = xid;
}
/**
* Instantiates a new Session condition.
*
* @param status the status
*/
public SessionCondition(GlobalStatus status) {
this.status = status;
this.statuses = new GlobalStatus[] {status};
}
/**
* Instantiates a new Session condition.
*
* @param statuses the statuses
*/
public SessionCondition(GlobalStatus... statuses) {
this.statuses = statuses;
}
/**
* Instantiates a new Session condition.
*
* @param overTimeAliveMills the over time alive mills
*/
public SessionCondition(long overTimeAliveMills) {
this.overTimeAliveMills = overTimeAliveMills;
}
/**
* Gets status.
*
* @return the status
*/
public GlobalStatus getStatus() {
return status;
}
/**
* Sets status.
*
* @param status the status
*/
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() {
return overTimeAliveMills;
}
/**
* Sets over time alive mills.
*
* @param overTimeAliveMills the over time alive mills
*/
public void setOverTimeAliveMills(Long overTimeAliveMills) {
this.overTimeAliveMills = overTimeAliveMills;
}
public Long getTransactionId() {
return transactionId;
}
public void setTransactionId(Long transactionId) {
this.transactionId = transactionId;
}
public String getXid() {
return xid;
}
public void setXid(String xid) {
this.xid = xid;
}
public GlobalStatus[] getStatuses() {
return statuses;
}
public void setStatuses(GlobalStatus... statuses) {
this.statuses = statuses;
}
public boolean isLazyLoadBranch() {
return lazyLoadBranch;
}
public void setLazyLoadBranch(boolean lazyLoadBranch) {
this.lazyLoadBranch = lazyLoadBranch;
}
}

View File

@@ -1,346 +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.session;
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.
*
* @author sharajava
*/
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() {
}
public static BranchSession newBranchByGlobal(GlobalSession globalSession, BranchType branchType, String resourceId, String lockKeys, String clientId) {
return newBranchByGlobal(globalSession, branchType, resourceId, null, lockKeys, clientId);
}
/**
* New branch by global branch session.
*
* @param globalSession the global session
* @param branchType the branch type
* @param resourceId the resource id
* @param lockKeys the lock keys
* @param clientId the client id
* @return the branch session
*/
public static BranchSession newBranchByGlobal(GlobalSession globalSession, BranchType branchType, String resourceId,
String applicationData, String lockKeys, String clientId) {
BranchSession branchSession = new BranchSession();
branchSession.setXid(globalSession.getXid());
branchSession.setTransactionId(globalSession.getTransactionId());
branchSession.setBranchId(UUIDGenerator.generateUUID());
branchSession.setBranchType(branchType);
branchSession.setResourceId(resourceId);
branchSession.setLockKey(lockKeys);
branchSession.setClientId(clientId);
branchSession.setApplicationData(applicationData);
return branchSession;
}
/**
* New branch
*
* @param branchType the branch type
* @param xid Transaction id.
* @param branchId Branch id.
* @param resourceId Resource id.
* @param applicationData Application data bind with this branch.
* @return the branch session
*/
public static BranchSession newBranch(BranchType branchType, String xid, long branchId, String resourceId, String applicationData) {
BranchSession branchSession = new BranchSession();
branchSession.setXid(xid);
branchSession.setBranchId(branchId);
branchSession.setBranchType(branchType);
branchSession.setResourceId(resourceId);
branchSession.setApplicationData(applicationData);
return branchSession;
}
/**
* End committed.
*
* @param globalSession the global session
* @param retryGlobal the retry global
* @throws TransactionException the transaction exception
*/
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 {
if (globalSession.isSaga()) {
globalSession.setStatus(GlobalStatus.Committed);
globalSession.end();
}
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, 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, 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 {
if (globalSession.isSaga()) {
globalSession.setStatus(GlobalStatus.Rollbacked);
globalSession.end();
}
MetricsPublisher.postSessionDoneEvent(globalSession, GlobalStatus.Rollbacked, false, false);
}
}
/**
* End rollback failed.
*
* @param globalSession the global session
* @param retryGlobal the retry global
* @throws TransactionException the transaction exception
*/
public static void endRollbackFailed(GlobalSession globalSession, boolean retryGlobal) throws TransactionException {
endRollbackFailed(globalSession, retryGlobal, false);
}
/**
* 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);
}
/**
* Foreach global sessions.
*
* @param sessions the global sessions
* @param handler the handler
* @since 1.5.0
*/
public static void forEach(Collection<GlobalSession> sessions, GlobalSessionHandler handler) {
if (CollectionUtils.isEmpty(sessions)) {
return;
}
sessions.parallelStream().forEach(globalSession -> {
try {
MDC.put(RootContext.MDC_KEY_XID, globalSession.getXid());
handler.handle(globalSession);
} catch (Throwable th) {
LOGGER.error("handle global session failed: {}", globalSession.getXid(), th);
} finally {
MDC.remove(RootContext.MDC_KEY_XID);
}
});
}
/**
* Foreach branch sessions.
*
* @param sessions the branch session
* @param handler the handler
* @since 1.5.0
*/
public static Boolean forEach(Collection<BranchSession> sessions, BranchSessionHandler handler) throws TransactionException {
Boolean result;
for (BranchSession branchSession : sessions) {
try {
MDC.put(RootContext.MDC_KEY_BRANCH_ID, String.valueOf(branchSession.getBranchId()));
result = handler.handle(branchSession);
if (result == null) {
continue;
}
return result;
} finally {
MDC.remove(RootContext.MDC_KEY_BRANCH_ID);
}
}
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 {
globalSession.unlockBranch(branchSession);
if (isEnableBranchRemoveAsync() && 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;
}
boolean isAsyncRemove = isEnableBranchRemoveAsync() && isAsync;
for (BranchSession branchSession : branchSessions) {
if (isAsyncRemove) {
globalSession.unlockBranch(branchSession);
} else {
globalSession.removeAndUnlockBranch(branchSession);
}
}
if (isAsyncRemove) {
COORDINATOR.doBranchRemoveAllAsync(globalSession);
}
}
/**
* if true, enable delete the branch asynchronously
*
* @return the boolean
*/
private static boolean isEnableBranchRemoveAsync() {
return Objects.equals(Boolean.TRUE, DELAY_HANDLE_SESSION)
&& Objects.equals(Boolean.TRUE, ENABLE_BRANCH_ASYNC_REMOVE);
}
}

View File

@@ -1,426 +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.session;
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;
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.exception.TransactionException;
import io.seata.core.model.GlobalStatus;
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 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.
*
* @author sharajava
*/
public class SessionHolder {
private static final Logger LOGGER = LoggerFactory.getLogger(SessionHolder.class);
/**
* The constant CONFIG.
*/
protected static final Configuration CONFIG = ConfigurationFactory.getInstance();
/**
* The constant ROOT_SESSION_MANAGER_NAME.
*/
public static final String ROOT_SESSION_MANAGER_NAME = "root.data";
/**
* The constant ASYNC_COMMITTING_SESSION_MANAGER_NAME.
*/
public static final String ASYNC_COMMITTING_SESSION_MANAGER_NAME = "async.commit.data";
/**
* The constant RETRY_COMMITTING_SESSION_MANAGER_NAME.
*/
public static final String RETRY_COMMITTING_SESSION_MANAGER_NAME = "retry.commit.data";
/**
* The constant RETRY_ROLLBACKING_SESSION_MANAGER_NAME.
*/
public static final String RETRY_ROLLBACKING_SESSION_MANAGER_NAME = "retry.rollback.data";
/**
* The default session store dir
*/
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;
private static SessionManager RETRY_COMMITTING_SESSION_MANAGER;
private static SessionManager RETRY_ROLLBACKING_SESSION_MANAGER;
private static DistributedLocker DISTRIBUTED_LOCKER;
public static void init() {
init(null);
}
/**
* Init.
*
* @param sessionMode the store mode: file, db, redis
* @throws IOException the io exception
*/
public static void init(SessionMode sessionMode) {
if (null == sessionMode) {
sessionMode = StoreConfig.getSessionMode();
}
LOGGER.info("use session store mode: {}", sessionMode.getName());
if (SessionMode.DB.equals(sessionMode)) {
ROOT_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, SessionMode.DB.getName());
ASYNC_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, SessionMode.DB.getName(),
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 (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, SessionMode.FILE.getName(),
new Object[]{ROOT_SESSION_MANAGER_NAME, sessionStorePath});
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 (SessionMode.REDIS.equals(sessionMode)) {
ROOT_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, SessionMode.REDIS.getName());
ASYNC_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class,
SessionMode.REDIS.getName(), new Object[]{ASYNC_COMMITTING_SESSION_MANAGER_NAME});
RETRY_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class,
SessionMode.REDIS.getName(), new Object[]{RETRY_COMMITTING_SESSION_MANAGER_NAME});
RETRY_ROLLBACKING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class,
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:" + sessionMode.getName());
}
reload(sessionMode);
}
//region reload
/**
* Reload.
*
* @param sessionMode the mode of store
*/
protected static void reload(SessionMode sessionMode) {
if (ROOT_SESSION_MANAGER instanceof Reloadable) {
((Reloadable) ROOT_SESSION_MANAGER).reload();
}
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;
case Committing:
case CommitRetrying:
queueToRetryCommit(globalSession);
break;
default: {
lockBranchSessions(globalSession.getSortedBranches());
switch (globalStatus) {
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;
}
}
}
}
} 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);
}
});
}
}
private static void removeInErrorState(GlobalSession globalSession) {
try {
LOGGER.warn("The global session should NOT be {}, remove it. xid = {}", globalSession.getStatus(), globalSession.getXid());
ROOT_SESSION_MANAGER.removeGlobalSession(globalSession);
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Remove global session succeed, xid = {}, status = {}", globalSession.getXid(), globalSession.getStatus());
}
} catch (Exception e) {
LOGGER.error("Remove global session failed, xid = {}, status = {}", globalSession.getXid(), globalSession.getStatus(), e);
}
}
private static void queueToAsyncCommitting(GlobalSession globalSession) {
try {
globalSession.addSessionLifecycleListener(getAsyncCommittingSessionManager());
getAsyncCommittingSessionManager().addGlobalSession(globalSession);
} catch (TransactionException e) {
throw new ShouldNeverHappenException(e);
}
}
private static void lockBranchSessions(List<BranchSession> branchSessions) {
branchSessions.forEach(branchSession -> {
try {
branchSession.lock();
} catch (TransactionException e) {
throw new ShouldNeverHappenException(e);
}
});
}
private static void queueToRetryCommit(GlobalSession globalSession) {
try {
globalSession.addSessionLifecycleListener(getRetryCommittingSessionManager());
getRetryCommittingSessionManager().addGlobalSession(globalSession);
} catch (TransactionException e) {
throw new ShouldNeverHappenException(e);
}
}
private static void queueToRetryRollback(GlobalSession globalSession) {
try {
globalSession.addSessionLifecycleListener(getRetryRollbackingSessionManager());
getRetryRollbackingSessionManager().addGlobalSession(globalSession);
} catch (TransactionException e) {
throw new ShouldNeverHappenException(e);
}
}
//endregion
//region get session manager
/**
* Gets root session manager.
*
* @return the root session manager
*/
public static SessionManager getRootSessionManager() {
if (ROOT_SESSION_MANAGER == null) {
throw new ShouldNeverHappenException("SessionManager is NOT init!");
}
return ROOT_SESSION_MANAGER;
}
/**
* 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!");
}
return ASYNC_COMMITTING_SESSION_MANAGER;
}
/**
* 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!");
}
return RETRY_COMMITTING_SESSION_MANAGER;
}
/**
* 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!");
}
return RETRY_ROLLBACKING_SESSION_MANAGER;
}
//endregion
/**
* Find global session.
*
* @param xid the xid
* @return the global session
*/
public static GlobalSession findGlobalSession(String xid) {
return findGlobalSession(xid, true);
}
/**
* Find global session.
*
* @param xid the xid
* @param withBranchSessions the withBranchSessions
* @return the global session
*/
public static GlobalSession findGlobalSession(String xid, boolean withBranchSessions) {
return getRootSessionManager().findGlobalSession(xid, withBranchSessions);
}
/**
* lock and execute
*
* @param globalSession the global session
* @param lockCallable the lock Callable
* @return the value
*/
public static <T> T lockAndExecute(GlobalSession globalSession, GlobalSession.LockCallable<T> lockCallable)
throws TransactionException {
return getRootSessionManager().lockAndExecute(globalSession, lockCallable);
}
/**
* acquire lock
*
* @param lockKey the lock key, should be distinct for each lock
* @return the boolean
*/
public static boolean acquireDistributedLock(String lockKey) {
return DISTRIBUTED_LOCKER.acquireLock(new DistributedLockDO(lockKey, XID.getIpAddressAndPort(), DISTRIBUTED_LOCK_EXPIRE_TIME));
}
/**
* release lock
*
* @return the boolean
*/
public static boolean releaseDistributedLock(String lockKey) {
return DISTRIBUTED_LOCKER.releaseLock(new DistributedLockDO(lockKey, XID.getIpAddressAndPort(), DISTRIBUTED_LOCK_EXPIRE_TIME));
}
/**
* 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 distributedLockAndExecute(String key, NoArgsFunc func) {
boolean lock = false;
try {
if (lock = acquireDistributedLock(key)) {
func.call();
}
} catch (Exception e) {
LOGGER.error("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();
}
}
@FunctionalInterface
public interface NoArgsFunc {
void call();
}
}

View File

@@ -1,105 +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.session;
import io.seata.core.exception.TransactionException;
import io.seata.core.model.BranchStatus;
import io.seata.core.model.GlobalStatus;
/**
* The interface Session lifecycle.
*
* @author sharajava
*/
public interface SessionLifecycle {
/**
* Begin.
*
* @throws TransactionException the transaction exception
*/
void begin() throws TransactionException;
/**
* Change status.
*
* @param status the status
* @throws TransactionException the transaction exception
*/
void changeGlobalStatus(GlobalStatus status) throws TransactionException;
/**
* Change branch status.
*
* @param branchSession the branch session
* @param status the status
* @throws TransactionException the transaction exception
*/
void changeBranchStatus(BranchSession branchSession, BranchStatus status) throws TransactionException;
/**
* Add branch.
*
* @param branchSession the branch session
* @throws TransactionException the transaction exception
*/
void addBranch(BranchSession branchSession) throws TransactionException;
/**
* Release the lock of branch.
*
* @param branchSession the branch session
* @throws TransactionException the transaction exception
*/
void unlockBranch(BranchSession branchSession) throws TransactionException;
/**
* Remove branch.
*
* @param branchSession the branch session
* @throws TransactionException the transaction exception
*/
void removeBranch(BranchSession branchSession) throws TransactionException;
/**
* Remove branch and release the lock of branch.
*
* @param branchSession the branchSession
* @throws TransactionException the TransactionException
*/
void removeAndUnlockBranch(BranchSession branchSession) throws TransactionException;
/**
* Is active boolean.
*
* @return the boolean
*/
boolean isActive();
/**
* Close.
*
* @throws TransactionException the transaction exception
*/
void close() throws TransactionException;
/**
* end.
*
* @throws TransactionException the transaction exception
*/
void end() throws TransactionException;
}

View File

@@ -1,98 +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.session;
import io.seata.core.exception.TransactionException;
import io.seata.core.model.BranchStatus;
import io.seata.core.model.GlobalStatus;
/**
* The interface Session lifecycle listener.
*
* @author sharajava
*/
public interface SessionLifecycleListener {
/**
* On begin.
*
* @param globalSession the global session
* @throws TransactionException the transaction exception
*/
void onBegin(GlobalSession globalSession) throws TransactionException;
/**
* On status change.
*
* @param globalSession the global session
* @param status the status
* @throws TransactionException the transaction exception
*/
void onStatusChange(GlobalSession globalSession, GlobalStatus status) throws TransactionException;
/**
* On branch status change.
*
* @param globalSession the global session
* @param branchSession the branch session
* @param status the status
* @throws TransactionException the transaction exception
*/
void onBranchStatusChange(GlobalSession globalSession, BranchSession branchSession, BranchStatus status)
throws TransactionException;
/**
* On add branch.
*
* @param globalSession the global session
* @param branchSession the branch session
* @throws TransactionException the transaction exception
*/
void onAddBranch(GlobalSession globalSession, BranchSession branchSession) throws TransactionException;
/**
* On remove branch.
*
* @param globalSession the global session
* @param branchSession the branch session
* @throws TransactionException the transaction exception
*/
void onRemoveBranch(GlobalSession globalSession, BranchSession branchSession) throws TransactionException;
/**
* On close.
*
* @param globalSession the global session
* @throws TransactionException the transaction exception
*/
void onClose(GlobalSession globalSession) throws TransactionException;
/**
* On end.
*
* @param globalSession the global session
* @throws TransactionException the transaction exception
*/
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;
}

View File

@@ -1,127 +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.session;
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.
*
* @author sharajava
*/
public interface SessionManager extends SessionLifecycleListener, Disposable {
/**
* Add global session.
*
* @param session the session
* @throws TransactionException the transaction exception
*/
void addGlobalSession(GlobalSession session) throws TransactionException;
/**
* Find global session global session.
*
* @param xid the xid
* @return the global session
*/
GlobalSession findGlobalSession(String xid) ;
/**
* Find global session global session.
*
* @param xid the xid
* @param withBranchSessions the withBranchSessions
* @return the global session
*/
GlobalSession findGlobalSession(String xid, boolean withBranchSessions);
/**
* Update global session status.
*
* @param session the session
* @param status the status
* @throws TransactionException the transaction exception
*/
void updateGlobalSessionStatus(GlobalSession session, GlobalStatus status) throws TransactionException;
/**
* Remove global session.
*
* @param session the session
* @throws TransactionException the transaction exception
*/
void removeGlobalSession(GlobalSession session) throws TransactionException;
/**
* Add branch session.
*
* @param globalSession the global session
* @param session the session
* @throws TransactionException the transaction exception
*/
void addBranchSession(GlobalSession globalSession, BranchSession session) throws TransactionException;
/**
* Update branch session status.
*
* @param session the session
* @param status the status
* @throws TransactionException the transaction exception
*/
void updateBranchSessionStatus(BranchSession session, BranchStatus status) throws TransactionException;
/**
* Remove branch session.
*
* @param globalSession the global session
* @param session the session
* @throws TransactionException the transaction exception
*/
void removeBranchSession(GlobalSession globalSession, BranchSession session) throws TransactionException;
/**
* All sessions collection.
*
* @return the collection
*/
Collection<GlobalSession> allSessions();
/**
* Find global sessions list.
*
* @param condition the condition
* @return the list
*/
List<GlobalSession> findGlobalSessions(SessionCondition condition);
/**
* lock and execute
*
* @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;
}

View File

@@ -1,87 +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.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;
}
}

View File

@@ -1,70 +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.spring.listener;
import io.seata.common.util.CollectionUtils;
import io.seata.common.util.StringUtils;
import io.seata.config.ConfigurationFactory;
import io.seata.config.FileConfiguration;
import io.seata.config.file.FileConfig;
import io.seata.server.store.StoreConfig;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertiesPropertySource;
import java.util.*;
import static io.seata.common.ConfigurationKeys.*;
@Order(Ordered.HIGHEST_PRECEDENCE)
public class SeataPropertiesLoader implements ApplicationContextInitializer<ConfigurableApplicationContext> {
List<String> prefixList = Arrays.asList(FILE_ROOT_PREFIX_CONFIG, FILE_ROOT_PREFIX_REGISTRY, SERVER_PREFIX,
STORE_PREFIX, METRICS_PREFIX, TRANSPORT_PREFIX);
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
ConfigurableEnvironment environment = applicationContext.getEnvironment();
FileConfiguration configuration = ConfigurationFactory.getOriginFileInstanceRegistry();
FileConfig fileConfig = configuration.getFileConfig();
Map<String, Object> configs = fileConfig.getAllConfig();
if (CollectionUtils.isNotEmpty(configs)) {
Optional<FileConfiguration> originFileInstance = ConfigurationFactory.getOriginFileInstance();
originFileInstance
.ifPresent(fileConfiguration -> configs.putAll(fileConfiguration.getFileConfig().getAllConfig()));
Properties properties = new Properties();
configs.forEach((k, v) -> {
if (v instanceof String) {
if (StringUtils.isEmpty((String)v)) {
return;
}
}
// Convert the configuration name to the configuration name under Spring Boot
if (prefixList.stream().anyMatch(k::startsWith)) {
properties.put(SEATA_FILE_PREFIX_ROOT_CONFIG + k, v);
}
});
environment.getPropertySources().addLast(new PropertiesPropertySource("seataOldConfig", properties));
}
// Load by priority
System.setProperty("sessionMode", StoreConfig.getSessionMode().getName());
System.setProperty("lockMode", StoreConfig.getLockMode().getName());
}
}

View File

@@ -1,124 +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.spring.listener;
import io.seata.common.holder.ObjectHolder;
import io.seata.common.util.StringUtils;
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();
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;
}
}

View File

@@ -1,207 +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.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, boolean lazyLoadBranch) {
if (globalTransactionDO == null) {
return null;
}
GlobalSession session = new GlobalSession(globalTransactionDO.getApplicationId(),
globalTransactionDO.getTransactionServiceGroup(),
globalTransactionDO.getTransactionName(),
globalTransactionDO.getTimeout(), lazyLoadBranch);
session.setXid(globalTransactionDO.getXid());
session.setTransactionId(globalTransactionDO.getTransactionId());
session.setStatus(GlobalStatus.get(globalTransactionDO.getStatus()));
session.setApplicationData(globalTransactionDO.getApplicationData());
session.setBeginTime(globalTransactionDO.getBeginTime());
return session;
}
public static GlobalSession convertGlobalSession(GlobalTransactionDO globalTransactionDO) {
return convertGlobalSession(globalTransactionDO, false);
}
public static BranchSession convertBranchSession(BranchTransactionDO branchTransactionDO) {
if (branchTransactionDO == null) {
return null;
}
BranchSession branchSession = new BranchSession();
branchSession.setXid(branchTransactionDO.getXid());
branchSession.setTransactionId(branchTransactionDO.getTransactionId());
branchSession.setApplicationData(branchTransactionDO.getApplicationData());
branchSession.setBranchId(branchTransactionDO.getBranchId());
branchSession.setBranchType(BranchType.valueOf(branchTransactionDO.getBranchType()));
branchSession.setResourceId(branchTransactionDO.getResourceId());
branchSession.setClientId(branchTransactionDO.getClientId());
branchSession.setResourceGroupId(branchTransactionDO.getResourceGroupId());
branchSession.setStatus(BranchStatus.get(branchTransactionDO.getStatus()));
return branchSession;
}
public static GlobalTransactionDO convertGlobalTransactionDO(SessionStorable session) {
if (session == null || !(session instanceof GlobalSession)) {
throw new IllegalArgumentException(
"The parameter of SessionStorable is not available, SessionStorable:" + StringUtils.toString(session));
}
GlobalSession globalSession = (GlobalSession)session;
GlobalTransactionDO globalTransactionDO = new GlobalTransactionDO();
globalTransactionDO.setXid(globalSession.getXid());
globalTransactionDO.setStatus(globalSession.getStatus().getCode());
globalTransactionDO.setApplicationId(globalSession.getApplicationId());
globalTransactionDO.setBeginTime(globalSession.getBeginTime());
globalTransactionDO.setTimeout(globalSession.getTimeout());
globalTransactionDO.setTransactionId(globalSession.getTransactionId());
globalTransactionDO.setTransactionName(globalSession.getTransactionName());
globalTransactionDO.setTransactionServiceGroup(globalSession.getTransactionServiceGroup());
globalTransactionDO.setApplicationData(globalSession.getApplicationData());
return globalTransactionDO;
}
public static BranchTransactionDO convertBranchTransactionDO(SessionStorable session) {
if (session == null || !(session instanceof BranchSession)) {
throw new IllegalArgumentException(
"The parameter of SessionStorable is not available, SessionStorable:" + StringUtils.toString(session));
}
BranchSession branchSession = (BranchSession)session;
BranchTransactionDO branchTransactionDO = new BranchTransactionDO();
branchTransactionDO.setXid(branchSession.getXid());
branchTransactionDO.setBranchId(branchSession.getBranchId());
branchTransactionDO.setBranchType(branchSession.getBranchType().name());
branchTransactionDO.setClientId(branchSession.getClientId());
branchTransactionDO.setResourceGroupId(branchSession.getResourceGroupId());
branchTransactionDO.setTransactionId(branchSession.getTransactionId());
branchTransactionDO.setApplicationData(branchSession.getApplicationData());
branchTransactionDO.setResourceId(branchSession.getResourceId());
branchTransactionDO.setStatus(branchSession.getStatus().getCode());
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;
}
}

View File

@@ -1,283 +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.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.HashSet;
import java.util.Objects;
import java.util.Set;
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;
private static final String LOCK_WAIT_TIMEOUT_MYSQL_MESSAGE = "try restarting transaction";
private static final int LOCK_WAIT_TIMEOUT_MYSQL_CODE = 1205;
private static final Set<Integer> IGNORE_MYSQL_CODE = new HashSet<>();
private static final Set<String> IGNORE_MYSQL_MESSAGE = new HashSet<>();
static {
IGNORE_MYSQL_CODE.add(LOCK_WAIT_TIMEOUT_MYSQL_CODE);
IGNORE_MYSQL_MESSAGE.add(LOCK_WAIT_TIMEOUT_MYSQL_MESSAGE);
}
/**
* whether the distribute lock demotion
* using for 1.5.0 only and will remove in 1.6.0
*/
@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 lockFromDB = getDistributedLockDO(connection, distributedLockDO.getLockKey());
if (null == lockFromDB) {
boolean ret = insertDistribute(connection, distributedLockDO);
connection.commit();
return ret;
}
if (lockFromDB.getExpireTime() >= System.currentTimeMillis()) {
LOGGER.debug("the distribute lock for key :{} is holding by :{}, acquire lock failure.",
distributedLockDO.getLockKey(), lockFromDB.getLockValue());
connection.commit();
return false;
}
boolean ret = updateDistributedLock(connection, distributedLockDO);
connection.commit();
return ret;
} catch (SQLException ex) {
// ignore "Lock wait timeout exceeded; try restarting transaction"
// TODO: need nowait adaptation
if (!ignoreSQLException(ex)) {
LOGGER.error("execute acquire lock failure, key is: {}", distributedLockDO.getLockKey(), ex);
}
try {
if (connection != null) {
connection.rollback();
}
} 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())) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("the distribute lock for key :{} is holding by :{}, skip the release lock.",
distributedLockDO.getLockKey(), distributedLockDOFromDB.getLockValue());
}
connection.commit();
return true;
}
distributedLockDO.setLockValue(StringUtils.SPACE);
distributedLockDO.setExpireTime(0L);
boolean ret = updateDistributedLock(connection, distributedLockDO);
connection.commit();
return ret;
} catch (SQLException ex) {
if (!ignoreSQLException(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();
}
private boolean ignoreSQLException(SQLException exception) {
if (IGNORE_MYSQL_CODE.contains(exception.getErrorCode())) {
return true;
}
if (StringUtils.isNotBlank(exception.getMessage())) {
return IGNORE_MYSQL_MESSAGE.stream().anyMatch(message -> exception.getMessage().contains(message));
}
return false;
}
}

View File

@@ -1,78 +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.storage.db.lock;
import io.seata.common.executor.Initialize;
import io.seata.common.loader.EnhancedServiceLoader;
import io.seata.common.loader.LoadLevel;
import io.seata.config.ConfigurationFactory;
import io.seata.core.constants.ConfigurationKeys;
import io.seata.core.exception.TransactionException;
import io.seata.core.lock.Locker;
import io.seata.core.store.db.DataSourceProvider;
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.
*
* @author zjinlei
*/
@LoadLevel(name = "db")
public class DataBaseLockManager extends AbstractLockManager implements Initialize {
/**
* The locker.
*/
private Locker locker;
@Override
public void init() {
// init dataSource
String datasourceType = ConfigurationFactory.getInstance().getConfig(ConfigurationKeys.STORE_DB_DATASOURCE_TYPE);
DataSource lockStoreDataSource = EnhancedServiceLoader.load(DataSourceProvider.class, datasourceType).provide();
locker = new DataBaseLocker(lockStoreDataSource);
}
@Override
public boolean releaseLock(BranchSession branchSession) throws TransactionException {
try {
return getLocker().releaseLock(branchSession.getXid(), branchSession.getBranchId());
} catch (Exception t) {
LOGGER.error("unLock error, xid {}, branchId:{}", branchSession.getXid(), branchSession.getBranchId(), t);
return false;
}
}
@Override
public Locker getLocker(BranchSession branchSession) {
return locker;
}
@Override
public boolean releaseGlobalSessionLock(GlobalSession globalSession) throws TransactionException {
try {
return getLocker().releaseLock(globalSession.getXid());
} catch (Exception t) {
LOGGER.error("unLock globalSession error, xid:{}", globalSession.getXid(), t);
return false;
}
}
}

View File

@@ -1,144 +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.storage.db.lock;
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.
*
* @author zhangsen
*/
public class DataBaseLocker extends AbstractLocker {
private LockStore lockStore;
/**
* Instantiates a new Data base locker.
*/
public DataBaseLocker() {
}
/**
* Instantiates a new Data base locker.
*
* @param logStoreDataSource the log store data source
*/
public DataBaseLocker(DataSource logStoreDataSource) {
lockStore = new LockStoreDataBaseDAO(logStoreDataSource);
}
@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), autoCommit, skipCheckLock);
} catch (StoreException e) {
throw e;
} catch (Exception t) {
LOGGER.error("AcquireLock error, locks:{}", CollectionUtils.toString(locks), t);
return false;
}
}
@Override
public boolean releaseLock(List<RowLock> locks) {
if (CollectionUtils.isEmpty(locks)) {
// no lock
return true;
}
try {
return lockStore.unLock(convertToLockDO(locks));
} catch (StoreException e) {
throw e;
} catch (Exception t) {
LOGGER.error("unLock error, locks:{}", CollectionUtils.toString(locks), t);
return false;
}
}
@Override
public boolean releaseLock(String xid, Long branchId) {
try {
return lockStore.unLock(branchId);
} catch (StoreException e) {
throw e;
} catch (Exception t) {
LOGGER.error("unLock by branchId error, xid {}, branchId:{}", xid, branchId, t);
return false;
}
}
@Override
public boolean releaseLock(String xid) {
try {
return lockStore.unLock(xid);
} catch (StoreException e) {
throw e;
} catch (Exception t) {
LOGGER.error("unLock by branchIds error, xid {}", xid, t);
return false;
}
}
@Override
public boolean isLockable(List<RowLock> locks) {
if (CollectionUtils.isEmpty(locks)) {
// no lock
return true;
}
try {
return lockStore.isLockable(convertToLockDO(locks));
} catch (DataAccessException e) {
throw e;
} catch (Exception t) {
LOGGER.error("isLockable error, locks:{}", CollectionUtils.toString(locks), t);
return false;
}
}
@Override
public void updateLockStatus(String xid, LockStatus lockStatus) {
lockStore.updateLockStatus(xid, lockStatus);
}
/**
* Sets lock store.
*
* @param lockStore the lock store
*/
public void setLockStore(LockStore lockStore) {
this.lockStore = lockStore;
}
}

View File

@@ -1,437 +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.storage.db.lock;
import io.seata.common.exception.DataAccessException;
import io.seata.common.exception.StoreException;
import io.seata.common.util.CollectionUtils;
import io.seata.common.util.IOUtil;
import io.seata.common.util.LambdaUtils;
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.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.
*
* @author zhangsen
*/
public class LockStoreDataBaseDAO implements LockStore {
private static final Logger LOGGER = LoggerFactory.getLogger(LockStoreDataBaseDAO.class);
/**
* The constant CONFIG.
*/
protected static final Configuration CONFIG = ConfigurationFactory.getInstance();
/**
* The Lock store data source.
*/
protected DataSource lockStoreDataSource;
/**
* The Lock table.
*/
protected String lockTable;
/**
* The Db type.
*/
protected String dbType;
/**
* Instantiates a new Data base lock store dao.
*
* @param lockStoreDataSource the log store data source
*/
public LockStoreDataBaseDAO(DataSource lockStoreDataSource) {
this.lockStoreDataSource = lockStoreDataSource;
lockTable = CONFIG.getConfig(ConfigurationKeys.LOCK_DB_TABLE, DEFAULT_LOCK_DB_TABLE);
dbType = CONFIG.getConfig(ConfigurationKeys.STORE_DB_TYPE);
if (StringUtils.isBlank(dbType)) {
throw new StoreException("there must be db type.");
}
if (lockStoreDataSource == null) {
throw new StoreException("there must be lockStoreDataSource.");
}
}
@Override
public boolean acquireLock(LockDO lockDO) {
return acquireLock(Collections.singletonList(lockDO));
}
@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;
Set<String> dbExistedRowKeys = new HashSet<>();
boolean originalAutoCommit = true;
if (lockDOs.size() > 1) {
lockDOs = lockDOs.stream().filter(LambdaUtils.distinctByKey(LockDO::getRowKey)).collect(Collectors.toList());
}
try {
conn = lockStoreDataSource.getConnection();
if (originalAutoCommit = conn.getAutoCommit()) {
conn.setAutoCommit(false);
}
List<LockDO> unrepeatedLockDOs = lockDOs;
//check lock
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;
}
}
// lock
if (unrepeatedLockDOs.size() == 1) {
LockDO lockDO = unrepeatedLockDOs.get(0);
if (!doAcquireLock(conn, lockDO)) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Global lock acquire failed, xid {} branchId {} pk {}", lockDO.getXid(), lockDO.getBranchId(), lockDO.getPk());
}
conn.rollback();
return false;
}
} else {
if (!doAcquireLocks(conn, unrepeatedLockDOs)) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Global lock batch acquire failed, xid {} branchId {} pks {}", unrepeatedLockDOs.get(0).getXid(),
unrepeatedLockDOs.get(0).getBranchId(), unrepeatedLockDOs.stream().map(lockDO -> lockDO.getPk()).collect(Collectors.toList()));
}
conn.rollback();
return false;
}
}
conn.commit();
return true;
} catch (SQLException e) {
throw new StoreException(e);
} finally {
IOUtil.close(rs, ps);
if (conn != null) {
try {
if (originalAutoCommit) {
conn.setAutoCommit(true);
}
conn.close();
} catch (SQLException e) {
}
}
}
}
@Override
public boolean unLock(LockDO lockDO) {
return unLock(Collections.singletonList(lockDO));
}
@Override
public boolean unLock(List<LockDO> lockDOs) {
Connection conn = null;
PreparedStatement ps = null;
try {
conn = lockStoreDataSource.getConnection();
conn.setAutoCommit(true);
//batch release lock
String batchDeleteSQL = LockStoreSqlFactory.getLogStoreSql(dbType).getBatchDeleteLockSql(lockTable, lockDOs.size());
ps = conn.prepareStatement(batchDeleteSQL);
ps.setString(1, lockDOs.get(0).getXid());
for (int i = 0; i < lockDOs.size(); i++) {
ps.setString(i + 2, lockDOs.get(i).getRowKey());
}
ps.executeUpdate();
} catch (SQLException e) {
throw new StoreException(e);
} finally {
IOUtil.close(ps, conn);
}
return true;
}
@Override
public boolean unLock(String xid) {
Connection conn = null;
PreparedStatement ps = null;
try {
conn = lockStoreDataSource.getConnection();
conn.setAutoCommit(true);
//batch release lock by branch list
String batchDeleteSQL = LockStoreSqlFactory.getLogStoreSql(dbType).getBatchDeleteLockSqlByXid(lockTable);
ps = conn.prepareStatement(batchDeleteSQL);
ps.setString(1, xid);
ps.executeUpdate();
} catch (SQLException e) {
throw new StoreException(e);
} finally {
IOUtil.close(ps, conn);
}
return true;
}
@Override
public boolean unLock(Long branchId) {
Connection conn = null;
PreparedStatement ps = null;
try {
conn = lockStoreDataSource.getConnection();
conn.setAutoCommit(true);
//batch release lock by branchId
String batchDeleteSQL = LockStoreSqlFactory.getLogStoreSql(dbType).getBatchDeleteLockSqlByBranchId(lockTable);
ps = conn.prepareStatement(batchDeleteSQL);
ps.setLong(1, branchId);
ps.executeUpdate();
} catch (SQLException e) {
throw new StoreException(e);
} finally {
IOUtil.close(ps, conn);
}
return true;
}
@Override
public boolean isLockable(List<LockDO> lockDOs) {
Connection conn = null;
try {
conn = lockStoreDataSource.getConnection();
conn.setAutoCommit(true);
if (!checkLockable(conn, lockDOs)) {
return false;
}
return true;
} catch (SQLException e) {
throw new DataAccessException(e);
} finally {
IOUtil.close(conn);
}
}
@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
* @param lockDO the lock do
* @return the boolean
*/
protected boolean doAcquireLock(Connection conn, LockDO lockDO) {
PreparedStatement ps = null;
try {
//insert
String insertLockSQL = LockStoreSqlFactory.getLogStoreSql(dbType).getInsertLockSQL(lockTable);
ps = conn.prepareStatement(insertLockSQL);
ps.setString(1, lockDO.getXid());
ps.setLong(2, lockDO.getTransactionId());
ps.setLong(3, lockDO.getBranchId());
ps.setString(4, lockDO.getResourceId());
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 {
IOUtil.close(ps);
}
}
/**
* Do acquire lock boolean.
*
* @param conn the conn
* @param lockDOs the lock do list
* @return the boolean
*/
protected boolean doAcquireLocks(Connection conn, List<LockDO> lockDOs) throws SQLException {
PreparedStatement ps = null;
try {
//insert
String insertLockSQL = LockStoreSqlFactory.getLogStoreSql(dbType).getInsertLockSQL(lockTable);
ps = conn.prepareStatement(insertLockSQL);
for (LockDO lockDO : lockDOs) {
ps.setString(1, lockDO.getXid());
ps.setLong(2, lockDO.getTransactionId());
ps.setLong(3, lockDO.getBranchId());
ps.setString(4, lockDO.getResourceId());
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 (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);
}
}
/**
* Check lock boolean.
*
* @param conn the conn
* @param lockDOs the lock do
* @return the boolean
*/
protected boolean checkLockable(Connection conn, List<LockDO> lockDOs) {
PreparedStatement ps = null;
ResultSet rs = null;
try {
//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();
while (rs.next()) {
String xid = rs.getString("xid");
if (!StringUtils.equals(xid, lockDOs.get(0).getXid())) {
return false;
}
}
return true;
} catch (SQLException e) {
throw new DataAccessException(e);
} finally {
IOUtil.close(rs, ps);
}
}
/**
* Sets lock table.
*
* @param lockTable the lock table
*/
public void setLockTable(String lockTable) {
this.lockTable = lockTable;
}
/**
* Sets db type.
*
* @param dbType the db type
*/
public void setDbType(String dbType) {
this.dbType = dbType;
}
/**
* Sets log store data source.
*
* @param lockStoreDataSource the log store data source
*/
public void setLogStoreDataSource(DataSource lockStoreDataSource) {
this.lockStoreDataSource = lockStoreDataSource;
}
}

View File

@@ -1,190 +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.storage.db.session;
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.*;
import io.seata.server.storage.db.store.DataBaseTransactionStoreManager;
import io.seata.server.store.TransactionStoreManager.LogOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.List;
/**
* The Data base session manager.
*
* @author zhangsen
*/
@LoadLevel(name = "db", scope = Scope.PROTOTYPE)
public class DataBaseSessionManager extends AbstractSessionManager
implements Initialize {
/**
* The constant LOGGER.
*/
protected static final Logger LOGGER = LoggerFactory.getLogger(DataBaseSessionManager.class);
/**
* The Task name.
*/
protected String taskName;
/**
* Instantiates a new Data base session manager.
*/
public DataBaseSessionManager() {
super();
}
/**
* Instantiates a new Data base session manager.
*
* @param name the name
*/
public DataBaseSessionManager(String name) {
super();
this.taskName = name;
}
@Override
public void init() {
transactionStoreManager = DataBaseTransactionStoreManager.getInstance();
}
@Override
public void addGlobalSession(GlobalSession session) throws TransactionException {
if (StringUtils.isBlank(taskName)) {
boolean ret = transactionStoreManager.writeSession(LogOperation.GLOBAL_ADD, session);
if (!ret) {
throw new StoreException("addGlobalSession failed.");
}
} else {
boolean ret = transactionStoreManager.writeSession(LogOperation.GLOBAL_UPDATE, session);
if (!ret) {
throw new StoreException("addGlobalSession failed.");
}
}
}
@Override
public void updateGlobalSessionStatus(GlobalSession session, GlobalStatus status) throws TransactionException {
if (StringUtils.isNotBlank(taskName)) {
return;
}
session.setStatus(status);
boolean ret = transactionStoreManager.writeSession(LogOperation.GLOBAL_UPDATE, session);
if (!ret) {
throw new StoreException("updateGlobalSessionStatus failed.");
}
}
/**
* remove globalSession
* 1. rootSessionManager remove normal globalSession
* 2. retryCommitSessionManager and retryRollbackSessionManager remove retry expired globalSession
* @param session the session
* @throws TransactionException the transaction exception
*/
@Override
public void removeGlobalSession(GlobalSession session) throws TransactionException {
boolean ret = transactionStoreManager.writeSession(LogOperation.GLOBAL_REMOVE, session);
if (!ret) {
throw new StoreException("removeGlobalSession failed.");
}
}
@Override
public void addBranchSession(GlobalSession globalSession, BranchSession session) throws TransactionException {
if (StringUtils.isNotBlank(taskName)) {
return;
}
boolean ret = transactionStoreManager.writeSession(LogOperation.BRANCH_ADD, session);
if (!ret) {
throw new StoreException("addBranchSession failed.");
}
}
@Override
public void updateBranchSessionStatus(BranchSession session, BranchStatus status) throws TransactionException {
if (StringUtils.isNotBlank(taskName)) {
return;
}
boolean ret = transactionStoreManager.writeSession(LogOperation.BRANCH_UPDATE, session);
if (!ret) {
throw new StoreException("updateBranchSessionStatus failed.");
}
}
@Override
public void removeBranchSession(GlobalSession globalSession, BranchSession session) throws TransactionException {
if (StringUtils.isNotBlank(taskName)) {
return;
}
boolean ret = transactionStoreManager.writeSession(LogOperation.BRANCH_REMOVE, session);
if (!ret) {
throw new StoreException("removeBranchSession failed.");
}
}
@Override
public GlobalSession findGlobalSession(String xid) {
return this.findGlobalSession(xid, true);
}
@Override
public GlobalSession findGlobalSession(String xid, boolean withBranchSessions) {
return transactionStoreManager.readSession(xid, withBranchSessions);
}
@Override
public Collection<GlobalSession> allSessions() {
// get by taskName
if (SessionHolder.ASYNC_COMMITTING_SESSION_MANAGER_NAME.equalsIgnoreCase(taskName)) {
return findGlobalSessions(new SessionCondition(GlobalStatus.AsyncCommitting));
} else if (SessionHolder.RETRY_COMMITTING_SESSION_MANAGER_NAME.equalsIgnoreCase(taskName)) {
return findGlobalSessions(new SessionCondition(GlobalStatus.CommitRetrying, GlobalStatus.Committing));
} else if (SessionHolder.RETRY_ROLLBACKING_SESSION_MANAGER_NAME.equalsIgnoreCase(taskName)) {
return findGlobalSessions(new SessionCondition(GlobalStatus.RollbackRetrying, GlobalStatus.Rollbacking,
GlobalStatus.TimeoutRollbacking, GlobalStatus.TimeoutRollbackRetrying));
} else {
// all data
return findGlobalSessions(new SessionCondition(GlobalStatus.UnKnown, GlobalStatus.Begin, GlobalStatus.Committing,
GlobalStatus.CommitRetrying, GlobalStatus.Rollbacking, GlobalStatus.RollbackRetrying, GlobalStatus.TimeoutRollbacking,
GlobalStatus.TimeoutRollbackRetrying, GlobalStatus.AsyncCommitting));
}
}
@Override
public List<GlobalSession> findGlobalSessions(SessionCondition condition) {
// nothing need to do
return transactionStoreManager.readSession(condition);
}
@Override
public <T> T lockAndExecute(GlobalSession globalSession, GlobalSession.LockCallable<T> lockCallable)
throws TransactionException {
return lockCallable.call();
}
}

View File

@@ -1,256 +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.storage.db.store;
import io.seata.common.exception.StoreException;
import io.seata.common.loader.EnhancedServiceLoader;
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.model.GlobalStatus;
import io.seata.core.store.BranchTransactionDO;
import io.seata.core.store.GlobalTransactionDO;
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 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.
*
* @author zhangsen
*/
public class DataBaseTransactionStoreManager extends AbstractTransactionStoreManager
implements TransactionStoreManager {
private static volatile DataBaseTransactionStoreManager instance;
/**
* The constant CONFIG.
*/
protected static final Configuration CONFIG = ConfigurationFactory.getInstance();
/**
* The Log store.
*/
protected LogStore logStore;
/**
* The Log query limit.
*/
protected int logQueryLimit;
/**
* Get the instance.
*/
public static DataBaseTransactionStoreManager getInstance() {
if (instance == null) {
synchronized (DataBaseTransactionStoreManager.class) {
if (instance == null) {
instance = new DataBaseTransactionStoreManager();
}
}
}
return instance;
}
/**
* Instantiates a new Database transaction store manager.
*/
private DataBaseTransactionStoreManager() {
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();
logStore = new LogStoreDataBaseDAO(logStoreDataSource);
}
@Override
public boolean writeSession(LogOperation logOperation, SessionStorable session) {
if (LogOperation.GLOBAL_ADD.equals(logOperation)) {
return logStore.insertGlobalTransactionDO(SessionConverter.convertGlobalTransactionDO(session));
} else if (LogOperation.GLOBAL_UPDATE.equals(logOperation)) {
return logStore.updateGlobalTransactionDO(SessionConverter.convertGlobalTransactionDO(session));
} else if (LogOperation.GLOBAL_REMOVE.equals(logOperation)) {
return logStore.deleteGlobalTransactionDO(SessionConverter.convertGlobalTransactionDO(session));
} else if (LogOperation.BRANCH_ADD.equals(logOperation)) {
return logStore.insertBranchTransactionDO(SessionConverter.convertBranchTransactionDO(session));
} else if (LogOperation.BRANCH_UPDATE.equals(logOperation)) {
return logStore.updateBranchTransactionDO(SessionConverter.convertBranchTransactionDO(session));
} else if (LogOperation.BRANCH_REMOVE.equals(logOperation)) {
return logStore.deleteBranchTransactionDO(SessionConverter.convertBranchTransactionDO(session));
} else {
throw new StoreException("Unknown LogOperation:" + logOperation.name());
}
}
/**
* Read session global session.
*
* @param transactionId the transaction id
* @return the global session
*/
public GlobalSession readSession(Long transactionId) {
//global transaction
GlobalTransactionDO globalTransactionDO = logStore.queryGlobalTransactionDO(transactionId);
if (globalTransactionDO == null) {
return null;
}
//branch transactions
List<BranchTransactionDO> branchTransactionDOs = logStore.queryBranchTransactionDO(
globalTransactionDO.getXid());
return getGlobalSession(globalTransactionDO, branchTransactionDOs);
}
/**
* Read session global session.
*
* @param xid the xid
* @return the global session
*/
@Override
public GlobalSession readSession(String xid) {
return this.readSession(xid, true);
}
/**
* Read session global session.
*
* @param xid the xid
* @param withBranchSessions the withBranchSessions
* @return the global session
*/
@Override
public GlobalSession readSession(String xid, boolean withBranchSessions) {
//global transaction
GlobalTransactionDO globalTransactionDO = logStore.queryGlobalTransactionDO(xid);
if (globalTransactionDO == null) {
return null;
}
//branch transactions
List<BranchTransactionDO> branchTransactionDOs = null;
//reduce rpc with db when branchRegister and getGlobalStatus
if (withBranchSessions) {
branchTransactionDOs = logStore.queryBranchTransactionDO(globalTransactionDO.getXid());
}
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
*/
@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);
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()));
}
}
Map<String, List<BranchTransactionDO>> finalBranchTransactionDOsMap = branchTransactionDOsMap;
return globalTransactionDOs.stream()
.map(globalTransactionDO -> getGlobalSession(globalTransactionDO,
finalBranchTransactionDOsMap.get(globalTransactionDO.getXid()), withBranchSessions))
.collect(Collectors.toList());
}
@Override
public List<GlobalSession> readSession(SessionCondition sessionCondition) {
if (StringUtils.isNotBlank(sessionCondition.getXid())) {
GlobalSession globalSession = readSession(sessionCondition.getXid());
if (globalSession != null) {
List<GlobalSession> globalSessions = new ArrayList<>();
globalSessions.add(globalSession);
return globalSessions;
}
} else if (sessionCondition.getTransactionId() != null) {
GlobalSession globalSession = readSession(sessionCondition.getTransactionId());
if (globalSession != null) {
List<GlobalSession> globalSessions = new ArrayList<>();
globalSessions.add(globalSession);
return globalSessions;
}
} else if (CollectionUtils.isNotEmpty(sessionCondition.getStatuses())) {
return readSession(sessionCondition.getStatuses(), !sessionCondition.isLazyLoadBranch());
}
return null;
}
private GlobalSession getGlobalSession(GlobalTransactionDO globalTransactionDO,
List<BranchTransactionDO> branchTransactionDOs) {
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) {
globalSession.add(SessionConverter.convertBranchSession(branchTransactionDO));
}
}
return globalSession;
}
/**
* Sets log store.
*
* @param logStore the log store
*/
public void setLogStore(LogStore logStore) {
this.logStore = logStore;
}
/**
* Sets log query limit.
*
* @param logQueryLimit the log query limit
*/
public void setLogQueryLimit(int logQueryLimit) {
this.logQueryLimit = logQueryLimit;
}
}

View File

@@ -1,601 +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.storage.db.store;
import io.seata.common.exception.DataAccessException;
import io.seata.common.exception.StoreException;
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.core.constants.ConfigurationKeys;
import io.seata.core.constants.ServerTableColumnsName;
import io.seata.core.store.BranchTransactionDO;
import io.seata.core.store.GlobalTransactionDO;
import io.seata.core.store.LogStore;
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;
/**
* The type Log store data base dao.
*
* @author zhangsen
*/
public class LogStoreDataBaseDAO implements LogStore {
private static final Logger LOGGER = LoggerFactory.getLogger(LogStoreDataBaseDAO.class);
/**
* The transaction name key
*/
private static final String TRANSACTION_NAME_KEY = "TRANSACTION_NAME";
/**
* The transaction name default size is 128
*/
private static final int TRANSACTION_NAME_DEFAULT_SIZE = 128;
/**
* The constant CONFIG.
*/
protected static final Configuration CONFIG = ConfigurationFactory.getInstance();
/**
* The Log store data source.
*/
protected DataSource logStoreDataSource = null;
/**
* The Global table.
*/
protected String globalTable;
/**
* The Branch table.
*/
protected String branchTable;
private String dbType;
private int transactionNameColumnSize = TRANSACTION_NAME_DEFAULT_SIZE;
/**
* Instantiates a new Log store data base dao.
*
* @param logStoreDataSource the log store data source
*/
public LogStoreDataBaseDAO(DataSource logStoreDataSource) {
this.logStoreDataSource = logStoreDataSource;
globalTable = CONFIG.getConfig(ConfigurationKeys.STORE_DB_GLOBAL_TABLE,
DEFAULT_STORE_DB_GLOBAL_TABLE);
branchTable = CONFIG.getConfig(ConfigurationKeys.STORE_DB_BRANCH_TABLE,
DEFAULT_STORE_DB_BRANCH_TABLE);
dbType = CONFIG.getConfig(ConfigurationKeys.STORE_DB_TYPE);
if (StringUtils.isBlank(dbType)) {
throw new StoreException("there must be db type.");
}
if (logStoreDataSource == null) {
throw new StoreException("there must be logStoreDataSource.");
}
// init transaction_name size
initTransactionNameSize();
}
@Override
public GlobalTransactionDO queryGlobalTransactionDO(String xid) {
String sql = LogStoreSqlsFactory.getLogStoreSqls(dbType).getQueryGlobalTransactionSQL(globalTable);
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = logStoreDataSource.getConnection();
conn.setAutoCommit(true);
ps = conn.prepareStatement(sql);
ps.setString(1, xid);
rs = ps.executeQuery();
if (rs.next()) {
return convertGlobalTransactionDO(rs);
} else {
return null;
}
} catch (SQLException e) {
throw new DataAccessException(e);
} finally {
IOUtil.close(rs, ps, conn);
}
}
@Override
public GlobalTransactionDO queryGlobalTransactionDO(long transactionId) {
String sql = LogStoreSqlsFactory.getLogStoreSqls(dbType).getQueryGlobalTransactionSQLByTransactionId(globalTable);
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = logStoreDataSource.getConnection();
conn.setAutoCommit(true);
ps = conn.prepareStatement(sql);
ps.setLong(1, transactionId);
rs = ps.executeQuery();
if (rs.next()) {
return convertGlobalTransactionDO(rs);
} else {
return null;
}
} catch (SQLException e) {
throw new DataAccessException(e);
} finally {
IOUtil.close(rs, ps, conn);
}
}
@Override
public List<GlobalTransactionDO> queryGlobalTransactionDO(int[] statuses, int limit) {
List<GlobalTransactionDO> ret = new ArrayList<>();
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = logStoreDataSource.getConnection();
conn.setAutoCommit(true);
String paramsPlaceHolder = org.apache.commons.lang.StringUtils.repeat("?", ",", statuses.length);
String sql = LogStoreSqlsFactory.getLogStoreSqls(dbType).getQueryGlobalTransactionSQLByStatus(globalTable, paramsPlaceHolder);
ps = conn.prepareStatement(sql);
for (int i = 0; i < statuses.length; i++) {
int status = statuses[i];
ps.setInt(i + 1, status);
}
ps.setInt(statuses.length + 1, limit);
rs = ps.executeQuery();
while (rs.next()) {
ret.add(convertGlobalTransactionDO(rs));
}
return ret;
} catch (SQLException e) {
throw new DataAccessException(e);
} finally {
IOUtil.close(rs, ps, conn);
}
}
@Override
public boolean insertGlobalTransactionDO(GlobalTransactionDO globalTransactionDO) {
String sql = LogStoreSqlsFactory.getLogStoreSqls(dbType).getInsertGlobalTransactionSQL(globalTable);
Connection conn = null;
PreparedStatement ps = null;
try {
int index = 1;
conn = logStoreDataSource.getConnection();
conn.setAutoCommit(true);
ps = conn.prepareStatement(sql);
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(index++, transactionName);
ps.setInt(index++, globalTransactionDO.getTimeout());
ps.setLong(index++, globalTransactionDO.getBeginTime());
ps.setString(index++, globalTransactionDO.getApplicationData());
return ps.executeUpdate() > 0;
} catch (SQLException e) {
throw new StoreException(e);
} finally {
IOUtil.close(ps, conn);
}
}
@Override
public boolean updateGlobalTransactionDO(GlobalTransactionDO globalTransactionDO) {
String sql = LogStoreSqlsFactory.getLogStoreSqls(dbType).getUpdateGlobalTransactionStatusSQL(globalTable);
Connection conn = null;
PreparedStatement ps = null;
try {
int index = 1;
conn = logStoreDataSource.getConnection();
conn.setAutoCommit(true);
ps = conn.prepareStatement(sql);
ps.setInt(index++, globalTransactionDO.getStatus());
ps.setString(index++, globalTransactionDO.getXid());
return ps.executeUpdate() > 0;
} catch (SQLException e) {
throw new StoreException(e);
} finally {
IOUtil.close(ps, conn);
}
}
@Override
public boolean deleteGlobalTransactionDO(GlobalTransactionDO globalTransactionDO) {
String sql = LogStoreSqlsFactory.getLogStoreSqls(dbType).getDeleteGlobalTransactionSQL(globalTable);
Connection conn = null;
PreparedStatement ps = null;
try {
conn = logStoreDataSource.getConnection();
conn.setAutoCommit(true);
ps = conn.prepareStatement(sql);
ps.setString(1, globalTransactionDO.getXid());
ps.executeUpdate();
} catch (SQLException e) {
throw new StoreException(e);
} finally {
IOUtil.close(ps, conn);
}
return true;
}
@Override
public List<BranchTransactionDO> queryBranchTransactionDO(String xid) {
List<BranchTransactionDO> rets = new ArrayList<>();
String sql = LogStoreSqlsFactory.getLogStoreSqls(dbType).getQueryBranchTransaction(branchTable);
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = logStoreDataSource.getConnection();
conn.setAutoCommit(true);
ps = conn.prepareStatement(sql);
ps.setString(1, xid);
rs = ps.executeQuery();
while (rs.next()) {
rets.add(convertBranchTransactionDO(rs));
}
return rets;
} catch (SQLException e) {
throw new DataAccessException(e);
} finally {
IOUtil.close(rs, ps, conn);
}
}
@Override
public List<BranchTransactionDO> queryBranchTransactionDO(List<String> xids) {
int length = xids.size();
List<BranchTransactionDO> rets = new ArrayList<>(length * 3);
String paramsPlaceHolder = org.apache.commons.lang.StringUtils.repeat("?", ",", length);
String sql = LogStoreSqlsFactory.getLogStoreSqls(dbType).getQueryBranchTransaction(branchTable, paramsPlaceHolder);
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = logStoreDataSource.getConnection();
conn.setAutoCommit(true);
ps = conn.prepareStatement(sql);
for (int i = 0; i < length; i++) {
ps.setString(i + 1, xids.get(i));
}
rs = ps.executeQuery();
while (rs.next()) {
rets.add(convertBranchTransactionDO(rs));
}
return rets;
} catch (SQLException e) {
throw new DataAccessException(e);
} finally {
IOUtil.close(rs, ps, conn);
}
}
@Override
public boolean insertBranchTransactionDO(BranchTransactionDO branchTransactionDO) {
String sql = LogStoreSqlsFactory.getLogStoreSqls(dbType).getInsertBranchTransactionSQL(branchTable);
Connection conn = null;
PreparedStatement ps = null;
try {
int index = 1;
conn = logStoreDataSource.getConnection();
conn.setAutoCommit(true);
ps = conn.prepareStatement(sql);
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) {
throw new StoreException(e);
} finally {
IOUtil.close(ps, conn);
}
}
@Override
public boolean updateBranchTransactionDO(BranchTransactionDO branchTransactionDO) {
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(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) {
throw new StoreException(e);
} finally {
IOUtil.close(ps, conn);
}
}
@Override
public boolean deleteBranchTransactionDO(BranchTransactionDO branchTransactionDO) {
String sql = LogStoreSqlsFactory.getLogStoreSqls(dbType).getDeleteBranchTransactionByBranchIdSQL(branchTable);
Connection conn = null;
PreparedStatement ps = null;
try {
conn = logStoreDataSource.getConnection();
conn.setAutoCommit(true);
ps = conn.prepareStatement(sql);
ps.setString(1, branchTransactionDO.getXid());
ps.setLong(2, branchTransactionDO.getBranchId());
ps.executeUpdate();
} catch (SQLException e) {
throw new StoreException(e);
} finally {
IOUtil.close(ps, conn);
}
return true;
}
@Override
public long getCurrentMaxSessionId(long high, long low) {
String transMaxSql = LogStoreSqlsFactory.getLogStoreSqls(dbType).getQueryGlobalMax(globalTable);
String branchMaxSql = LogStoreSqlsFactory.getLogStoreSqls(dbType).getQueryBranchMax(branchTable);
long maxTransId = getCurrentMaxSessionId(transMaxSql, high, low);
long maxBranchId = getCurrentMaxSessionId(branchMaxSql, high, low);
return Math.max(maxBranchId, maxTransId);
}
private long getCurrentMaxSessionId(String sql, long high, long low) {
long max = 0;
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
int index = 1;
conn = logStoreDataSource.getConnection();
conn.setAutoCommit(true);
ps = conn.prepareStatement(sql);
ps.setLong(index++, high);
ps.setLong(index++, low);
rs = ps.executeQuery();
while (rs.next()) {
max = rs.getLong(1);
}
} catch (SQLException e) {
throw new DataAccessException(e);
} finally {
IOUtil.close(rs, ps, conn);
}
return max;
}
private GlobalTransactionDO convertGlobalTransactionDO(ResultSet rs) throws SQLException {
GlobalTransactionDO globalTransactionDO = new GlobalTransactionDO();
globalTransactionDO.setXid(rs.getString(ServerTableColumnsName.GLOBAL_TABLE_XID));
globalTransactionDO.setStatus(rs.getInt(ServerTableColumnsName.GLOBAL_TABLE_STATUS));
globalTransactionDO.setApplicationId(rs.getString(ServerTableColumnsName.GLOBAL_TABLE_APPLICATION_ID));
globalTransactionDO.setBeginTime(rs.getLong(ServerTableColumnsName.GLOBAL_TABLE_BEGIN_TIME));
globalTransactionDO.setTimeout(rs.getInt(ServerTableColumnsName.GLOBAL_TABLE_TIMEOUT));
globalTransactionDO.setTransactionId(rs.getLong(ServerTableColumnsName.GLOBAL_TABLE_TRANSACTION_ID));
globalTransactionDO.setTransactionName(rs.getString(ServerTableColumnsName.GLOBAL_TABLE_TRANSACTION_NAME));
globalTransactionDO.setTransactionServiceGroup(
rs.getString(ServerTableColumnsName.GLOBAL_TABLE_TRANSACTION_SERVICE_GROUP));
globalTransactionDO.setApplicationData(rs.getString(ServerTableColumnsName.GLOBAL_TABLE_APPLICATION_DATA));
globalTransactionDO.setGmtCreate(rs.getTimestamp(ServerTableColumnsName.GLOBAL_TABLE_GMT_CREATE));
globalTransactionDO.setGmtModified(rs.getTimestamp(ServerTableColumnsName.GLOBAL_TABLE_GMT_MODIFIED));
return globalTransactionDO;
}
private BranchTransactionDO convertBranchTransactionDO(ResultSet rs) throws SQLException {
BranchTransactionDO branchTransactionDO = new BranchTransactionDO();
branchTransactionDO.setResourceGroupId(rs.getString(ServerTableColumnsName.BRANCH_TABLE_RESOURCE_GROUP_ID));
branchTransactionDO.setStatus(rs.getInt(ServerTableColumnsName.BRANCH_TABLE_STATUS));
branchTransactionDO.setApplicationData(rs.getString(ServerTableColumnsName.BRANCH_TABLE_APPLICATION_DATA));
branchTransactionDO.setClientId(rs.getString(ServerTableColumnsName.BRANCH_TABLE_CLIENT_ID));
branchTransactionDO.setXid(rs.getString(ServerTableColumnsName.BRANCH_TABLE_XID));
branchTransactionDO.setResourceId(rs.getString(ServerTableColumnsName.BRANCH_TABLE_RESOURCE_ID));
branchTransactionDO.setBranchId(rs.getLong(ServerTableColumnsName.BRANCH_TABLE_BRANCH_ID));
branchTransactionDO.setBranchType(rs.getString(ServerTableColumnsName.BRANCH_TABLE_BRANCH_TYPE));
branchTransactionDO.setTransactionId(rs.getLong(ServerTableColumnsName.BRANCH_TABLE_TRANSACTION_ID));
branchTransactionDO.setGmtCreate(rs.getTimestamp(ServerTableColumnsName.BRANCH_TABLE_GMT_CREATE));
branchTransactionDO.setGmtModified(rs.getTimestamp(ServerTableColumnsName.BRANCH_TABLE_GMT_MODIFIED));
return branchTransactionDO;
}
/**
* the public modifier only for test
*/
public void initTransactionNameSize() {
ColumnInfo columnInfo = queryTableStructure(globalTable, TRANSACTION_NAME_KEY);
if (columnInfo == null) {
LOGGER.warn("{} table or {} column not found", globalTable, TRANSACTION_NAME_KEY);
return;
}
this.transactionNameColumnSize = columnInfo.getColumnSize();
}
/**
* query column info from table
*
* @param tableName the table name
* @param colName the column name
* @return the column info
*/
private ColumnInfo queryTableStructure(final String tableName, String colName) {
try (Connection conn = logStoreDataSource.getConnection()) {
DatabaseMetaData dbmd = conn.getMetaData();
String schema = getSchema(conn);
ResultSet tableRs = dbmd.getTables(null, schema, "%", new String[]{"TABLE"});
while (tableRs.next()) {
String table = tableRs.getString("TABLE_NAME");
if (StringUtils.equalsIgnoreCase(table, tableName)) {
ResultSet columnRs = conn.getMetaData().getColumns(null, schema, table, null);
while (columnRs.next()) {
ColumnInfo info = new ColumnInfo();
String columnName = columnRs.getString("COLUMN_NAME");
info.setColumnName(columnName);
String typeName = columnRs.getString("TYPE_NAME");
info.setTypeName(typeName);
int columnSize = columnRs.getInt("COLUMN_SIZE");
info.setColumnSize(columnSize);
String remarks = columnRs.getString("REMARKS");
info.setRemarks(remarks);
if (StringUtils.equalsIgnoreCase(columnName, colName)) {
return info;
}
}
break;
}
}
} catch (SQLException e) {
LOGGER.error("query transaction_name size fail, {}", e.getMessage(), e);
}
return null;
}
private String getSchema(Connection conn) throws SQLException {
if ("h2".equalsIgnoreCase(dbType)) {
return null;
} else if ("postgresql".equalsIgnoreCase(dbType)) {
String sql = "select current_schema";
try (PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery()) {
String schema = null;
if (rs.next()) {
schema = rs.getString(1);
}
return schema;
} catch (SQLException e) {
throw new StoreException(e);
}
} else {
return conn.getMetaData().getUserName();
}
}
/**
* Sets log store data source.
*
* @param logStoreDataSource the log store data source
*/
public void setLogStoreDataSource(DataSource logStoreDataSource) {
this.logStoreDataSource = logStoreDataSource;
}
/**
* Sets global table.
*
* @param globalTable the global table
*/
public void setGlobalTable(String globalTable) {
this.globalTable = globalTable;
}
/**
* Sets branch table.
*
* @param branchTable the branch table
*/
public void setBranchTable(String branchTable) {
this.branchTable = branchTable;
}
/**
* Sets db type.
*
* @param dbType the db type
*/
public void setDbType(String dbType) {
this.dbType = dbType;
}
public int getTransactionNameColumnSize() {
return transactionNameColumnSize;
}
/**
* column info
*/
private static class ColumnInfo {
private String columnName;
private String typeName;
private int columnSize;
private String remarks;
public String getColumnName() {
return columnName;
}
public void setColumnName(String columnName) {
this.columnName = columnName;
}
public String getTypeName() {
return typeName;
}
public void setTypeName(String typeName) {
this.typeName = typeName;
}
public int getColumnSize() {
return columnSize;
}
public void setColumnSize(int columnSize) {
this.columnSize = columnSize;
}
public String getRemarks() {
return remarks;
}
public void setRemarks(String remarks) {
this.remarks = remarks;
}
}
}

View File

@@ -1,43 +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.storage.file;
/**
* @author lizhao
*/
public enum FlushDiskMode {
/**
* sync flush disk
*/
SYNC_MODEL("sync"),
/**
* async flush disk
*/
ASYNC_MODEL("async");
private String modeStr;
FlushDiskMode(String modeStr) {
this.modeStr = modeStr;
}
public static FlushDiskMode findDiskMode(String modeStr) {
if (SYNC_MODEL.modeStr.equals(modeStr)) {
return SYNC_MODEL;
}
return ASYNC_MODEL;
}
}

View File

@@ -1,45 +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.storage.file;
import java.util.List;
/**
* The interface Reloadable store.
*
* @author zhangsen
*/
public interface ReloadableStore {
/**
* Read write store.
*
* @param readSize the read size
* @param isHistory the is history
* @return the list
*/
List<TransactionWriteStore> readWriteStore(int readSize, boolean isHistory);
/**
* Has remaining boolean.
*
* @param isHistory the is history
* @return the boolean
*/
boolean hasRemaining(boolean isHistory);
}

View File

@@ -1,129 +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.storage.file;
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.
*
* @author slievrly
*/
public class TransactionWriteStore implements SessionStorable {
private SessionStorable sessionRequest;
private LogOperation operate;
/**
* Instantiates a new Transaction write store.
*
* @param sessionRequest the session request
* @param operate the operate
*/
public TransactionWriteStore(SessionStorable sessionRequest, LogOperation operate) {
this.sessionRequest = sessionRequest;
this.operate = operate;
}
/**
* Instantiates a new Transaction write store.
*/
public TransactionWriteStore() {}
/**
* Gets session request.
*
* @return the session request
*/
public SessionStorable getSessionRequest() {
return sessionRequest;
}
/**
* Sets session request.
*
* @param sessionRequest the session request
*/
public void setSessionRequest(SessionStorable sessionRequest) {
this.sessionRequest = sessionRequest;
}
/**
* Gets operate.
*
* @return the operate
*/
public LogOperation getOperate() {
return operate;
}
/**
* Sets operate.
*
* @param operate the operate
*/
public void setOperate(LogOperation operate) {
this.operate = operate;
}
@Override
public byte[] encode() {
byte[] bySessionRequest = this.sessionRequest.encode();
byte byOpCode = this.getOperate().getCode();
int len = bySessionRequest.length + 1;
byte[] byResult = new byte[len];
ByteBuffer byteBuffer = ByteBuffer.wrap(byResult);
byteBuffer.put(bySessionRequest);
byteBuffer.put(byOpCode);
return byResult;
}
@Override
public void decode(byte[] src) {
ByteBuffer byteBuffer = ByteBuffer.wrap(src);
byte[] bySessionRequest = new byte[src.length - 1];
byteBuffer.get(bySessionRequest);
byte byOpCode = byteBuffer.get();
this.operate = LogOperation.getLogOperationByCode(byOpCode);
SessionStorable tmpSessionStorable = getSessionInstanceByOperation(this.operate);
tmpSessionStorable.decode(bySessionRequest);
this.sessionRequest = tmpSessionStorable;
}
private SessionStorable getSessionInstanceByOperation(LogOperation logOperation) {
SessionStorable sessionStorable = null;
switch (logOperation) {
case GLOBAL_ADD:
case GLOBAL_UPDATE:
case GLOBAL_REMOVE:
sessionStorable = new GlobalSession();
break;
case BRANCH_ADD:
case BRANCH_UPDATE:
case BRANCH_REMOVE:
sessionStorable = new BranchSession();
break;
default:
throw new ShouldNeverHappenException("incorrect logOperation");
}
return sessionStorable;
}
}

View File

@@ -1,60 +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.storage.file.lock;
import io.seata.common.loader.LoadLevel;
import io.seata.core.exception.TransactionException;
import io.seata.core.lock.Locker;
import io.seata.server.lock.AbstractLockManager;
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;
/**
* The type file lock manager.
*
* @author zhangsen
*/
@LoadLevel(name = "file")
public class FileLockManager extends AbstractLockManager {
@Override
public Locker getLocker(BranchSession branchSession) {
return new FileLocker(branchSession);
}
@Override
public boolean releaseGlobalSessionLock(GlobalSession globalSession) throws TransactionException {
List<BranchSession> branchSessions = globalSession.getBranchSessions();
boolean releaseLockResult = true;
for (BranchSession branchSession : branchSessions) {
try {
MDC.put(MDC_KEY_BRANCH_ID, String.valueOf(branchSession.getBranchId()));
if (!this.releaseLock(branchSession)) {
releaseLockResult = false;
}
} finally {
MDC.remove(MDC_KEY_BRANCH_ID);
}
}
return releaseLockResult;
}
}

View File

@@ -1,220 +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.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 static io.seata.core.exception.TransactionExceptionCode.LockKeyConflictFailFast;
/**
* The type Memory locker.
*
* @author zhangsen
*/
public class FileLocker extends AbstractLocker {
private static final int BUCKET_PER_TABLE = 128;
private static final ConcurrentMap<String/* resourceId */, ConcurrentMap<String/* tableName */,
ConcurrentMap<Integer/* bucketId */, BucketLockMap>>>
LOCK_MAP = new ConcurrentHashMap<>();
/**
* The Branch session.
*/
protected BranchSession branchSession;
/**
* Instantiates a new Memory locker.
*
* @param branchSession the branch session
*/
public FileLocker(BranchSession branchSession) {
this.branchSession = branchSession;
}
@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;
}
String resourceId = branchSession.getResourceId();
long transactionId = branchSession.getTransactionId();
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();
ConcurrentMap<Integer, BucketLockMap> tableLockMap = CollectionUtils.computeIfAbsent(dbLockMap, tableName,
key -> new ConcurrentHashMap<>());
int bucketId = pk.hashCode() % BUCKET_PER_TABLE;
BucketLockMap bucketLockMap = CollectionUtils.computeIfAbsent(tableLockMap, bucketId,
key -> new BucketLockMap());
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 -> ConcurrentHashMap.newKeySet());
keysInHolder.add(pk);
} else if (previousLockBranchSession.getTransactionId() == transactionId) {
// Locked by me before
continue;
} else {
LOGGER.info("Global lock on [" + tableName + ":" + pk + "] is holding by " + previousLockBranchSession.getBranchId());
try {
// Release all acquired locks.
branchSession.unlock();
} catch (TransactionException e) {
throw new FrameworkException(e);
}
if (!autoCommit && previousLockBranchSession.getLockStatus() == LockStatus.Rollbacking) {
failFast = true;
break;
}
if (canLock) {
canLock = false;
if (autoCommit) {
break;
}
}
}
}
if (failFast) {
throw new StoreException(new BranchTransactionException(LockKeyConflictFailFast));
}
return canLock;
}
@Override
public boolean releaseLock(List<RowLock> rowLock) {
if (CollectionUtils.isEmpty(rowLock)) {
//no lock
return true;
}
ConcurrentMap<BucketLockMap, Set<String>> lockHolder = branchSession.getLockHolder();
if (CollectionUtils.isEmpty(lockHolder)) {
return true;
}
for (Map.Entry<BucketLockMap, Set<String>> entry : lockHolder.entrySet()) {
BucketLockMap bucket = entry.getKey();
Set<String> keys = entry.getValue();
for (String key : keys) {
// remove lock only if it locked by myself
bucket.get().remove(key, branchSession);
}
}
lockHolder.clear();
return true;
}
@Override
public boolean isLockable(List<RowLock> rowLocks) {
if (CollectionUtils.isEmpty(rowLocks)) {
//no lock
return true;
}
Long transactionId = rowLocks.get(0).getTransactionId();
String resourceId = rowLocks.get(0).getResourceId();
ConcurrentMap<String, ConcurrentMap<Integer, BucketLockMap>> dbLockMap = LOCK_MAP.get(resourceId);
if (dbLockMap == null) {
return true;
}
for (RowLock rowLock : rowLocks) {
String tableName = rowLock.getTableName();
String pk = rowLock.getPk();
ConcurrentMap<Integer, BucketLockMap> tableLockMap = dbLockMap.get(tableName);
if (tableLockMap == null) {
continue;
}
int bucketId = pk.hashCode() % BUCKET_PER_TABLE;
BucketLockMap bucketLockMap = tableLockMap.get(bucketId);
if (bucketLockMap == null) {
continue;
}
BranchSession branchSession = bucketLockMap.get().get(pk);
Long lockingTransactionId = branchSession != null ? branchSession.getTransactionId() : null;
if (lockingTransactionId == null || lockingTransactionId.longValue() == transactionId) {
// Locked by me
continue;
} else {
LOGGER.info("Global lock on [" + tableName + ":" + pk + "] is holding by " + lockingTransactionId);
return false;
}
}
return true;
}
@Override
public void updateLockStatus(String xid, LockStatus lockStatus) {
}
@Override
public void cleanAllLocks() {
LOCK_MAP.clear();
}
/**
* 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 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 */, BranchSession/* branchSession */> bucketLockMap
= new ConcurrentHashMap<>();
ConcurrentHashMap<String, BranchSession> get() {
return bucketLockMap;
}
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public boolean equals(Object o) {
return super.equals(o);
}
}
}

View File

@@ -1,370 +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.storage.file.session;
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.*;
import io.seata.server.storage.file.ReloadableStore;
import io.seata.server.storage.file.TransactionWriteStore;
import io.seata.server.storage.file.store.FileTransactionStoreManager;
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.
*
* @author slievrly
*/
@LoadLevel(name = "file", scope = Scope.PROTOTYPE)
public class FileSessionManager extends AbstractSessionManager implements Reloadable {
private static final int READ_SIZE = ConfigurationFactory.getInstance().getInt(
ConfigurationKeys.SERVICE_SESSION_RELOAD_READ_SIZE, DEFAULT_SERVICE_SESSION_RELOAD_READ_SIZE);
/**
* The Session map.
*/
private Map<String, GlobalSession> sessionMap = new ConcurrentHashMap<>();
/**
* Instantiates a new File based session manager.
*
* @param name the name
* @param sessionStoreFilePath the session store file path
* @throws IOException the io exception
*/
public FileSessionManager(String name, String sessionStoreFilePath) throws IOException {
super(name);
if (StringUtils.isNotBlank(sessionStoreFilePath)) {
transactionStoreManager = new FileTransactionStoreManager(
sessionStoreFilePath + File.separator + name, this);
} else {
transactionStoreManager = new AbstractTransactionStoreManager() {
@Override
public boolean writeSession(LogOperation logOperation, SessionStorable session) {
return true;
}
};
}
}
@Override
public void reload() {
restoreSessions();
}
@Override
public void addGlobalSession(GlobalSession session) throws TransactionException {
CollectionUtils.computeIfAbsent(sessionMap, session.getXid(), k -> {
try {
super.addGlobalSession(session);
} catch (TransactionException e) {
LOGGER.error("addGlobalSession fail, msg: {}", e.getMessage());
}
return session;
});
}
@Override
public GlobalSession findGlobalSession(String xid) {
return sessionMap.get(xid);
}
@Override
public GlobalSession findGlobalSession(String xid, boolean withBranchSessions) {
// withBranchSessions without process in memory
return sessionMap.get(xid);
}
@Override
public void removeGlobalSession(GlobalSession session) throws TransactionException {
if (sessionMap.remove(session.getXid()) != null) {
super.removeGlobalSession(session);
}
}
@Override
public Collection<GlobalSession> allSessions() {
return sessionMap.values();
}
@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 (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;
}
@Override
public <T> T lockAndExecute(GlobalSession globalSession, GlobalSession.LockCallable<T> lockCallable)
throws TransactionException {
globalSession.lock();
try {
return lockCallable.call();
} finally {
globalSession.unlock();
}
}
private void restoreSessions() {
final Set<String> removedGlobalBuffer = new HashSet<>();
final Map<String, Map<Long, BranchSession>> unhandledBranchBuffer = new HashMap<>();
restoreSessions(true, removedGlobalBuffer, unhandledBranchBuffer);
restoreSessions(false, removedGlobalBuffer, unhandledBranchBuffer);
if (!unhandledBranchBuffer.isEmpty()) {
unhandledBranchBuffer.values().forEach(unhandledBranchSessions -> {
unhandledBranchSessions.values().forEach(branchSession -> {
String xid = branchSession.getXid();
if (removedGlobalBuffer.contains(xid)) {
return;
}
long bid = branchSession.getBranchId();
GlobalSession found = sessionMap.get(xid);
if (found == null) {
// Ignore
if (LOGGER.isInfoEnabled()) {
LOGGER.info("GlobalSession Does Not Exists For BranchSession [" + bid + "/" + xid + "]");
}
} else {
BranchSession existingBranch = found.getBranch(branchSession.getBranchId());
if (existingBranch == null) {
found.add(branchSession);
} else {
existingBranch.setStatus(branchSession.getStatus());
}
}
});
});
}
}
private boolean checkSessionStatus(GlobalSession globalSession) {
GlobalStatus globalStatus = globalSession.getStatus();
switch (globalStatus) {
case UnKnown:
case Committed:
case CommitFailed:
case Rollbacked:
case RollbackFailed:
case TimeoutRollbacked:
case TimeoutRollbackFailed:
case RollbackRetryTimeout:
case Finished:
return false;
default:
return true;
}
}
private void restoreSessions(boolean isHistory, Set<String> removedGlobalBuffer, Map<String,
Map<Long, BranchSession>> unhandledBranchBuffer) {
if (!(transactionStoreManager instanceof ReloadableStore)) {
return;
}
while (((ReloadableStore)transactionStoreManager).hasRemaining(isHistory)) {
List<TransactionWriteStore> stores = ((ReloadableStore)transactionStoreManager).readWriteStore(READ_SIZE,
isHistory);
restore(stores, removedGlobalBuffer, unhandledBranchBuffer);
}
}
private void restore(List<TransactionWriteStore> stores, Set<String> removedGlobalBuffer,
Map<String, Map<Long, BranchSession>> unhandledBranchBuffer) {
for (TransactionWriteStore store : stores) {
TransactionStoreManager.LogOperation logOperation = store.getOperate();
SessionStorable sessionStorable = store.getSessionRequest();
switch (logOperation) {
case GLOBAL_ADD:
case GLOBAL_UPDATE: {
GlobalSession globalSession = (GlobalSession)sessionStorable;
if (globalSession.getTransactionId() == 0) {
LOGGER.error(
"Restore globalSession from file failed, the transactionId is zero , xid:" + globalSession
.getXid());
break;
}
if (removedGlobalBuffer.contains(globalSession.getXid())) {
break;
}
GlobalSession foundGlobalSession = sessionMap.get(globalSession.getXid());
if (foundGlobalSession == null) {
if (this.checkSessionStatus(globalSession)) {
sessionMap.put(globalSession.getXid(), globalSession);
} else {
removedGlobalBuffer.add(globalSession.getXid());
unhandledBranchBuffer.remove(globalSession.getXid());
}
} else {
if (this.checkSessionStatus(globalSession)) {
foundGlobalSession.setStatus(globalSession.getStatus());
} else {
sessionMap.remove(globalSession.getXid());
removedGlobalBuffer.add(globalSession.getXid());
unhandledBranchBuffer.remove(globalSession.getXid());
}
}
break;
}
case GLOBAL_REMOVE: {
GlobalSession globalSession = (GlobalSession)sessionStorable;
if (globalSession.getTransactionId() == 0) {
LOGGER.error(
"Restore globalSession from file failed, the transactionId is zero , xid:" + globalSession
.getXid());
break;
}
if (removedGlobalBuffer.contains(globalSession.getXid())) {
break;
}
if (sessionMap.remove(globalSession.getXid()) == null) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("GlobalSession To Be Removed Does Not Exists [" + globalSession.getXid() + "]");
}
}
removedGlobalBuffer.add(globalSession.getXid());
unhandledBranchBuffer.remove(globalSession.getXid());
break;
}
case BRANCH_ADD:
case BRANCH_UPDATE: {
BranchSession branchSession = (BranchSession)sessionStorable;
if (branchSession.getTransactionId() == 0) {
LOGGER.error(
"Restore branchSession from file failed, the transactionId is zero , xid:" + branchSession
.getXid());
break;
}
if (removedGlobalBuffer.contains(branchSession.getXid())) {
break;
}
GlobalSession foundGlobalSession = sessionMap.get(branchSession.getXid());
if (foundGlobalSession == null) {
unhandledBranchBuffer.computeIfAbsent(branchSession.getXid(), key -> new HashMap<>())
.put(branchSession.getBranchId(), branchSession);
} else {
BranchSession existingBranch = foundGlobalSession.getBranch(branchSession.getBranchId());
if (existingBranch == null) {
foundGlobalSession.add(branchSession);
} else {
existingBranch.setStatus(branchSession.getStatus());
}
}
break;
}
case BRANCH_REMOVE: {
BranchSession branchSession = (BranchSession)sessionStorable;
String xid = branchSession.getXid();
if (removedGlobalBuffer.contains(xid)) {
break;
}
long bid = branchSession.getBranchId();
if (branchSession.getTransactionId() == 0) {
LOGGER.error(
"Restore branchSession from file failed, the transactionId is zero , xid:" + branchSession
.getXid());
break;
}
GlobalSession found = sessionMap.get(xid);
if (found == null) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info(
"GlobalSession To Be Updated (Remove Branch) Does Not Exists [" + bid + "/" + xid
+ "]");
}
} else {
BranchSession theBranch = found.getBranch(bid);
if (theBranch == null) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("BranchSession To Be Updated Does Not Exists [" + bid + "/" + xid + "]");
}
} else {
found.remove(theBranch);
}
}
break;
}
default:
throw new ShouldNeverHappenException("Unknown Operation: " + logOperation);
}
}
}
@Override
public void destroy() {
transactionStoreManager.shutdown();
}
}

View File

@@ -1,651 +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.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;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;
import static io.seata.core.context.RootContext.MDC_KEY_BRANCH_ID;
/**
* The type File transaction store manager.
*
* @author slievrly
*/
public class FileTransactionStoreManager extends AbstractTransactionStoreManager
implements TransactionStoreManager, ReloadableStore {
private static final Logger LOGGER = LoggerFactory.getLogger(FileTransactionStoreManager.class);
private static final int MAX_THREAD_WRITE = 1;
private ExecutorService fileWriteExecutor;
private volatile boolean stopping = false;
private static final int MAX_SHUTDOWN_RETRY = 3;
private static final int SHUTDOWN_CHECK_INTERVAL = 1 * 1000;
private static final int MAX_WRITE_RETRY = 5;
private static final String HIS_DATA_FILENAME_POSTFIX = ".1";
private static final AtomicLong FILE_TRX_NUM = new AtomicLong(0);
private static final AtomicLong FILE_FLUSH_NUM = new AtomicLong(0);
private static final int MARK_SIZE = 4;
private static final int MAX_WAIT_TIME_MILLS = 2 * 1000;
private static final int MAX_FLUSH_TIME_MILLS = 2 * 1000;
private static final int MAX_FLUSH_NUM = 10;
private static final int PER_FILE_BLOCK_SIZE = 65535 * 8;
private static final long MAX_TRX_TIMEOUT_MILLS = 30 * 60 * 1000;
private static volatile long trxStartTimeMills = System.currentTimeMillis();
private File currDataFile;
private RandomAccessFile currRaf;
private FileChannel currFileChannel;
private long recoverCurrOffset = 0;
private long recoverHisOffset = 0;
private SessionManager sessionManager;
private String currFullFileName;
private String hisFullFileName;
private WriteDataFileRunnable writeDataFileRunnable;
private ReentrantLock writeSessionLock = new ReentrantLock();
private volatile long lastModifiedTime;
private static final int MAX_WRITE_BUFFER_SIZE = StoreConfig.getFileWriteBufferCacheSize();
private final ByteBuffer writeBuffer = ByteBuffer.allocateDirect(MAX_WRITE_BUFFER_SIZE);
private static final FlushDiskMode FLUSH_DISK_MODE = StoreConfig.getFlushDiskMode();
private static final int MAX_WAIT_FOR_FLUSH_TIME_MILLS = 2 * 1000;
private static final int MAX_WAIT_FOR_CLOSE_TIME_MILLS = 2 * 1000;
private static final int INT_BYTE_SIZE = 4;
/**
* Instantiates a new File transaction store manager.
*
* @param fullFileName the dir path
* @param sessionManager the session manager
* @throws IOException the io exception
*/
public FileTransactionStoreManager(String fullFileName, SessionManager sessionManager) throws IOException {
initFile(fullFileName);
fileWriteExecutor = new ThreadPoolExecutor(MAX_THREAD_WRITE, MAX_THREAD_WRITE, Integer.MAX_VALUE,
TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(),
new NamedThreadFactory("fileTransactionStore", MAX_THREAD_WRITE, true));
writeDataFileRunnable = new WriteDataFileRunnable();
fileWriteExecutor.submit(writeDataFileRunnable);
this.sessionManager = sessionManager;
}
private void initFile(String fullFileName) throws IOException {
this.currFullFileName = fullFileName;
this.hisFullFileName = fullFileName + HIS_DATA_FILENAME_POSTFIX;
try {
currDataFile = new File(currFullFileName);
if (!currDataFile.exists()) {
// create parent dir first
if (currDataFile.getParentFile() != null && !currDataFile.getParentFile().exists()) {
currDataFile.getParentFile().mkdirs();
}
currDataFile.createNewFile();
trxStartTimeMills = System.currentTimeMillis();
} else {
trxStartTimeMills = currDataFile.lastModified();
}
lastModifiedTime = System.currentTimeMillis();
currRaf = new RandomAccessFile(currDataFile, "rw");
currRaf.seek(currDataFile.length());
currFileChannel = currRaf.getChannel();
} catch (IOException exx) {
LOGGER.error("init file error,{}", exx.getMessage(), exx);
throw exx;
}
}
@Override
public boolean writeSession(LogOperation logOperation, SessionStorable session) {
long curFileTrxNum;
writeSessionLock.lock();
try {
if (!writeDataFile(new TransactionWriteStore(session, logOperation).encode())) {
return false;
}
lastModifiedTime = System.currentTimeMillis();
curFileTrxNum = FILE_TRX_NUM.incrementAndGet();
if (curFileTrxNum % PER_FILE_BLOCK_SIZE == 0
&& (System.currentTimeMillis() - trxStartTimeMills) > MAX_TRX_TIMEOUT_MILLS) {
return saveHistory();
}
} catch (Exception exx) {
LOGGER.error("writeSession error, {}", exx.getMessage(), exx);
return false;
} finally {
writeSessionLock.unlock();
}
flushDisk(curFileTrxNum, currFileChannel);
return true;
}
private void flushDisk(long curFileNum, FileChannel currFileChannel) {
if (FLUSH_DISK_MODE == FlushDiskMode.SYNC_MODEL) {
SyncFlushRequest syncFlushRequest = new SyncFlushRequest(curFileNum, currFileChannel);
writeDataFileRunnable.putRequest(syncFlushRequest);
syncFlushRequest.waitForFlush(MAX_WAIT_FOR_FLUSH_TIME_MILLS);
} else {
writeDataFileRunnable.putRequest(new AsyncFlushRequest(curFileNum, currFileChannel));
}
}
/**
* get all overTimeSessionStorables
* merge write file
*
* @throws IOException
*/
private boolean saveHistory() throws IOException {
boolean result;
try {
result = findTimeoutAndSave();
CloseFileRequest request = new CloseFileRequest(currFileChannel, currRaf);
writeDataFileRunnable.putRequest(request);
request.waitForClose(MAX_WAIT_FOR_CLOSE_TIME_MILLS);
Files.move(currDataFile.toPath(), new File(hisFullFileName).toPath(), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException exx) {
LOGGER.error("save history data file error, {}", exx.getMessage(), exx);
result = false;
} finally {
initFile(currFullFileName);
}
return result;
}
private boolean writeDataFrame(byte[] data) {
if (data == null || data.length <= 0) {
return true;
}
int dataLength = data.length;
int bufferRemainingSize = writeBuffer.remaining();
if (bufferRemainingSize <= INT_BYTE_SIZE) {
if (!flushWriteBuffer(writeBuffer)) {
return false;
}
}
bufferRemainingSize = writeBuffer.remaining();
if (bufferRemainingSize <= INT_BYTE_SIZE) {
throw new IllegalStateException(
String.format("Write buffer remaining size %d was too small", bufferRemainingSize));
}
writeBuffer.putInt(dataLength);
bufferRemainingSize = writeBuffer.remaining();
int dataPos = 0;
while (dataPos < dataLength) {
int dataLengthToWrite = dataLength - dataPos;
dataLengthToWrite = Math.min(dataLengthToWrite, bufferRemainingSize);
writeBuffer.put(data, dataPos, dataLengthToWrite);
bufferRemainingSize = writeBuffer.remaining();
if (bufferRemainingSize == 0) {
if (!flushWriteBuffer(writeBuffer)) {
return false;
}
bufferRemainingSize = writeBuffer.remaining();
}
dataPos += dataLengthToWrite;
}
return true;
}
private boolean flushWriteBuffer(ByteBuffer writeBuffer) {
writeBuffer.flip();
if (!writeDataFileByBuffer(writeBuffer)) {
return false;
}
writeBuffer.clear();
return true;
}
private boolean findTimeoutAndSave() throws IOException {
List<GlobalSession> globalSessionsOverMaxTimeout = sessionManager.findGlobalSessions(
new SessionCondition(MAX_TRX_TIMEOUT_MILLS));
if (CollectionUtils.isEmpty(globalSessionsOverMaxTimeout)) {
return true;
}
for (GlobalSession globalSession : globalSessionsOverMaxTimeout) {
TransactionWriteStore globalWriteStore = new TransactionWriteStore(globalSession, LogOperation.GLOBAL_ADD);
byte[] data = globalWriteStore.encode();
if (!writeDataFrame(data)) {
return false;
}
List<BranchSession> branchSessIonsOverMaXTimeout = globalSession.getSortedBranches();
if (branchSessIonsOverMaXTimeout != null) {
for (BranchSession branchSession : branchSessIonsOverMaXTimeout) {
try {
MDC.put(MDC_KEY_BRANCH_ID, String.valueOf(branchSession.getBranchId()));
TransactionWriteStore branchWriteStore = new TransactionWriteStore(branchSession,
LogOperation.BRANCH_ADD);
data = branchWriteStore.encode();
if (!writeDataFrame(data)) {
return false;
}
} finally {
MDC.remove(MDC_KEY_BRANCH_ID);
}
}
}
}
if (flushWriteBuffer(writeBuffer)) {
currFileChannel.force(false);
return true;
}
return false;
}
@Override
public GlobalSession readSession(String xid) {
throw new StoreException("unsupport for read from file, xid:" + xid);
}
@Override
public List<GlobalSession> readSession(SessionCondition sessionCondition) {
throw new StoreException("unsupport for read from file");
}
@Override
public void shutdown() {
if (fileWriteExecutor != null) {
fileWriteExecutor.shutdown();
stopping = true;
int retry = 0;
while (!fileWriteExecutor.isTerminated() && retry < MAX_SHUTDOWN_RETRY) {
++retry;
try {
Thread.sleep(SHUTDOWN_CHECK_INTERVAL);
} catch (InterruptedException ignore) {
}
}
if (retry >= MAX_SHUTDOWN_RETRY) {
fileWriteExecutor.shutdownNow();
}
}
try {
if (currFileChannel.isOpen()) {
currFileChannel.force(true);
}
} catch (IOException e) {
LOGGER.error("fileChannel force error: {}", e.getMessage(), e);
}
closeFile(currRaf);
}
@Override
public List<TransactionWriteStore> readWriteStore(int readSize, boolean isHistory) {
File file = null;
long currentOffset = 0;
if (isHistory) {
file = new File(hisFullFileName);
currentOffset = recoverHisOffset;
} else {
file = new File(currFullFileName);
currentOffset = recoverCurrOffset;
}
if (file.exists()) {
return parseDataFile(file, readSize, currentOffset, isHistory);
}
return null;
}
@Override
public boolean hasRemaining(boolean isHistory) {
File file;
RandomAccessFile raf = null;
long currentOffset;
if (isHistory) {
file = new File(hisFullFileName);
currentOffset = recoverHisOffset;
} else {
file = new File(currFullFileName);
currentOffset = recoverCurrOffset;
}
try {
raf = new RandomAccessFile(file, "r");
return currentOffset < raf.length();
} catch (IOException ignore) {
} finally {
closeFile(raf);
}
return false;
}
private List<TransactionWriteStore> parseDataFile(File file, int readSize, long currentOffset, boolean isHistory) {
List<TransactionWriteStore> transactionWriteStores = new ArrayList<>(readSize);
RandomAccessFile raf = null;
FileChannel fileChannel = null;
try {
raf = new RandomAccessFile(file, "r");
raf.seek(currentOffset);
fileChannel = raf.getChannel();
fileChannel.position(currentOffset);
long size = raf.length();
ByteBuffer buffSize = ByteBuffer.allocate(MARK_SIZE);
while (fileChannel.position() < size) {
try {
buffSize.clear();
int avilReadSize = fileChannel.read(buffSize);
if (avilReadSize != MARK_SIZE) {
break;
}
buffSize.flip();
int bodySize = buffSize.getInt();
byte[] byBody = new byte[bodySize];
ByteBuffer buffBody = ByteBuffer.wrap(byBody);
avilReadSize = fileChannel.read(buffBody);
if (avilReadSize != bodySize) {
break;
}
TransactionWriteStore writeStore = new TransactionWriteStore();
writeStore.decode(byBody);
transactionWriteStores.add(writeStore);
if (transactionWriteStores.size() == readSize) {
break;
}
} catch (Exception ex) {
LOGGER.error("decode data file error:{}", ex.getMessage(), ex);
break;
}
}
return transactionWriteStores;
} catch (IOException exx) {
LOGGER.error("parse data file error:{},file:{}", exx.getMessage(), file.getName(), exx);
return null;
} finally {
try {
if (fileChannel != null) {
if (isHistory) {
recoverHisOffset = fileChannel.position();
} else {
recoverCurrOffset = fileChannel.position();
}
}
closeFile(raf);
} catch (IOException exx) {
LOGGER.error("file close error{}", exx.getMessage(), exx);
}
}
}
private void closeFile(RandomAccessFile raf) {
try {
if (raf != null) {
raf.close();
raf = null;
}
} catch (IOException exx) {
LOGGER.error("file close error,{}", exx.getMessage(), exx);
}
}
private boolean writeDataFile(byte[] bs) {
if (bs == null || bs.length >= Integer.MAX_VALUE - 3) {
return false;
}
if (!writeDataFrame(bs)) {
return false;
}
return flushWriteBuffer(writeBuffer);
}
private boolean writeDataFileByBuffer(ByteBuffer byteBuffer) {
for (int retry = 0; retry < MAX_WRITE_RETRY; retry++) {
try {
while (byteBuffer.hasRemaining()) {
currFileChannel.write(byteBuffer);
}
return true;
} catch (Exception exx) {
LOGGER.error("write data file error:{}", exx.getMessage(), exx);
}
}
LOGGER.error("write dataFile failed,retry more than :{}", MAX_WRITE_RETRY);
return false;
}
interface StoreRequest {
}
abstract static class AbstractFlushRequest implements StoreRequest {
private final long curFileTrxNum;
private final FileChannel curFileChannel;
protected AbstractFlushRequest(long curFileTrxNum, FileChannel curFileChannel) {
this.curFileTrxNum = curFileTrxNum;
this.curFileChannel = curFileChannel;
}
public long getCurFileTrxNum() {
return curFileTrxNum;
}
public FileChannel getCurFileChannel() {
return curFileChannel;
}
}
class SyncFlushRequest extends AbstractFlushRequest {
private final CountDownLatch countDownLatch = new CountDownLatch(1);
public SyncFlushRequest(long curFileTrxNum, FileChannel curFileChannel) {
super(curFileTrxNum, curFileChannel);
}
public void wakeup() {
this.countDownLatch.countDown();
}
public void waitForFlush(long timeout) {
try {
this.countDownLatch.await(timeout, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
LOGGER.error("Interrupted", e);
}
}
}
class AsyncFlushRequest extends AbstractFlushRequest {
public AsyncFlushRequest(long curFileTrxNum, FileChannel curFileChannel) {
super(curFileTrxNum, curFileChannel);
}
}
static class CloseFileRequest implements StoreRequest {
private final CountDownLatch countDownLatch = new CountDownLatch(1);
private FileChannel fileChannel;
private RandomAccessFile file;
public CloseFileRequest(FileChannel fileChannel, RandomAccessFile file) {
this.fileChannel = fileChannel;
this.file = file;
}
public FileChannel getFileChannel() {
return fileChannel;
}
public RandomAccessFile getFile() {
return file;
}
public void wakeup() {
this.countDownLatch.countDown();
}
public void waitForClose(long timeout) {
try {
this.countDownLatch.await(timeout, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
LOGGER.error("Interrupted", e);
}
}
}
/**
* The type Write data file runnable.
*/
class WriteDataFileRunnable implements Runnable {
private LinkedBlockingQueue<StoreRequest> storeRequests = new LinkedBlockingQueue<>();
public void putRequest(final StoreRequest request) {
storeRequests.add(request);
}
@Override
public void run() {
while (!stopping) {
try {
StoreRequest storeRequest = storeRequests.poll(MAX_WAIT_TIME_MILLS, TimeUnit.MILLISECONDS);
handleStoreRequest(storeRequest);
} catch (Exception exx) {
LOGGER.error("write file error: {}", exx.getMessage(), exx);
}
}
handleRestRequest();
}
/**
* handle the rest requests when stopping is true
*/
private void handleRestRequest() {
int remainNums = storeRequests.size();
for (int i = 0; i < remainNums; i++) {
handleStoreRequest(storeRequests.poll());
}
}
private void handleStoreRequest(StoreRequest storeRequest) {
if (storeRequest == null) {
flushOnCondition(currFileChannel);
}
if (storeRequest instanceof SyncFlushRequest) {
syncFlush((SyncFlushRequest)storeRequest);
} else if (storeRequest instanceof AsyncFlushRequest) {
async((AsyncFlushRequest)storeRequest);
} else if (storeRequest instanceof CloseFileRequest) {
closeAndFlush((CloseFileRequest)storeRequest);
}
}
private void closeAndFlush(CloseFileRequest req) {
long diff = FILE_TRX_NUM.get() - FILE_FLUSH_NUM.get();
flush(req.getFileChannel());
FILE_FLUSH_NUM.addAndGet(diff);
closeFile(req.getFile());
req.wakeup();
}
private void async(AsyncFlushRequest req) {
flushOnCondition(req.getCurFileChannel());
}
private void syncFlush(SyncFlushRequest req) {
if (req.getCurFileTrxNum() > FILE_FLUSH_NUM.get()) {
long diff = FILE_TRX_NUM.get() - FILE_FLUSH_NUM.get();
flush(req.getCurFileChannel());
FILE_FLUSH_NUM.addAndGet(diff);
}
// notify
req.wakeup();
}
private void flushOnCondition(FileChannel fileChannel) {
if (FLUSH_DISK_MODE == FlushDiskMode.SYNC_MODEL) {
return;
}
long diff = FILE_TRX_NUM.get() - FILE_FLUSH_NUM.get();
if (diff == 0) {
return;
}
if (diff % MAX_FLUSH_NUM == 0 || System.currentTimeMillis() - lastModifiedTime > MAX_FLUSH_TIME_MILLS) {
flush(fileChannel);
FILE_FLUSH_NUM.addAndGet(diff);
}
}
private void flush(FileChannel fileChannel) {
try {
fileChannel.force(false);
} catch (IOException exx) {
LOGGER.error("flush error: {}", exx.getMessage(), exx);
}
}
}
}

View File

@@ -1,125 +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.storage.redis;
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 java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import static io.seata.common.DefaultValues.*;
/**
* @author funkye
*/
public class JedisPooledFactory {
/**
* The constant LOGGER.
*/
protected static final Logger LOGGER = LoggerFactory.getLogger(JedisPooledFactory.class);
private static volatile JedisPoolAbstract jedisPool = null;
private static final String HOST = "127.0.0.1";
private static final int PORT = 6379;
private static final int DATABASE = 0;
private static final int SENTINEL_HOST_NUMBER = 3;
private static final Configuration CONFIGURATION = ConfigurationFactory.getInstance();
/**
* get the RedisPool instance (singleton)
*
* @return redisPool
*/
public static JedisPoolAbstract getJedisPoolInstance(JedisPoolAbstract... jedisPools) {
if (jedisPool == null) {
synchronized (JedisPooledFactory.class) {
if (jedisPool == null) {
JedisPoolAbstract tempJedisPool = null;
if (jedisPools != null && jedisPools.length > 0) {
tempJedisPool = jedisPools[0];
} else {
String password = CONFIGURATION.getConfig(ConfigurationKeys.STORE_REDIS_PASSWORD);
if (StringUtils.isBlank(password)) {
password = null;
} else {
String publicKey = CONFIGURATION.getConfig(ConfigurationKeys.STORE_PUBLIC_KEY);
if (StringUtils.isNotBlank(publicKey)) {
try {
password = ConfigTools.publicDecrypt(password, publicKey);
} catch (Exception e) {
LOGGER.error("decryption failed,please confirm whether the ciphertext and secret key are correct! error msg: {}", e.getMessage());
}
}
}
JedisPoolConfig poolConfig = new JedisPoolConfig();
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)) {
String masterName = CONFIGURATION.getConfig(ConfigurationKeys.STORE_REDIS_SENTINEL_MASTERNAME);
if (StringUtils.isBlank(masterName)) {
throw new RedisException("The masterName is null in redis sentinel mode");
}
Set<String> sentinels = new HashSet<>(SENTINEL_HOST_NUMBER);
String[] sentinelHosts = CONFIGURATION.getConfig(ConfigurationKeys.STORE_REDIS_SENTINEL_HOST).split(",");
Arrays.asList(sentinelHosts).forEach(sentinelHost -> sentinels.add(sentinelHost));
tempJedisPool = new JedisSentinelPool(masterName, sentinels, poolConfig, 60000, password, CONFIGURATION.getInt(ConfigurationKeys.STORE_REDIS_DATABASE, DATABASE));
} else if (mode.equals(ConfigurationKeys.REDIS_SINGLE_MODE)) {
String host = CONFIGURATION.getConfig(ConfigurationKeys.STORE_REDIS_SINGLE_HOST);
host = StringUtils.isBlank(host) ? 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;
tempJedisPool = new JedisPool(poolConfig, host, port, 60000, password, CONFIGURATION.getInt(ConfigurationKeys.STORE_REDIS_DATABASE, DATABASE));
} else {
throw new RedisException("Configuration error of redis cluster mode");
}
}
if (LOGGER.isInfoEnabled()) {
LOGGER.info("initialization of the build redis connection pool is complete");
}
jedisPool = tempJedisPool;
}
}
}
return jedisPool;
}
/**
* get an instance of Jedis (connection) from the connection pool
*
* @return jedis
*/
public static Jedis getJedisInstance() {
return getJedisPoolInstance().getResource();
}
}

View File

@@ -1,85 +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.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;
}
}
}

View File

@@ -1,66 +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.storage.redis.lock;
import io.seata.common.executor.Initialize;
import io.seata.common.loader.LoadLevel;
import io.seata.core.exception.TransactionException;
import io.seata.core.lock.Locker;
import io.seata.server.lock.AbstractLockManager;
import io.seata.server.session.BranchSession;
import io.seata.server.session.GlobalSession;
/**
* @author funkye
*/
@LoadLevel(name = "redis")
public class RedisLockManager extends AbstractLockManager implements Initialize {
/**
* The locker.
*/
private Locker locker;
@Override
public void init() {
locker = new RedisLocker();
}
@Override
public Locker getLocker(BranchSession branchSession) {
return locker;
}
@Override
public boolean releaseLock(BranchSession branchSession) throws TransactionException {
try {
return getLocker().releaseLock(branchSession.getXid(), branchSession.getBranchId());
} catch (Exception t) {
LOGGER.error("unLock error, xid {}, branchId:{}", branchSession.getXid(), branchSession.getBranchId(), t);
return false;
}
}
@Override
public boolean releaseGlobalSessionLock(GlobalSession globalSession) throws TransactionException {
try {
return getLocker().releaseLock(globalSession.getXid());
} catch (Exception t) {
LOGGER.error("unLock globalSession error, xid:{}", globalSession.getXid(), t);
return false;
}
}
}

View File

@@ -1,385 +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.storage.redis.lock;
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
*
* @author funkye
* @author wangzhongxiang
*/
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 XID = "xid";
private static final String TRANSACTION_ID = "transactionId";
private static final String BRANCH_ID = "branchId";
private static final String RESOURCE_ID = "resourceId";
private static final String TABLE_NAME = "tableName";
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);
if (!skipCheckLock) {
Pipeline pipeline1 = jedis.pipelined();
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++) {
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));
} else {
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.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);
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));
}
}
// 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
public boolean releaseLock(List<RowLock> rowLocks) {
if (CollectionUtils.isEmpty(rowLocks)) {
return true;
}
String currentXid = rowLocks.get(0).getXid();
Long branchId = rowLocks.get(0).getBranchId();
List<LockDO> needReleaseLocks = convertToLockDO(rowLocks);
String[] needReleaseKeys = new String[needReleaseLocks.size()];
for (int i = 0; i < needReleaseLocks.size(); i++) {
needReleaseKeys[i] = buildLockKey(needReleaseLocks.get(i).getRowKey());
}
try (Jedis jedis = JedisPooledFactory.getJedisInstance(); Pipeline pipelined = jedis.pipelined()) {
pipelined.del(needReleaseKeys);
pipelined.hdel(buildXidLockKey(currentXid), branchId.toString());
pipelined.sync();
return true;
}
}
@Override
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()) {
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();
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();
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)) {
String[] keys = rowKeyStr.split(ROW_LOCK_KEY_SPLIT_CHAR);
pipelined.del(keys);
} else {
pipelined.del(rowKeyStr);
}
}
});
pipelined.sync();
}
return true;
}
}
private String buildXidLockKey(String xid) {
return DEFAULT_REDIS_SEATA_GLOBAL_LOCK_PREFIX + xid;
}
private String buildLockKey(String rowKey) {
return DEFAULT_REDIS_SEATA_ROW_LOCK_PREFIX + rowKey;
}
}

View File

@@ -1,189 +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.storage.redis.session;
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.*;
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
*/
@LoadLevel(name = "redis", scope = Scope.PROTOTYPE)
public class RedisSessionManager extends AbstractSessionManager
implements Initialize {
/**
* The constant LOGGER.
*/
protected static final Logger LOGGER = LoggerFactory.getLogger(RedisSessionManager.class);
/**
* The Task name.
*/
protected String taskName;
/**
* Instantiates a new Data base session manager.
*/
public RedisSessionManager() {
super();
}
/**
* Instantiates a new Data base session manager.
*
* @param name
* the name
*/
public RedisSessionManager(String name) {
super();
this.taskName = name;
}
@Override
public void init() {
transactionStoreManager = RedisTransactionStoreManager.getInstance();
}
@Override
public void addGlobalSession(GlobalSession session) throws TransactionException {
if (StringUtils.isBlank(taskName)) {
boolean ret = transactionStoreManager.writeSession(LogOperation.GLOBAL_ADD, session);
if (!ret) {
throw new StoreException("addGlobalSession failed.");
}
} else {
boolean ret = transactionStoreManager.writeSession(LogOperation.GLOBAL_UPDATE, session);
if (!ret) {
throw new StoreException("addGlobalSession failed.");
}
}
}
@Override
public void updateGlobalSessionStatus(GlobalSession session, GlobalStatus status) throws TransactionException {
if (!StringUtils.isEmpty(taskName)) {
return;
}
session.setStatus(status);
boolean ret = transactionStoreManager.writeSession(LogOperation.GLOBAL_UPDATE, session);
if (!ret) {
throw new StoreException("updateGlobalSessionStatus failed.");
}
}
/**
* remove globalSession 1. rootSessionManager remove normal globalSession 2. retryCommitSessionManager and
* retryRollbackSessionManager remove retry expired globalSession
*
* @param session
* the session
* @throws TransactionException
*/
@Override
public void removeGlobalSession(GlobalSession session) throws TransactionException {
boolean ret = transactionStoreManager.writeSession(LogOperation.GLOBAL_REMOVE, session);
if (!ret) {
throw new StoreException("removeGlobalSession failed.");
}
}
@Override
public void addBranchSession(GlobalSession globalSession, BranchSession session) throws TransactionException {
if (!StringUtils.isEmpty(taskName)) {
return;
}
boolean ret = transactionStoreManager.writeSession(LogOperation.BRANCH_ADD, session);
if (!ret) {
throw new StoreException("addBranchSession failed.");
}
}
@Override
public void updateBranchSessionStatus(BranchSession session, BranchStatus status) throws TransactionException {
if (!StringUtils.isEmpty(taskName)) {
return;
}
boolean ret = transactionStoreManager.writeSession(LogOperation.BRANCH_UPDATE, session);
if (!ret) {
throw new StoreException("updateBranchSessionStatus failed.");
}
}
@Override
public void removeBranchSession(GlobalSession globalSession, BranchSession session) throws TransactionException {
if (!StringUtils.isEmpty(taskName)) {
return;
}
boolean ret = transactionStoreManager.writeSession(LogOperation.BRANCH_REMOVE, session);
if (!ret) {
throw new StoreException("removeBranchSession failed.");
}
}
@Override
public GlobalSession findGlobalSession(String xid) {
return this.findGlobalSession(xid, true);
}
@Override
public GlobalSession findGlobalSession(String xid, boolean withBranchSessions) {
return transactionStoreManager.readSession(xid, withBranchSessions);
}
@Override
public Collection<GlobalSession> allSessions() {
// get by taskName
if (SessionHolder.ASYNC_COMMITTING_SESSION_MANAGER_NAME.equalsIgnoreCase(taskName)) {
return findGlobalSessions(new SessionCondition(GlobalStatus.AsyncCommitting));
} else if (SessionHolder.RETRY_COMMITTING_SESSION_MANAGER_NAME.equalsIgnoreCase(taskName)) {
return findGlobalSessions(new SessionCondition(GlobalStatus.CommitRetrying, GlobalStatus.Committing));
} else if (SessionHolder.RETRY_ROLLBACKING_SESSION_MANAGER_NAME.equalsIgnoreCase(taskName)) {
return findGlobalSessions(new SessionCondition(GlobalStatus.RollbackRetrying, GlobalStatus.Rollbacking,
GlobalStatus.TimeoutRollbacking, GlobalStatus.TimeoutRollbackRetrying));
} else {
// all data
return findGlobalSessions(new SessionCondition(GlobalStatus.UnKnown, GlobalStatus.Begin, GlobalStatus.Committing,
GlobalStatus.CommitRetrying, GlobalStatus.Rollbacking, GlobalStatus.RollbackRetrying, GlobalStatus.TimeoutRollbacking,
GlobalStatus.TimeoutRollbackRetrying, GlobalStatus.AsyncCommitting));
}
}
@Override
public List<GlobalSession> findGlobalSessions(SessionCondition condition) {
// nothing need to do
return transactionStoreManager.readSession(condition);
}
@Override
public <T> T lockAndExecute(GlobalSession globalSession, GlobalSession.LockCallable<T> lockCallable)
throws TransactionException {
return lockCallable.call();
}
}

View File

@@ -1,835 +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.storage.redis.store;
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.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 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 {
private static final Logger LOGGER = LoggerFactory.getLogger(RedisTransactionStoreManager.class);
/**the prefix of the branch transactions*/
private static final String REDIS_SEATA_BRANCHES_PREFIX = "SEATA_BRANCHES_";
/**the prefix of the branch transaction*/
private static final String REDIS_SEATA_BRANCH_PREFIX = "SEATA_BRANCH_";
/**the prefix of the global transaction*/
private static final String REDIS_SEATA_GLOBAL_PREFIX = "SEATA_GLOBAL_";
/**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.
*/
public static RedisTransactionStoreManager getInstance() {
if (instance == null) {
synchronized (RedisTransactionStoreManager.class) {
if (instance == null) {
instance = new RedisTransactionStoreManager();
}
}
}
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 (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());
}
}
/**
* Insert branch transaction
* @param branchTransactionDO
* @return the boolean
*/
private boolean insertBranchTransactionDO(BranchTransactionDO branchTransactionDO) {
String branchKey = buildBranchKey(branchTransactionDO.getBranchId());
String branchListKey = buildBranchListKeyByXid(branchTransactionDO.getXid());
try (Jedis jedis = JedisPooledFactory.getJedisInstance(); Pipeline pipelined = jedis.pipelined()) {
Date now = new Date();
branchTransactionDO.setGmtCreate(now);
branchTransactionDO.setGmtModified(now);
pipelined.hmset(branchKey, BeanUtils.objectToMap(branchTransactionDO));
pipelined.rpush(branchListKey, branchKey);
pipelined.sync();
return true;
} catch (Exception ex) {
throw new RedisException(ex);
}
}
/**
* Delete the branch transaction
* @param branchTransactionDO
* @return
*/
private boolean deleteBranchTransactionDO(BranchTransactionDO branchTransactionDO) {
String branchKey = buildBranchKey(branchTransactionDO.getBranchId());
try (Jedis jedis = JedisPooledFactory.getJedisInstance()) {
String xid = jedis.hget(branchKey, REDIS_KEY_BRANCH_XID);
if (StringUtils.isEmpty(xid)) {
return true;
}
String branchListKey = buildBranchListKeyByXid(branchTransactionDO.getXid());
try (Pipeline pipelined = jedis.pipelined()) {
pipelined.lrem(branchListKey, 0, branchKey);
pipelined.del(branchKey);
pipelined.sync();
}
return true;
} catch (Exception ex) {
throw new RedisException(ex);
}
}
/**
* Update the branch transaction
* @param branchTransactionDO
* @return
*/
private boolean updateBranchTransactionDO(BranchTransactionDO branchTransactionDO) {
String branchKey = buildBranchKey(branchTransactionDO.getBranchId());
try (Jedis jedis = JedisPooledFactory.getJedisInstance()) {
String previousBranchStatus = jedis.hget(branchKey, REDIS_KEY_BRANCH_STATUS);
if (StringUtils.isEmpty(previousBranchStatus)) {
throw new StoreException("Branch transaction is not exist, update branch transaction failed.");
}
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;
} catch (Exception ex) {
throw new RedisException(ex);
}
}
/**
* Insert the global transaction.
* @param globalTransactionDO
* @return
*/
private boolean insertGlobalTransactionDO(GlobalTransactionDO globalTransactionDO) {
String globalKey = buildGlobalKeyByTransactionId(globalTransactionDO.getTransactionId());
try (Jedis jedis = JedisPooledFactory.getJedisInstance(); Pipeline pipelined = jedis.pipelined()) {
Date now = new Date();
globalTransactionDO.setGmtCreate(now);
globalTransactionDO.setGmtModified(now);
pipelined.hmset(globalKey, BeanUtils.objectToMap(globalTransactionDO));
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;
} catch (Exception ex) {
throw new RedisException(ex);
}
}
/**
* Delete the global transaction.
* It will operate two parts:
* 1.delete the global session map
* 2.remove the xid from the global status list
* If the operate failed,the succeed operates will rollback
* @param globalTransactionDO
* @return
*/
private boolean deleteGlobalTransactionDO(GlobalTransactionDO globalTransactionDO) {
String globalKey = buildGlobalKeyByTransactionId(globalTransactionDO.getTransactionId());
try (Jedis jedis = JedisPooledFactory.getJedisInstance()) {
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;
}
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) {
throw new RedisException(ex);
}
}
/**
* Update the global transaction.
* It will update two parts:
* 1.the global session map
* 2.the global status list
* If the update failed,the succeed operates will rollback
* @param globalTransactionDO
* @return
*/
private boolean updateGlobalTransactionDO(GlobalTransactionDO globalTransactionDO) {
String xid = globalTransactionDO.getXid();
String globalKey = buildGlobalKeyByTransactionId(globalTransactionDO.getTransactionId());
try (Jedis jedis = JedisPooledFactory.getJedisInstance()) {
// Defensive watch to prevent other TC server operating concurrently,Fail fast
jedis.watch(globalKey);
List<String> statusAndGmtModified = jedis.hmget(globalKey, REDIS_KEY_GLOBAL_STATUS, REDIS_KEY_GLOBAL_GMT_MODIFIED);
String previousStatus = statusAndGmtModified.get(0);
if (StringUtils.isEmpty(previousStatus)) {
jedis.unwatch();
throw new StoreException("Global transaction is not exist, update global transaction failed.");
}
if (previousStatus.equals(String.valueOf(globalTransactionDO.getStatus()))) {
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();
Map<String,String> map = new HashMap<>(2);
map.put(REDIS_KEY_GLOBAL_STATUS,String.valueOf(globalTransactionDO.getStatus()));
map.put(REDIS_KEY_GLOBAL_GMT_MODIFIED,String.valueOf((new Date()).getTime()));
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);
if (OK.equalsIgnoreCase(hmset) && lrem > 0 && rpush > 0) {
return true;
} else {
// If someone failed, the succeed operations need rollback
if (OK.equalsIgnoreCase(hmset)) {
// Defensive watch to prevent other TC server operating concurrently,give up this operate
jedis.watch(globalKey);
String xid2 = jedis.hget(globalKey, REDIS_KEY_GLOBAL_XID);
if (StringUtils.isNotEmpty(xid2)) {
Map<String,String> mapPrevious = new HashMap<>(2,1);
mapPrevious.put(REDIS_KEY_GLOBAL_STATUS,previousStatus);
mapPrevious.put(REDIS_KEY_GLOBAL_GMT_MODIFIED,previousGmtModified);
Transaction multi2 = jedis.multi();
multi2.hmset(globalKey,mapPrevious);
multi2.exec();
}
}
if (lrem > 0) {
jedis.rpush(buildGlobalStatus(Integer.valueOf(previousStatus)), xid);
}
if (rpush > 0) {
jedis.lrem(buildGlobalStatus(globalTransactionDO.getStatus()), 0, xid);
}
return false;
}
} catch (Exception ex) {
throw new RedisException(ex);
}
}
/**
* Read session global session.
*
* @param xid the xid
* @param withBranchSessions the withBranchSessions
* @return the global session
*/
@Override
public GlobalSession readSession(String xid, boolean withBranchSessions) {
String transactionId = String.valueOf(XID.getTransactionId(xid));
String globalKey = buildGlobalKeyByTransactionId(transactionId);
try (Jedis jedis = JedisPooledFactory.getJedisInstance()) {
Map<String, String> map = jedis.hgetAll(globalKey);
if (CollectionUtils.isEmpty(map)) {
return null;
}
GlobalTransactionDO globalTransactionDO = (GlobalTransactionDO)BeanUtils.mapToObject(map, GlobalTransactionDO.class);
List<BranchTransactionDO> branchTransactionDOs = null;
if (withBranchSessions) {
branchTransactionDOs = this.readBranchSessionByXid(jedis, xid);
}
GlobalSession session = getGlobalSession(globalTransactionDO, branchTransactionDOs, withBranchSessions);
return session;
}
}
/**
* Read session global session.
*
* @param xid
* the xid
* @return the global session
*/
@Override
public GlobalSession readSession(String xid) {
return this.readSession(xid, true);
}
/**
* Read globalSession list by global status
*
* @param statuses the statuses
* @return the list
*/
@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;
}
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, withBranchSessions);
if (globalSession != null) {
globalSessions.add(globalSession);
}
});
}
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;
}
/**
* read the global session list by different condition
* @param sessionCondition the session condition
* @return the global sessions
*/
@Override
public List<GlobalSession> readSession(SessionCondition sessionCondition) {
List<GlobalSession> globalSessions = new ArrayList<>();
if (StringUtils.isNotEmpty(sessionCondition.getXid())) {
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(), !sessionCondition.isLazyLoadBranch());
if (globalSession != null) {
globalSessions.add(globalSession);
}
return globalSessions;
} else if (CollectionUtils.isNotEmpty(sessionCondition.getStatuses())) {
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, boolean withBranchSessions) {
GlobalSession globalSession = SessionConverter.convertGlobalSession(globalTransactionDO, !withBranchSessions);
if (CollectionUtils.isNotEmpty(branchTransactionDOs)) {
for (BranchTransactionDO branchTransactionDO : branchTransactionDOs) {
globalSession.add(SessionConverter.convertBranchSession(branchTransactionDO));
}
}
return globalSession;
}
/**
* read the global session by transactionId
* @param transactionId the transaction id
* @param withBranchSessions if read branch sessions
* @return the global session
*/
private GlobalSession readSessionByTransactionId(String transactionId, boolean withBranchSessions) {
String globalKey = buildGlobalKeyByTransactionId(transactionId);
String xid = null;
try (Jedis jedis = JedisPooledFactory.getJedisInstance()) {
Map<String, String> map = jedis.hgetAll(globalKey);
if (CollectionUtils.isEmpty(map)) {
return null;
}
GlobalTransactionDO globalTransactionDO = (GlobalTransactionDO)BeanUtils.mapToObject(map, GlobalTransactionDO.class);
if (globalTransactionDO != null) {
xid = globalTransactionDO.getXid();
}
List<BranchTransactionDO> branchTransactionDOs = new ArrayList<>();
if (withBranchSessions) {
branchTransactionDOs = this.readBranchSessionByXid(jedis, xid);
}
return getGlobalSession(globalTransactionDO, branchTransactionDOs, withBranchSessions);
}
}
/**
* Read the branch session list by xid
* @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 = lRange(jedis, branchListKey);
if (CollectionUtils.isNotEmpty(branchKeys)) {
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)) {
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;
}
private String buildGlobalKeyByTransactionId(Object transactionId) {
return REDIS_SEATA_GLOBAL_PREFIX + transactionId;
}
private String buildBranchKey(Long branchId) {
return REDIS_SEATA_BRANCH_PREFIX + branchId;
}
private String buildGlobalStatus(Integer status) {
return REDIS_SEATA_STATUS_PREFIX + status;
}
/**
* Sets log query limit.
*
* @param logQueryLimit the log query limit
*/
public void setLogQueryLimit(int logQueryLimit) {
this.logQueryLimit = logQueryLimit;
}
}

View File

@@ -1,60 +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.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;
/**
* The type Abstract transaction store manager.
*
* @author zhangsen
*/
public abstract class AbstractTransactionStoreManager implements TransactionStoreManager {
@Override
public GlobalSession readSession(String xid) {
return null;
}
@Override
public GlobalSession readSession(String xid, boolean withBranchSessions) {
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 Collections.emptyList();
}
@Override
public void shutdown() {
}
}

View File

@@ -1,58 +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.store;
import io.seata.common.loader.LoadLevel;
import io.seata.core.store.db.AbstractDataSourceProvider;
import org.apache.commons.dbcp2.BasicDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
/**
* The dbcp datasource provider
* @author zhangsen
* @author ggndnn
* @author will
*/
@LoadLevel(name = "dbcp")
public class DbcpDataSourceProvider extends AbstractDataSourceProvider {
@Override
public DataSource generate() {
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName(getDriverClassName());
// DriverClassLoader works if upgrade commons-dbcp to at least 1.3.1.
// https://issues.apache.org/jira/browse/DBCP-333
ds.setDriverClassLoader(getDriverClassLoader());
ds.setUrl(getUrl());
ds.setUsername(getUser());
ds.setPassword(getPassword());
ds.setInitialSize(getMinConn());
ds.setMaxTotal(getMaxConn());
ds.setMinIdle(getMinConn());
ds.setMaxIdle(getMinConn());
ds.setMaxWaitMillis(getMaxWait());
ds.setTimeBetweenEvictionRunsMillis(120000);
ds.setNumTestsPerEvictionRun(1);
ds.setTestWhileIdle(true);
ds.setValidationQuery(getValidationQuery(getDBType()));
ds.setConnectionProperties("useUnicode=yes;characterEncoding=utf8;socketTimeout=5000;connectTimeout=500");
ds.setDefaultTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
return ds;
}
}

View File

@@ -1,59 +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.store;
import com.alibaba.druid.pool.DruidDataSource;
import io.seata.common.loader.LoadLevel;
import io.seata.core.store.db.AbstractDataSourceProvider;
import javax.sql.DataSource;
import java.sql.Connection;
/**
* The druid datasource provider
* @author zhangsen
* @author ggndnn
* @author will
*/
@LoadLevel(name = "druid")
public class DruidDataSourceProvider extends AbstractDataSourceProvider {
@Override
public DataSource generate() {
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(getDriverClassName());
ds.setDriverClassLoader(getDriverClassLoader());
ds.setUrl(getUrl());
ds.setUsername(getUser());
ds.setPassword(getPassword());
ds.setInitialSize(getMinConn());
ds.setMaxActive(getMaxConn());
ds.setMinIdle(getMinConn());
ds.setMaxWait(getMaxWait());
ds.setTimeBetweenEvictionRunsMillis(120000);
ds.setMinEvictableIdleTimeMillis(300000);
ds.setTestWhileIdle(true);
ds.setTestOnBorrow(false);
ds.setPoolPreparedStatements(true);
ds.setMaxPoolPreparedStatementPerConnectionSize(20);
ds.setValidationQuery(getValidationQuery(getDBType()));
ds.setDefaultAutoCommit(true);
// fix issue 5030
ds.setUseOracleImplicitCache(false);
ds.setDefaultTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
return ds;
}
}

View File

@@ -1,62 +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.store;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.util.IsolationLevel;
import io.seata.common.loader.LoadLevel;
import io.seata.core.store.db.AbstractDataSourceProvider;
import javax.sql.DataSource;
import java.util.Properties;
/**
* The hikari datasource provider
* @author diguage
* @author will
*/
@LoadLevel(name = "hikari")
public class HikariDataSourceProvider extends AbstractDataSourceProvider {
@Override
public DataSource generate() {
Properties properties = new Properties();
properties.setProperty("dataSource.cachePrepStmts", "true");
properties.setProperty("dataSource.prepStmtCacheSize", "250");
properties.setProperty("dataSource.prepStmtCacheSqlLimit", "2048");
properties.setProperty("dataSource.useServerPrepStmts", "true");
properties.setProperty("dataSource.useLocalSessionState", "true");
properties.setProperty("dataSource.rewriteBatchedStatements", "true");
properties.setProperty("dataSource.cacheResultSetMetadata", "true");
properties.setProperty("dataSource.cacheServerConfiguration", "true");
properties.setProperty("dataSource.elideSetAutoCommits", "true");
properties.setProperty("dataSource.maintainTimeStats", "false");
HikariConfig config = new HikariConfig(properties);
config.setDriverClassName(getDriverClassName());
config.setJdbcUrl(getUrl());
config.setUsername(getUser());
config.setPassword(getPassword());
config.setMaximumPoolSize(getMaxConn());
config.setMinimumIdle(getMinConn());
config.setAutoCommit(true);
config.setConnectionTimeout(getMaxWait());
config.setInitializationFailTimeout(-1);
config.setTransactionIsolation(IsolationLevel.TRANSACTION_READ_COMMITTED.name());
return new HikariDataSource(config);
}
}

View File

@@ -1,38 +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.store;
/**
* The interface Session storable.
*
* @author slievrly
*/
public interface SessionStorable {
/**
* Encode byte [ ].
*
* @return the byte [ ]
*/
byte[] encode();
/**
* Decode.
*
* @param src the src
*/
void decode(byte[] src);
}

View File

@@ -1,248 +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.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;
/**
* @author lizhao
*/
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.
*/
private static final int DEFAULT_MAX_BRANCH_SESSION_SIZE = 1024 * 16;
/**
* Default 512b.
*/
private static final int DEFAULT_MAX_GLOBAL_SESSION_SIZE = 512;
/**
* Default 16kb.
*/
private static final int DEFAULT_WRITE_BUFFER_SIZE = 1024 * 16;
public static int getMaxBranchSessionSize() {
return CONFIGURATION.getInt(STORE_FILE_PREFIX + "maxBranchSessionSize", DEFAULT_MAX_BRANCH_SESSION_SIZE);
}
public static int getMaxGlobalSessionSize() {
return CONFIGURATION.getInt(STORE_FILE_PREFIX + "maxGlobalSessionSize", DEFAULT_MAX_GLOBAL_SESSION_SIZE);
}
public static int getFileWriteBufferCacheSize() {
return CONFIGURATION.getInt(STORE_FILE_PREFIX + "fileWriteBufferCacheSize", DEFAULT_WRITE_BUFFER_SIZE);
}
public static FlushDiskMode getFlushDiskMode() {
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);
}
}
}

View File

@@ -1,148 +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.store;
import io.seata.core.model.GlobalStatus;
import io.seata.server.session.GlobalSession;
import io.seata.server.session.SessionCondition;
import java.util.List;
/**
* The interface Transaction store manager.
*
* @author slievrly
*/
public interface TransactionStoreManager {
/**
* Write session boolean.
*
* @param logOperation the log operation
* @param session the session
* @return the boolean
*/
boolean writeSession(LogOperation logOperation, SessionStorable session);
/**
* Read global session global session.
*
* @param xid the xid
* @return the global session
*/
GlobalSession readSession(String xid);
/**
* Read session global session.
*
* @param xid the xid
* @param withBranchSessions the withBranchSessions
* @return the global session
*/
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
* @return the list
*/
List<GlobalSession> readSession(SessionCondition sessionCondition);
/**
* Shutdown.
*/
void shutdown();
/**
* The enum Log operation.
*/
enum LogOperation {
/**
* Global add log operation.
*/
GLOBAL_ADD((byte)1),
/**
* Global update log operation.
*/
GLOBAL_UPDATE((byte)2),
/**
* Global remove log operation.
*/
GLOBAL_REMOVE((byte)3),
/**
* Branch add log operation.
*/
BRANCH_ADD((byte)4),
/**
* Branch update log operation.
*/
BRANCH_UPDATE((byte)5),
/**
* Branch remove log operation.
*/
BRANCH_REMOVE((byte)6);
private byte code;
LogOperation(byte code) {
this.code = code;
}
/**
* Gets code.
*
* @return the code
*/
public byte getCode() {
return this.code;
}
/**
* Gets log operation by code.
*
* @param code the code
* @return the log operation by code
*/
public static LogOperation getLogOperationByCode(byte code) {
for (LogOperation temp : values()) {
if (temp.getCode() == code) {
return temp;
}
}
throw new IllegalArgumentException("Unknown LogOperation[" + code + "]");
}
}
}

View File

@@ -1,104 +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.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;
import io.seata.core.rpc.RemotingServer;
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;
/**
* The type at core.
*
* @author ph3636
*/
public class ATCore extends AbstractCore {
private final ObjectMapper objectMapper = new ObjectMapper();
public ATCore(RemotingServer remotingServer) {
super(remotingServer);
}
@Override
public BranchType getHandleBranchType() {
return BranchType.AT;
}
@Override
protected void branchSessionLock(GlobalSession globalSession, BranchSession branchSession)
throws TransactionException {
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) {
Throwable cause = e.getCause();
if (cause instanceof BranchTransactionException) {
throw new BranchTransactionException(((BranchTransactionException)cause).getCode(),
String.format("Global lock acquire failed xid = %s branchId = %s", globalSession.getXid(),
branchSession.getBranchId()));
}
throw e;
}
}
@Override
protected void branchSessionUnlock(BranchSession branchSession) throws TransactionException {
branchSession.unlock();
}
@Override
public boolean lockQuery(BranchType branchType, String resourceId, String xid, String lockKeys)
throws TransactionException {
return lockManager.isLockable(xid, resourceId, lockKeys);
}
}

View File

@@ -1,230 +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.transaction.saga;
import io.netty.channel.Channel;
import io.seata.common.util.CollectionUtils;
import io.seata.core.exception.GlobalTransactionException;
import io.seata.core.exception.TransactionException;
import io.seata.core.model.BranchStatus;
import io.seata.core.model.BranchType;
import io.seata.core.model.GlobalStatus;
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.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.
*
* @author ph3636
*/
public class SagaCore extends AbstractCore {
public SagaCore(RemotingServer remotingServer) {
super(remotingServer);
}
@Override
public BranchType getHandleBranchType() {
return BranchType.SAGA;
}
@Override
public void globalSessionStatusCheck(GlobalSession globalSession) throws GlobalTransactionException {
// SAGA type accept forward(retry) operation on timeout or commit fail, forward operation will register remaining branches
}
@Override
public BranchStatus branchCommitSend(BranchCommitRequest request, GlobalSession globalSession,
BranchSession branchSession) throws IOException, TimeoutException {
Map<String, Channel> channels = ChannelManager.getRmChannels();
if (CollectionUtils.isEmpty(channels)) {
LOGGER.error("Failed to commit SAGA global[" + globalSession.getXid() + ", RM channels is empty.");
return BranchStatus.PhaseTwo_CommitFailed_Retryable;
}
String sagaResourceId = getSagaResourceId(globalSession);
Channel sagaChannel = channels.get(sagaResourceId);
if (sagaChannel == null) {
LOGGER.error("Failed to commit SAGA global[" + globalSession.getXid()
+ ", cannot find channel by resourceId[" + sagaResourceId + "]");
return BranchStatus.PhaseTwo_CommitFailed_Retryable;
}
BranchCommitResponse response = (BranchCommitResponse) remotingServer.sendSyncRequest(sagaChannel, request);
return response.getBranchStatus();
}
@Override
public BranchStatus branchRollbackSend(BranchRollbackRequest request, GlobalSession globalSession,
BranchSession branchSession) throws IOException, TimeoutException {
Map<String, Channel> channels = ChannelManager.getRmChannels();
if (CollectionUtils.isEmpty(channels)) {
LOGGER.error("Failed to rollback SAGA global[" + globalSession.getXid() + ", RM channels is empty.");
return BranchStatus.PhaseTwo_RollbackFailed_Retryable;
}
String sagaResourceId = getSagaResourceId(globalSession);
Channel sagaChannel = channels.get(sagaResourceId);
if (sagaChannel == null) {
LOGGER.error("Failed to rollback SAGA global[" + globalSession.getXid()
+ ", cannot find channel by resourceId[" + sagaResourceId + "]");
return BranchStatus.PhaseTwo_RollbackFailed_Retryable;
}
BranchRollbackResponse response = (BranchRollbackResponse) remotingServer.sendSyncRequest(sagaChannel, request);
return response.getBranchStatus();
}
@Override
public boolean doGlobalCommit(GlobalSession globalSession, boolean retrying) throws TransactionException {
try {
BranchStatus branchStatus = branchCommit(globalSession, SessionHelper.newBranch(BranchType.SAGA,
globalSession.getXid(), -1, getSagaResourceId(globalSession), globalSession.getStatus().name()));
switch (branchStatus) {
case PhaseTwo_Committed:
SessionHelper.removeAllBranch(globalSession, !retrying);
LOGGER.info("Successfully committed SAGA global[" + globalSession.getXid() + "]");
break;
case PhaseTwo_Rollbacked:
LOGGER.info("Successfully rollbacked SAGA global[" + globalSession.getXid() + "]");
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,
globalSession.getXid());
SessionHolder.getRetryCommittingSessionManager().removeGlobalSession(globalSession);
globalSession.queueToRetryRollback();
return false;
case PhaseOne_Failed:
LOGGER.error("By [{}], finish SAGA global [{}]", branchStatus, globalSession.getXid());
SessionHelper.removeAllBranch(globalSession, !retrying);
globalSession.changeGlobalStatus(GlobalStatus.Finished);
globalSession.end();
return false;
case PhaseTwo_CommitFailed_Unretryable:
if (globalSession.canBeCommittedAsync()) {
LOGGER.error("By [{}], failed to commit SAGA global [{}]", branchStatus,
globalSession.getXid());
break;
} else {
SessionHelper.endCommitFailed(globalSession,retrying);
LOGGER.error("Finally, failed to commit SAGA global[{}]", globalSession.getXid());
return false;
}
default:
if (!retrying) {
globalSession.queueToRetryCommit();
} else {
LOGGER.error("Failed to commit SAGA global[{}], will retry later.", globalSession.getXid());
}
return false;
}
} catch (Exception ex) {
LOGGER.error("Failed to commit global[" + globalSession.getXid() + "]", ex);
if (!retrying) {
globalSession.queueToRetryRollback();
}
throw new TransactionException(ex);
}
return true;
}
@Override
public boolean doGlobalRollback(GlobalSession globalSession, boolean retrying) throws TransactionException {
try {
BranchStatus branchStatus = branchRollback(globalSession, SessionHelper.newBranch(BranchType.SAGA,
globalSession.getXid(), -1, getSagaResourceId(globalSession), globalSession.getStatus().name()));
switch (branchStatus) {
case PhaseTwo_Rollbacked:
SessionHelper.removeAllBranch(globalSession, !retrying);
LOGGER.info("Successfully rollbacked SAGA global[{}]",globalSession.getXid());
break;
case PhaseTwo_RollbackFailed_Unretryable:
SessionHelper.endRollbackFailed(globalSession, retrying);
LOGGER.error("Failed to rollback SAGA global[{}]", globalSession.getXid());
return false;
case PhaseTwo_CommitFailed_Retryable:
SessionHolder.getRetryRollbackingSessionManager().removeGlobalSession(globalSession);
globalSession.queueToRetryCommit();
LOGGER.warn("Retry by custom recover strategy [Forward] on timeout, SAGA global[{}]", globalSession.getXid());
return false;
default:
LOGGER.error("Failed to rollback SAGA global[{}]", globalSession.getXid());
if (!retrying) {
globalSession.queueToRetryRollback();
}
return false;
}
} catch (Exception ex) {
LOGGER.error("Failed to rollback global[{}]", globalSession.getXid(), ex);
if (!retrying) {
globalSession.queueToRetryRollback();
}
throw new TransactionException(ex);
}
return true;
}
@Override
public void doGlobalReport(GlobalSession globalSession, String xid, GlobalStatus globalStatus) throws TransactionException {
if (GlobalStatus.Committed.equals(globalStatus)) {
SessionHelper.removeAllBranch(globalSession, false);
SessionHelper.endCommitted(globalSession, false);
LOGGER.info("Global[{}] committed", globalSession.getXid());
} else if (GlobalStatus.Rollbacked.equals(globalStatus)
|| GlobalStatus.Finished.equals(globalStatus)) {
SessionHelper.removeAllBranch(globalSession, false);
SessionHelper.endRollbacked(globalSession, false);
LOGGER.info("Global[{}] rollbacked", globalSession.getXid());
} else {
globalSession.changeGlobalStatus(globalStatus);
LOGGER.info("Global[{}] reporting is successfully done. status[{}]", globalSession.getXid(), globalSession.getStatus());
if (GlobalStatus.RollbackRetrying.equals(globalStatus)
|| GlobalStatus.TimeoutRollbackRetrying.equals(globalStatus)
|| GlobalStatus.UnKnown.equals(globalStatus)) {
globalSession.queueToRetryRollback();
LOGGER.info("Global[{}] will retry rollback", globalSession.getXid());
} else if (GlobalStatus.CommitRetrying.equals(globalStatus)) {
globalSession.queueToRetryCommit();
LOGGER.info("Global[{}] will retry commit", globalSession.getXid());
}
}
}
/**
* get saga ResourceId
*
* @param globalSession the globalSession
* @return sagaResourceId
*/
private String getSagaResourceId(GlobalSession globalSession) {
return globalSession.getApplicationId() + "#" + globalSession.getTransactionServiceGroup();
}
}

View File

@@ -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.transaction.tcc;
import io.seata.core.model.BranchType;
import io.seata.core.rpc.RemotingServer;
import io.seata.server.coordinator.AbstractCore;
/**
* The type tcc core.
*
* @author ph3636
*/
public class TccCore extends AbstractCore {
public TccCore(RemotingServer remotingServer) {
super(remotingServer);
}
@Override
public BranchType getHandleBranchType() {
return BranchType.TCC;
}
}

View File

@@ -1,48 +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.transaction.xa;
import io.seata.core.exception.TransactionException;
import io.seata.core.model.BranchStatus;
import io.seata.core.model.BranchType;
import io.seata.core.rpc.RemotingServer;
import io.seata.server.coordinator.AbstractCore;
/**
* The type XA core.
*
* @author sharajava
*/
public class XACore extends AbstractCore {
public XACore(RemotingServer remotingServer) {
super(remotingServer);
}
@Override
public BranchType getHandleBranchType() {
return BranchType.XA;
}
@Override
public void branchReport(BranchType branchType, String xid, long branchId, BranchStatus status,
String applicationData) throws TransactionException {
super.branchReport(branchType, xid, branchId, status, applicationData);
if (BranchStatus.PhaseOne_Failed == status) {
}
}
}

View File

@@ -1,362 +0,0 @@
[
{
"condition": {
"typeReachable": "io.seata.core.rpc.RegisterCheckAuthHandler"
},
"name": "io.seata.server.auth.DefaultCheckAuthHandler",
"methods": [
{
"name": "<init>",
"parameterTypes": []
}
]
},
{
"condition": {
"typeReachable": "io.seata.core.store.db.DataSourceProvider"
},
"name": "io.seata.server.store.DbcpDataSourceProvider",
"methods": [
{
"name": "<init>",
"parameterTypes": []
}
]
},
{
"condition": {
"typeReachable": "io.seata.core.store.db.DataSourceProvider"
},
"name": "io.seata.server.store.DruidDataSourceProvider",
"methods": [
{
"name": "<init>",
"parameterTypes": []
}
]
},
{
"condition": {
"typeReachable": "io.seata.core.store.db.DataSourceProvider"
},
"name": "io.seata.server.store.HikariDataSourceProvider",
"methods": [
{
"name": "<init>",
"parameterTypes": []
}
]
},
{
"condition": {
"typeReachable": "io.seata.core.store.DistributedLocker"
},
"name": "io.seata.server.storage.redis.lock.RedisDistributedLocker",
"methods": [
{
"name": "<init>",
"parameterTypes": []
}
]
},
{
"condition": {
"typeReachable": "io.seata.core.store.DistributedLocker"
},
"name": "io.seata.server.storage.db.lock.DataBaseDistributedLocker",
"methods": [
{
"name": "<init>",
"parameterTypes": []
}
]
},
{
"condition": {
"typeReachable": "io.seata.server.coordinator.AbstractCore"
},
"name": "io.seata.server.transaction.at.ATCore",
"methods": [
{
"name": "<init>",
"parameterTypes": [
"io.seata.core.rpc.RemotingServer"
]
}
]
},
{
"condition": {
"typeReachable": "io.seata.server.coordinator.AbstractCore"
},
"name": "io.seata.server.transaction.tcc.TccCore",
"methods": [
{
"name": "<init>",
"parameterTypes": [
"io.seata.core.rpc.RemotingServer"
]
}
]
},
{
"condition": {
"typeReachable": "io.seata.server.coordinator.AbstractCore"
},
"name": "io.seata.server.transaction.saga.SagaCore",
"methods": [
{
"name": "<init>",
"parameterTypes": [
"io.seata.core.rpc.RemotingServer"
]
}
]
},
{
"condition": {
"typeReachable": "io.seata.server.coordinator.AbstractCore"
},
"name": "io.seata.server.transaction.xa.XACore",
"methods": [
{
"name": "<init>",
"parameterTypes": [
"io.seata.core.rpc.RemotingServer"
]
}
]
},
{
"condition": {
"typeReachable": "io.seata.server.lock.LockManager"
},
"name": "io.seata.server.storage.db.lock.DataBaseLockManager",
"methods": [
{
"name": "<init>",
"parameterTypes": []
}
]
},
{
"condition": {
"typeReachable": "io.seata.server.lock.LockManager"
},
"name": "io.seata.server.storage.file.lock.FileLockManager",
"methods": [
{
"name": "<init>",
"parameterTypes": []
}
]
},
{
"condition": {
"typeReachable": "io.seata.server.lock.LockManager"
},
"name": "io.seata.server.storage.redis.lock.RedisLockManager",
"methods": [
{
"name": "<init>",
"parameterTypes": []
}
]
},
{
"condition": {
"typeReachable": "io.seata.server.session.SessionManager"
},
"name": "io.seata.server.storage.file.session.FileSessionManager",
"methods": [
{
"name": "<init>",
"parameterTypes": [
"java.lang.String",
"java.lang.String"
]
}
]
},
{
"condition": {
"typeReachable": "io.seata.server.session.SessionManager"
},
"name": "io.seata.server.storage.db.session.DataBaseSessionManager",
"methods": [
{
"name": "<init>",
"parameterTypes": []
},
{
"name": "<init>",
"parameterTypes": [
"java.lang.String"
]
}
]
},
{
"condition": {
"typeReachable": "io.seata.server.session.SessionManager"
},
"name": "io.seata.server.storage.redis.session.RedisSessionManager",
"methods": [
{
"name": "<init>",
"parameterTypes": []
},
{
"name": "<init>",
"parameterTypes": [
"java.lang.String"
]
}
]
},
{
"condition": {
"typeReachable": "com.google.inject.internal.TypeConverterBindingProcessor"
},
"name": "java.lang.Integer",
"methods": [
{
"name": "parseInteger",
"parameterTypes": [
"java.lang.String"
]
}
]
},
{
"condition": {
"typeReachable": "com.google.inject.internal.TypeConverterBindingProcessor"
},
"name": "java.lang.Long",
"methods": [
{
"name": "parseLong",
"parameterTypes": [
"java.lang.String"
]
}
]
},
{
"condition": {
"typeReachable": "com.google.inject.internal.TypeConverterBindingProcessor"
},
"name": "java.lang.Boolean",
"methods": [
{
"name": "parseBoolean",
"parameterTypes": [
"java.lang.String"
]
}
]
},
{
"condition": {
"typeReachable": "com.google.inject.internal.TypeConverterBindingProcessor"
},
"name": "java.lang.Byte",
"methods": [
{
"name": "parseByte",
"parameterTypes": [
"java.lang.String"
]
}
]
},
{
"condition": {
"typeReachable": "com.google.inject.internal.TypeConverterBindingProcessor"
},
"name": "java.lang.Short",
"methods": [
{
"name": "parseShort",
"parameterTypes": [
"java.lang.String"
]
}
]
},
{
"condition": {
"typeReachable": "com.google.inject.internal.TypeConverterBindingProcessor"
},
"name": "java.lang.Float",
"methods": [
{
"name": "parseFloat",
"parameterTypes": [
"java.lang.String"
]
}
]
},
{
"condition": {
"typeReachable": "com.google.inject.internal.TypeConverterBindingProcessor"
},
"name": "java.lang.Double",
"methods": [
{
"name": "parseDouble",
"parameterTypes": [
"java.lang.String"
]
}
]
},
{
"condition": {
"typeReachable": "io.netty.channel.socket.nio.SelectorProviderUtil"
},
"name": "java.nio.channels.spi.SelectorProvider",
"methods": [
{
"name": "openServerSocketChannel",
"parameterTypes": [
"java.net.ProtocolFamily"
]
}
]
},
{
"condition": {
"typeReachable": "io.netty.channel.DefaultChannelConfig"
},
"name": "io.netty.buffer.ByteBufAllocator"
},
{
"condition": {
"typeReachable": "io.netty.channel.DefaultChannelConfig"
},
"name": "io.netty.buffer.ByteBufUtil"
},
{
"condition": {
"typeReachable": "io.netty.util.ResourceLeakDetector"
},
"name": "io.netty.buffer.AbstractByteBufAllocator",
"allDeclaredMethods": true
},
{
"condition": {
"typeReachable": "io.netty.util.ResourceLeakDetector"
},
"name": "io.netty.buffer.AdvancedLeakAwareByteBuf",
"allDeclaredMethods": true
},
{
"condition": {
"typeReachable": "io.netty.util.ResourceLeakDetector"
},
"name": "io.netty.util.ReferenceCountUtil",
"allDeclaredMethods": true
}
]

View File

@@ -1,21 +0,0 @@
{
"resources": {
"includes": [
{
"pattern": "\\Qlogback\/\\E.*"
},
{
"pattern": "\\Qlua\/redislocker\/redislock.lua\\E"
},
{
"pattern": "\\Qapplication.yml\\E"
},
{
"pattern": "\\Qbanner.txt\\E"
},
{
"pattern": "\\Qlogback-spring.xml\\E"
}
]
}
}

View File

@@ -1 +0,0 @@
io.seata.server.auth.DefaultCheckAuthHandler

Some files were not shown because too many files have changed in this diff Show More