diff --git a/pigx-visual/pigx-seata-server/pom.xml b/pigx-visual/pigx-seata-server/pom.xml index 26d7f66dc..9ca5fbb5d 100644 --- a/pigx-visual/pigx-seata-server/pom.xml +++ b/pigx-visual/pigx-seata-server/pom.xml @@ -28,111 +28,171 @@ pigx-seata-server - 1.4.2 + 1.6.1 6.5 0.2.0-RC2 1.72 27.0.1-jre + + + + + io.seata + seata-dependencies + ${seata.version} + pom + import + + + + + - - io.seata - seata-core - ${seata.version} - - - io.seata - seata-config-all - ${seata.version} - - - io.seata - seata-discovery-all - ${seata.version} - - - io.seata - seata-serializer-all - ${seata.version} - - - io.seata - seata-compressor-all - ${seata.version} - + + io.seata + seata-spring-autoconfigure-server + ${seata.version} + - - io.seata - seata-metrics-all - ${seata.version} - + + io.seata + seata-core + ${seata.version} + + + io.seata + seata-config-all + ${seata.version} + + + log4j + log4j + + + + + io.seata + seata-discovery-all + ${seata.version} + + + io.seata + seata-serializer-all + ${seata.version} + + + io.seata + seata-compressor-all + ${seata.version} + - - - com.alibaba - druid - - - org.apache.commons - commons-dbcp2 - - - com.zaxxer - HikariCP - - - com.h2database - h2 - - - mysql - mysql-connector-java - - - org.postgresql - postgresql - - - com.beust - jcommander - ${jcommander.version} - + + io.seata + seata-metrics-all + ${seata.version} + - - - com.google.guava - guava - ${guava.version} - + + io.seata + seata-console + ${seata.version} + - - - ch.qos.logback - logback-classic - + + + com.alibaba + druid + + + org.apache.commons + commons-dbcp2 + + + com.zaxxer + HikariCP + + + com.h2database + h2 + + + + mysql + mysql-connector-java + + + org.postgresql + postgresql + + - - - redis.clients - jedis - + + com.beust + jcommander + + + + + com.google.guava + guava + + + + + redis.clients + jedis + + + + com.github.microwww + redis-server + test + + + + com.alibaba + fastjson + + + + + ch.qos.logback + logback-classic + + + ch.qos.logback + logback-core + + + + net.logstash.logback + logstash-logback-encoder + + + com.github.danielwegener + logback-kafka-appender + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-undertow + - - com.alibaba - fastjson - - - net.logstash.logback - logstash-logback-encoder - ${logstash-logback-encoder.version} - - - com.github.danielwegener - logback-kafka-appender - ${kafka-appender.version} - diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/AbstractTCInboundHandler.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/AbstractTCInboundHandler.java index 4ba44b6af..fdf0d28cc 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/AbstractTCInboundHandler.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/AbstractTCInboundHandler.java @@ -20,25 +20,7 @@ import io.seata.core.exception.AbstractExceptionHandler; import io.seata.core.exception.TransactionException; import io.seata.core.exception.TransactionExceptionCode; import io.seata.core.model.GlobalStatus; -import io.seata.core.protocol.transaction.AbstractGlobalEndRequest; -import io.seata.core.protocol.transaction.AbstractGlobalEndResponse; -import io.seata.core.protocol.transaction.BranchRegisterRequest; -import io.seata.core.protocol.transaction.BranchRegisterResponse; -import io.seata.core.protocol.transaction.BranchReportRequest; -import io.seata.core.protocol.transaction.BranchReportResponse; -import io.seata.core.protocol.transaction.GlobalBeginRequest; -import io.seata.core.protocol.transaction.GlobalBeginResponse; -import io.seata.core.protocol.transaction.GlobalCommitRequest; -import io.seata.core.protocol.transaction.GlobalCommitResponse; -import io.seata.core.protocol.transaction.GlobalLockQueryRequest; -import io.seata.core.protocol.transaction.GlobalLockQueryResponse; -import io.seata.core.protocol.transaction.GlobalReportRequest; -import io.seata.core.protocol.transaction.GlobalReportResponse; -import io.seata.core.protocol.transaction.GlobalRollbackRequest; -import io.seata.core.protocol.transaction.GlobalRollbackResponse; -import io.seata.core.protocol.transaction.GlobalStatusRequest; -import io.seata.core.protocol.transaction.GlobalStatusResponse; -import io.seata.core.protocol.transaction.TCInboundHandler; +import io.seata.core.protocol.transaction.*; import io.seata.core.rpc.RpcContext; import io.seata.server.session.GlobalSession; import io.seata.server.session.SessionHolder; @@ -52,293 +34,294 @@ import org.slf4j.LoggerFactory; */ public abstract class AbstractTCInboundHandler extends AbstractExceptionHandler implements TCInboundHandler { - private static final Logger LOGGER = LoggerFactory.getLogger(AbstractTCInboundHandler.class); + 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() { - @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; - } + @Override + public GlobalBeginResponse handle(GlobalBeginRequest request, final RpcContext rpcContext) { + GlobalBeginResponse response = new GlobalBeginResponse(); + exceptionHandleTemplate(new AbstractCallback() { + @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; + /** + * 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() { - @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 GlobalCommitResponse handle(GlobalCommitRequest request, final RpcContext rpcContext) { + GlobalCommitResponse response = new GlobalCommitResponse(); + response.setGlobalStatus(GlobalStatus.Committing); + exceptionHandleTemplate(new AbstractCallback() { + @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 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); + } - @Override - public void onException(GlobalCommitRequest request, GlobalCommitResponse response, Exception rex) { - super.onException(request, response, rex); - checkTransactionStatus(request, response); - } - }, request, response); - return 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; + /** + * 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() { - @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 GlobalRollbackResponse handle(GlobalRollbackRequest request, final RpcContext rpcContext) { + GlobalRollbackResponse response = new GlobalRollbackResponse(); + response.setGlobalStatus(GlobalStatus.Rollbacking); + exceptionHandleTemplate(new AbstractCallback() { + @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 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; - } + @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; + /** + * 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() { - @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; - } + @Override + public BranchRegisterResponse handle(BranchRegisterRequest request, final RpcContext rpcContext) { + BranchRegisterResponse response = new BranchRegisterResponse(); + exceptionHandleTemplate(new AbstractCallback() { + @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; + /** + * 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() { - @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; - } + @Override + public BranchReportResponse handle(BranchReportRequest request, final RpcContext rpcContext) { + BranchReportResponse response = new BranchReportResponse(); + exceptionHandleTemplate(new AbstractCallback() { + @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; + /** + * 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() { - @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; - } + @Override + public GlobalLockQueryResponse handle(GlobalLockQueryRequest request, final RpcContext rpcContext) { + GlobalLockQueryResponse response = new GlobalLockQueryResponse(); + exceptionHandleTemplate(new AbstractCallback() { + @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; + /** + * 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() { - @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 GlobalStatusResponse handle(GlobalStatusRequest request, final RpcContext rpcContext) { + GlobalStatusResponse response = new GlobalStatusResponse(); + response.setGlobalStatus(GlobalStatus.UnKnown); + exceptionHandleTemplate(new AbstractCallback() { + @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 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; - } + @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; + /** + * 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() { - @Override - public void execute(GlobalReportRequest request, GlobalReportResponse response) - throws TransactionException { - doGlobalReport(request, response, rpcContext); - } - }, request, response); - return response; - } + @Override + public GlobalReportResponse handle(GlobalReportRequest request, final RpcContext rpcContext) { + GlobalReportResponse response = new GlobalReportResponse(); + response.setGlobalStatus(request.getGlobalStatus()); + exceptionHandleTemplate(new AbstractCallback() { + @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()); - } - } + /** + * 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()); + } + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/ParameterParser.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/ParameterParser.java index e3a533e70..55690fcaf 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/ParameterParser.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/ParameterParser.java @@ -19,12 +19,11 @@ import com.beust.jcommander.JCommander; import com.beust.jcommander.Parameter; import com.beust.jcommander.ParameterException; import io.seata.common.util.StringUtils; +import io.seata.config.Configuration; import io.seata.config.ConfigurationFactory; -import io.seata.core.constants.ConfigurationKeys; import io.seata.server.env.ContainerHelper; +import io.seata.server.store.StoreConfig; -import static io.seata.common.DefaultValues.SERVER_DEFAULT_PORT; -import static io.seata.common.DefaultValues.SERVER_DEFAULT_STORE_MODE; import static io.seata.config.ConfigurationFactory.ENV_PROPERTY_KEY; /** @@ -34,131 +33,168 @@ import static io.seata.config.ConfigurationFactory.ENV_PROPERTY_KEY; */ 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 String PROGRAM_NAME + = "sh seata-server.sh(for linux and mac) or cmd seata-server.bat(for windows)"; - @Parameter(names = "--help", help = true) - private boolean help; + private static final Configuration CONFIG = ConfigurationFactory.getInstance(); - @Parameter(names = { "--host", "-h" }, description = "The ip to register to registry center.", order = 1) - private String host; + @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; - @Parameter(names = { "--port", "-p" }, description = "The port to listen.", order = 2) - private int port = SERVER_DEFAULT_PORT; + /** + * Instantiates a new Parameter parser. + * + * @param args the args + */ + public ParameterParser(String... args) { + this.init(args); + } - @Parameter(names = { "--storeMode", "-m" }, description = "log store mode : file, db", order = 3) - private String storeMode; + /** + * 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); + } - @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; + 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); + } + } - /** - * Instantiates a new Parameter parser. - * @param args the args - */ - public ParameterParser(String[] args) { - this.init(args); - } + 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 init(String[] args) { - try { - if (ContainerHelper.isRunningInContainer()) { - this.seataEnv = ContainerHelper.getEnv(); - this.host = ContainerHelper.getHost(); - this.port = ContainerHelper.getPort(); - this.serverNode = ContainerHelper.getServerNode(); - this.storeMode = ContainerHelper.getStoreMode(); - } - else { - JCommander jCommander = JCommander.newBuilder().addObject(this).build(); - jCommander.parse(args); - if (help) { - jCommander.setProgramName(PROGRAM_NAME); - jCommander.usage(); - System.exit(0); - } - } - if (StringUtils.isNotBlank(seataEnv)) { - System.setProperty(ENV_PROPERTY_KEY, seataEnv); - } - if (StringUtils.isBlank(storeMode)) { - storeMode = ConfigurationFactory.getInstance().getConfig(ConfigurationKeys.STORE_MODE, - SERVER_DEFAULT_STORE_MODE); - } - } - catch (ParameterException e) { - printError(e); - } + 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; + } - private void printError(ParameterException e) { - System.err.println("Option error " + e.getMessage()); - e.getJCommander().setProgramName(PROGRAM_NAME); - e.usage(); - System.exit(0); - } + /** + * Gets port. + * + * @return the port + */ + public int getPort() { + return port; + } - /** - * Gets host. - * @return the host - */ - public String getHost() { - return host; - } + /** + * Gets store mode. + * + * @return the store mode + */ + public String getStoreMode() { + return storeMode; + } - /** - * Gets port. - * @return the port - */ - public int getPort() { - return port; - } + /** + * Gets lock store mode. + * + * @return the store mode + */ + public String getLockStoreMode() { + return lockStoreMode; + } - /** - * Gets store mode. - * @return the store mode - */ - public String getStoreMode() { - return storeMode; - } + /** + * Gets session store mode. + * + * @return the store mode + */ + public String getSessionStoreMode() { + return sessionStoreMode; + } - /** - * Is help boolean. - * @return the boolean - */ - public boolean isHelp() { - return help; - } + /** + * Is help boolean. + * + * @return the boolean + */ + public boolean isHelp() { + return help; + } - /** - * Gets server node. - * @return the server node - */ - public Long getServerNode() { - return serverNode; - } + /** + * 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; - } + /** + * 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); - } - } + /** + * Clean up. + */ + public void cleanUp() { + if (null != System.getProperty(ENV_PROPERTY_KEY)) { + System.clearProperty(ENV_PROPERTY_KEY); + } + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/PigxSeataServer.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/PigxSeataServer.java deleted file mode 100644 index 665175b6f..000000000 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/PigxSeataServer.java +++ /dev/null @@ -1,111 +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.core.constants.ConfigurationKeys; -import io.seata.core.rpc.ShutdownHook; -import io.seata.core.rpc.netty.NettyRemotingServer; -import io.seata.core.rpc.netty.NettyServerConfig; -import io.seata.server.coordinator.DefaultCoordinator; -import io.seata.server.env.ContainerHelper; -import io.seata.server.env.PortHelper; -import io.seata.server.metrics.MetricsManager; -import io.seata.server.session.SessionHolder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -/** - * The type Server. - * - * @author slievrly - */ -public class PigxSeataServer { - - /** - * The entry point of application. - * @param args the input arguments - * @throws IOException the io exception - */ - public static void main(String[] args) throws IOException { - // get port first, use to logback.xml - int port = PortHelper.getPort(args); - System.setProperty(ConfigurationKeys.SERVER_PORT, Integer.toString(port)); - - // create logger - final Logger logger = LoggerFactory.getLogger(PigxSeataServer.class); - if (ContainerHelper.isRunningInContainer()) { - logger.info("The server is running in container."); - } - - // 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(); - - System.setProperty(ConfigurationKeys.STORE_MODE, parameterParser.getStoreMode()); - - ThreadPoolExecutor workingThreads = new ThreadPoolExecutor(NettyServerConfig.getMinServerPoolSize(), - NettyServerConfig.getMaxServerPoolSize(), NettyServerConfig.getKeepAliveTime(), TimeUnit.SECONDS, - new LinkedBlockingQueue<>(NettyServerConfig.getMaxTaskQueueSize()), - new NamedThreadFactory("ServerHandlerThread", NettyServerConfig.getMaxServerPoolSize()), - new ThreadPoolExecutor.CallerRunsPolicy()); - - NettyRemotingServer nettyRemotingServer = new NettyRemotingServer(workingThreads); - // server port - nettyRemotingServer.setListenPort(parameterParser.getPort()); - UUIDGenerator.init(parameterParser.getServerNode()); - // log store mode : file, db, redis - SessionHolder.init(parameterParser.getStoreMode()); - - DefaultCoordinator coordinator = new DefaultCoordinator(nettyRemotingServer); - coordinator.init(); - nettyRemotingServer.setHandler(coordinator); - // register ShutdownHook - ShutdownHook.getInstance().addDisposable(coordinator); - ShutdownHook.getInstance().addDisposable(nettyRemotingServer); - - // 127.0.0.1 and 0.0.0.0 are not valid here. - if (NetUtil.isValidIp(parameterParser.getHost(), false)) { - XID.setIpAddress(parameterParser.getHost()); - } - else { - XID.setIpAddress(NetUtil.getLocalIp()); - } - XID.setPort(nettyRemotingServer.getListenPort()); - - try { - nettyRemotingServer.init(); - } - catch (Throwable e) { - logger.error("nettyServer init error:{}", e.getMessage(), e); - System.exit(-1); - } - - System.exit(0); - } - -} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/Server.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/Server.java new file mode 100644 index 000000000..81eb1119e --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/Server.java @@ -0,0 +1,89 @@ +/* + * 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(); + } +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/ServerApplication.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/ServerApplication.java new file mode 100644 index 000000000..9b2df000d --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/ServerApplication.java @@ -0,0 +1,32 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import java.io.IOException; + +/** + * @author spilledyear@outlook.com + */ +@SpringBootApplication(scanBasePackages = {"io.seata"}) +public class ServerApplication { + public static void main(String[] args) throws IOException { + // run the spring-boot application + SpringApplication.run(ServerApplication.class, args); + } +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/ServerApplicationListener.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/ServerApplicationListener.java new file mode 100644 index 000000000..e26301d75 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/ServerApplicationListener.java @@ -0,0 +1,128 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server; + +import io.seata.common.holder.ObjectHolder; +import io.seata.common.util.StringUtils; +import io.seata.server.store.StoreConfig; +import io.seata.spring.boot.autoconfigure.SeataCoreEnvironmentPostProcessor; +import io.seata.spring.boot.autoconfigure.SeataServerEnvironmentPostProcessor; +import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; +import org.springframework.boot.context.logging.LoggingApplicationListener; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.GenericApplicationListener; +import org.springframework.core.ResolvableType; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.PropertiesPropertySource; + +import java.util.Properties; + +import static io.seata.common.Constants.OBJECT_KEY_SPRING_CONFIGURABLE_ENVIRONMENT; +import static io.seata.common.DefaultValues.SERVICE_OFFSET_SPRING_BOOT; +import static io.seata.core.constants.ConfigurationKeys.*; + +/** + * @author slievrly + * @author funkye + */ +public class ServerApplicationListener implements GenericApplicationListener { + + @Override + public boolean supportsEventType(ResolvableType eventType) { + return eventType.getRawClass() != null + && ApplicationEnvironmentPreparedEvent.class.isAssignableFrom(eventType.getRawClass()); + } + + @Override + public void onApplicationEvent(ApplicationEvent event) { + if (!(event instanceof ApplicationEnvironmentPreparedEvent)) { + return; + } + ApplicationEnvironmentPreparedEvent environmentPreparedEvent = (ApplicationEnvironmentPreparedEvent)event; + ConfigurableEnvironment environment = environmentPreparedEvent.getEnvironment(); + ObjectHolder.INSTANCE.setObject(OBJECT_KEY_SPRING_CONFIGURABLE_ENVIRONMENT, environment); + SeataCoreEnvironmentPostProcessor.init(); + SeataServerEnvironmentPostProcessor.init(); + // Load by priority + System.setProperty("sessionMode", StoreConfig.getSessionMode().getName()); + System.setProperty("lockMode", StoreConfig.getLockMode().getName()); + + String[] args = environmentPreparedEvent.getArgs(); + + // port: -p > -D > env > yml > default + + //-p 8091 + if (args != null && args.length >= 2) { + for (int i = 0; i < args.length; ++i) { + if ("-p".equalsIgnoreCase(args[i]) && i < args.length - 1) { + setTargetPort(environment, args[i + 1], true); + return; + } + } + } + + // -Dserver.servicePort=8091 + String dPort = environment.getProperty(SERVER_SERVICE_PORT_CAMEL, String.class); + if (StringUtils.isNotBlank(dPort)) { + setTargetPort(environment, dPort, true); + return; + } + + //docker -e SEATA_PORT=8091 + String envPort = environment.getProperty(ENV_SEATA_PORT_KEY, String.class); + if (StringUtils.isNotBlank(envPort)) { + setTargetPort(environment, envPort, true); + return; + } + + //yml properties server.service-port=8091 + String configPort = environment.getProperty(SERVER_SERVICE_PORT_CONFIG, String.class); + if (StringUtils.isNotBlank(configPort)) { + setTargetPort(environment, configPort, false); + return; + } + + // server.port=7091 + String serverPort = environment.getProperty("server.port", String.class); + if (StringUtils.isBlank(serverPort)) { + serverPort = "8080"; + } + String servicePort = String.valueOf(Integer.parseInt(serverPort) + SERVICE_OFFSET_SPRING_BOOT); + setTargetPort(environment, servicePort, true); + } + + private void setTargetPort(ConfigurableEnvironment environment, String port, boolean needAddPropertySource) { + // get rpc port first, use to logback-spring.xml, @see the class named `SystemPropertyLoggerContextListener` + System.setProperty(SERVER_SERVICE_PORT_CAMEL, port); + + if (needAddPropertySource) { + // add property source to the first position + Properties pro = new Properties(); + pro.setProperty(SERVER_SERVICE_PORT_CONFIG, port); + environment.getPropertySources().addFirst(new PropertiesPropertySource("serverProperties", pro)); + } + } + + /** + * higher than LoggingApplicationListener + * + * @return the order + */ + @Override + public int getOrder() { + return LoggingApplicationListener.DEFAULT_ORDER - 1; + } +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/ServerRunner.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/ServerRunner.java new file mode 100644 index 000000000..d19233a06 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/ServerRunner.java @@ -0,0 +1,81 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server; + +import io.seata.core.rpc.Disposable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + + +/** + * @author spilledyear@outlook.com + */ +@Component +public class ServerRunner implements CommandLineRunner, DisposableBean { + + private static final Logger LOGGER = LoggerFactory.getLogger(ServerRunner.class); + + private boolean started = Boolean.FALSE; + + private static final List DISPOSABLE_LIST = new CopyOnWriteArrayList<>(); + + public static void addDisposable(Disposable disposable) { + DISPOSABLE_LIST.add(disposable); + } + + @Override + public void run(String... args) { + try { + long start = System.currentTimeMillis(); + Server.start(args); + started = true; + + long cost = System.currentTimeMillis() - start; + LOGGER.info("seata server started in {} millSeconds", cost); + } catch (Throwable e) { + started = Boolean.FALSE; + LOGGER.error("seata server start error: {} ", e.getMessage(), e); + System.exit(-1); + } + } + + + public boolean started() { + return started; + } + + @Override + public void destroy() throws Exception { + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("destoryAll starting"); + } + + for (Disposable disposable : DISPOSABLE_LIST) { + disposable.destroy(); + } + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("destoryAll finish"); + } + } +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/UUIDGenerator.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/UUIDGenerator.java index dee5a338b..11aa492e9 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/UUIDGenerator.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/UUIDGenerator.java @@ -24,29 +24,28 @@ import io.seata.common.util.IdWorker; */ public class UUIDGenerator { - private static volatile IdWorker idWorker; + 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); - } + /** + * 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); + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/auth/AbstractCheckAuthHandler.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/auth/AbstractCheckAuthHandler.java index bf0d531b3..ecfbfc12d 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/auth/AbstractCheckAuthHandler.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/auth/AbstractCheckAuthHandler.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package io.seata.server.auth; import io.seata.config.ConfigurationFactory; @@ -29,27 +28,26 @@ import static io.seata.common.DefaultValues.DEFAULT_SERVER_ENABLE_CHECK_AUTH; */ 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); + 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); - } + @Override + public boolean regTransactionManagerCheckAuth(RegisterTMRequest request) { + if (!ENABLE_CHECK_AUTH) { + return true; + } + return doRegTransactionManagerCheck(request); + } - public abstract boolean doRegTransactionManagerCheck(RegisterTMRequest 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); + @Override + public boolean regResourceManagerCheckAuth(RegisterRMRequest request) { + if (!ENABLE_CHECK_AUTH) { + return true; + } + return doRegResourceManagerCheck(request); + } + public abstract boolean doRegResourceManagerCheck(RegisterRMRequest request); } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/auth/DefaultCheckAuthHandler.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/auth/DefaultCheckAuthHandler.java index e98da6338..ed3e9343c 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/auth/DefaultCheckAuthHandler.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/auth/DefaultCheckAuthHandler.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package io.seata.server.auth; import io.seata.common.loader.LoadLevel; @@ -26,14 +25,13 @@ import io.seata.core.protocol.RegisterTMRequest; @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; - } + @Override + public boolean doRegTransactionManagerCheck(RegisterTMRequest request) { + return true; + } + @Override + public boolean doRegResourceManagerCheck(RegisterRMRequest request) { + return true; + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ansi/AnsiStyle.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/controller/BranchSessionController.java similarity index 53% rename from pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ansi/AnsiStyle.java rename to pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/controller/BranchSessionController.java index 1080d9fc7..f5e7a89d7 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ansi/AnsiStyle.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/controller/BranchSessionController.java @@ -13,53 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.server.logging.logback.ansi; +package io.seata.server.console.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; /** - * {@link AnsiElement Ansi} styles. - * - * @author Phillip Webb - * @origin Copied from spring-boot.jar by wang.liang + * Branch Session Controller + * @author zhongxiang.wang */ -public enum AnsiStyle implements AnsiElement { +@RestController +@RequestMapping("console/branchSession") +public class BranchSessionController { - /** - * normal - */ - NORMAL("0"), + @Resource(type = BranchSessionService.class) + private BranchSessionService branchSessionService; - /** - * bold - */ - BOLD("1"), - - /** - * faint - */ - FAINT("2"), - - /** - * italic - */ - ITALIC("3"), - - /** - * underline - */ - UNDERLINE("4"); - - /** - * code of style - */ - private final String code; - - AnsiStyle(String code) { - this.code = code; - } - - @Override - public String toString() { - return this.code; - } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/controller/GlobalLockController.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/controller/GlobalLockController.java new file mode 100644 index 000000000..d8ea221ce --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/controller/GlobalLockController.java @@ -0,0 +1,51 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.console.controller; + +import io.seata.console.result.PageResult; +import io.seata.server.console.param.GlobalLockParam; +import io.seata.server.console.service.GlobalLockService; +import io.seata.server.console.vo.GlobalLockVO; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + + +/** + * Global Lock Controller + * @author zhongxiang.wang + */ +@RestController +@RequestMapping("/api/v1/console/globalLock") +public class GlobalLockController { + + @Resource(type = GlobalLockService.class) + private GlobalLockService globalLockService; + + /** + * Query locks by param + * @param param the param + * @return the list of GlobalLockVO + */ + @GetMapping("query") + public PageResult query(@ModelAttribute GlobalLockParam param) { + return globalLockService.query(param); + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/controller/GlobalSessionController.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/controller/GlobalSessionController.java new file mode 100644 index 000000000..db34ace88 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/controller/GlobalSessionController.java @@ -0,0 +1,50 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.console.controller; + +import io.seata.console.result.PageResult; +import io.seata.server.console.param.GlobalSessionParam; +import io.seata.server.console.service.GlobalSessionService; +import io.seata.server.console.vo.GlobalSessionVO; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +/** + * Global Session Controller + * @author zhongxiang.wang + */ +@RestController +@RequestMapping("/api/v1/console/globalSession") +public class GlobalSessionController { + + @Resource(type = GlobalSessionService.class) + private GlobalSessionService globalSessionService; + + /** + * Query all globalSession + * @param param param for query globalSession + * @return the list of GlobalSessionVO + */ + @GetMapping("query") + public PageResult query(@ModelAttribute GlobalSessionParam param) { + return globalSessionService.query(param); + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/db/BranchSessionDBServiceImpl.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/db/BranchSessionDBServiceImpl.java new file mode 100644 index 000000000..963b273cd --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/db/BranchSessionDBServiceImpl.java @@ -0,0 +1,101 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.console.impl.db; + +import io.seata.common.ConfigurationKeys; +import io.seata.common.exception.StoreException; +import io.seata.common.loader.EnhancedServiceLoader; +import io.seata.common.util.IOUtil; +import io.seata.common.util.StringUtils; +import io.seata.config.Configuration; +import io.seata.config.ConfigurationFactory; +import io.seata.console.result.PageResult; +import io.seata.core.store.db.DataSourceProvider; +import io.seata.core.store.db.sql.log.LogStoreSqlsFactory; +import io.seata.server.console.service.BranchSessionService; +import io.seata.server.console.vo.BranchSessionVO; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import static io.seata.common.DefaultValues.DEFAULT_STORE_DB_BRANCH_TABLE; + +/** + * Branch Session DataBase ServiceImpl + * + * @author zhongxiang.wang + * @author lvekee 734843455@qq.com + */ +@Component +@org.springframework.context.annotation.Configuration +@ConditionalOnExpression("#{'db'.equals('${sessionMode}')}") +public class BranchSessionDBServiceImpl implements BranchSessionService { + + private String branchTable; + + private String dbType; + + private DataSource dataSource; + + public BranchSessionDBServiceImpl() { + Configuration configuration = ConfigurationFactory.getInstance(); + branchTable = configuration.getConfig(ConfigurationKeys.STORE_DB_BRANCH_TABLE, DEFAULT_STORE_DB_BRANCH_TABLE); + dbType = configuration.getConfig(ConfigurationKeys.STORE_DB_TYPE); + if (StringUtils.isBlank(dbType)) { + throw new IllegalArgumentException(ConfigurationKeys.STORE_DB_TYPE + " should not be blank"); + } + String dbDataSource = configuration.getConfig(ConfigurationKeys.STORE_DB_DATASOURCE_TYPE); + if (StringUtils.isBlank(dbDataSource)) { + throw new IllegalArgumentException(ConfigurationKeys.STORE_DB_DATASOURCE_TYPE + " should not be blank"); + } + dataSource = EnhancedServiceLoader.load(DataSourceProvider.class, dbDataSource).provide(); + } + + @Override + public PageResult queryByXid(String xid) { + if (StringUtils.isBlank(xid)) { + throw new IllegalArgumentException("xid should not be blank"); + } + + String whereCondition = " where xid = ? "; + String branchSessionSQL = LogStoreSqlsFactory.getLogStoreSqls(dbType).getAllBranchSessionSQL(branchTable, whereCondition); + + List list = new ArrayList<>(); + ResultSet rs = null; + + try (Connection conn = dataSource.getConnection(); + PreparedStatement ps = conn.prepareStatement(branchSessionSQL)) { + ps.setObject(1, xid); + rs = ps.executeQuery(); + while (rs.next()) { + list.add(BranchSessionVO.convert(rs)); + } + } catch (SQLException e) { + throw new StoreException(e); + } finally { + IOUtil.close(rs); + } + return PageResult.success(list, list.size(), 0, 0, 0); + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/db/GlobalLockDBServiceImpl.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/db/GlobalLockDBServiceImpl.java new file mode 100644 index 000000000..41d61efee --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/db/GlobalLockDBServiceImpl.java @@ -0,0 +1,145 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.console.impl.db; + +import io.seata.common.ConfigurationKeys; +import io.seata.common.exception.StoreException; +import io.seata.common.loader.EnhancedServiceLoader; +import io.seata.common.util.IOUtil; +import io.seata.common.util.PageUtil; +import io.seata.common.util.StringUtils; +import io.seata.config.Configuration; +import io.seata.config.ConfigurationFactory; +import io.seata.console.result.PageResult; +import io.seata.core.store.db.DataSourceProvider; +import io.seata.core.store.db.sql.lock.LockStoreSqlFactory; +import io.seata.server.console.param.GlobalLockParam; +import io.seata.server.console.service.GlobalLockService; +import io.seata.server.console.vo.GlobalLockVO; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import static io.seata.common.DefaultValues.DEFAULT_LOCK_DB_TABLE; + + +/** + * Global Lock DB ServiceImpl + * + * @author zhongxiang.wang + * @author lvekee 734843455@qq.com + */ +@Component +@org.springframework.context.annotation.Configuration +@ConditionalOnExpression("#{'db'.equals('${lockMode}')}") +public class GlobalLockDBServiceImpl implements GlobalLockService { + + private String lockTable; + + private String dbType; + + private DataSource dataSource; + + public GlobalLockDBServiceImpl() { + Configuration configuration = ConfigurationFactory.getInstance(); + lockTable = configuration.getConfig(ConfigurationKeys.LOCK_DB_TABLE, DEFAULT_LOCK_DB_TABLE); + dbType = configuration.getConfig(ConfigurationKeys.STORE_DB_TYPE); + if (StringUtils.isBlank(dbType)) { + throw new IllegalArgumentException(ConfigurationKeys.STORE_DB_TYPE + " should not be blank"); + } + String dbDataSource = configuration.getConfig(ConfigurationKeys.STORE_DB_DATASOURCE_TYPE); + if (StringUtils.isBlank(dbDataSource)) { + throw new IllegalArgumentException(ConfigurationKeys.STORE_DB_DATASOURCE_TYPE + " should not be blank"); + } + dataSource = EnhancedServiceLoader.load(DataSourceProvider.class, dbDataSource).provide(); + } + + @Override + public PageResult query(GlobalLockParam param) { + PageUtil.checkParam(param.getPageNum(), param.getPageSize()); + + List sqlParamList = new ArrayList<>(); + String whereCondition = this.getWhereConditionByParam(param, sqlParamList); + + String sourceSql = LockStoreSqlFactory.getLogStoreSql(dbType).getAllLockSql(lockTable, whereCondition); + String queryLockSql = PageUtil.pageSql(sourceSql, dbType, param.getPageNum(), param.getPageSize()); + String lockCountSql = PageUtil.countSql(sourceSql, dbType); + + List list = new ArrayList<>(); + int count = 0; + + ResultSet rs = null; + ResultSet countRs = null; + + try (Connection conn = dataSource.getConnection(); + PreparedStatement ps = conn.prepareStatement(queryLockSql); + PreparedStatement countPs = conn.prepareStatement(lockCountSql)) { + PageUtil.setObject(ps, sqlParamList); + rs = ps.executeQuery(); + while (rs.next()) { + list.add(GlobalLockVO.convert(rs)); + } + PageUtil.setObject(countPs, sqlParamList); + countRs = countPs.executeQuery(); + if (countRs.next()) { + count = countRs.getInt(1); + } + } catch (SQLException e) { + throw new StoreException(e); + } finally { + IOUtil.close(rs, countRs); + } + return PageResult.success(list, count, param.getPageNum(), param.getPageSize()); + } + + private String getWhereConditionByParam(GlobalLockParam param, List sqlParamList) { + StringBuilder whereConditionBuilder = new StringBuilder(); + if (StringUtils.isNotBlank(param.getXid())) { + whereConditionBuilder.append(" and xid = ? "); + sqlParamList.add(param.getXid()); + } + if (StringUtils.isNotBlank(param.getTableName())) { + whereConditionBuilder.append(" and table_name = ? "); + sqlParamList.add(param.getTableName()); + } + if (StringUtils.isNotBlank(param.getTransactionId())) { + whereConditionBuilder.append(" and transaction_id = ? "); + sqlParamList.add(param.getTransactionId()); + } + if (StringUtils.isNotBlank(param.getBranchId())) { + whereConditionBuilder.append(" and branch_id = ? "); + sqlParamList.add(param.getBranchId()); + } + if (param.getTimeStart() != null) { + whereConditionBuilder.append(" and gmt_create >= ? "); + sqlParamList.add(param.getTimeStart()); + } + if (param.getTimeEnd() != null) { + whereConditionBuilder.append(" and gmt_create <= ? "); + sqlParamList.add(param.getTimeEnd()); + } + String whereCondition = whereConditionBuilder.toString(); + return whereCondition.replaceFirst("and", "where"); + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/db/GlobalSessionDBServiceImpl.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/db/GlobalSessionDBServiceImpl.java new file mode 100644 index 000000000..318773473 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/db/GlobalSessionDBServiceImpl.java @@ -0,0 +1,160 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.console.impl.db; + +import io.seata.common.ConfigurationKeys; +import io.seata.common.exception.StoreException; +import io.seata.common.loader.EnhancedServiceLoader; +import io.seata.common.util.IOUtil; +import io.seata.common.util.PageUtil; +import io.seata.common.util.StringUtils; +import io.seata.config.Configuration; +import io.seata.config.ConfigurationFactory; +import io.seata.console.result.PageResult; +import io.seata.core.store.db.DataSourceProvider; +import io.seata.core.store.db.sql.log.LogStoreSqlsFactory; +import io.seata.server.console.param.GlobalSessionParam; +import io.seata.server.console.service.BranchSessionService; +import io.seata.server.console.service.GlobalSessionService; +import io.seata.server.console.vo.BranchSessionVO; +import io.seata.server.console.vo.GlobalSessionVO; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashSet; +import java.util.List; + +import static io.seata.common.DefaultValues.DEFAULT_STORE_DB_GLOBAL_TABLE; + +/** + * Global Session DataBase ServiceImpl + * + * @author zhongxiang.wang + * @author lvekee 734843455@qq.com + */ +@Component +@org.springframework.context.annotation.Configuration +@ConditionalOnExpression("#{'db'.equals('${sessionMode}')}") +public class GlobalSessionDBServiceImpl implements GlobalSessionService { + + private String globalTable; + + private String dbType; + + private DataSource dataSource; + + @Resource(type = BranchSessionService.class) + private BranchSessionService branchSessionService; + + public GlobalSessionDBServiceImpl() { + Configuration configuration = ConfigurationFactory.getInstance(); + globalTable = configuration.getConfig(ConfigurationKeys.STORE_DB_GLOBAL_TABLE, DEFAULT_STORE_DB_GLOBAL_TABLE); + dbType = configuration.getConfig(ConfigurationKeys.STORE_DB_TYPE); + if (StringUtils.isBlank(dbType)) { + throw new IllegalArgumentException(ConfigurationKeys.STORE_DB_TYPE + " should not be blank"); + } + String dbDataSource = configuration.getConfig(ConfigurationKeys.STORE_DB_DATASOURCE_TYPE); + if (StringUtils.isBlank(dbDataSource)) { + throw new IllegalArgumentException(ConfigurationKeys.STORE_DB_DATASOURCE_TYPE + " should not be blank"); + } + dataSource = EnhancedServiceLoader.load(DataSourceProvider.class, dbDataSource).provide(); + } + + @Override + public PageResult query(GlobalSessionParam param) { + PageUtil.checkParam(param.getPageNum(), param.getPageSize()); + + List sqlParamList = new ArrayList<>(); + String whereCondition = getWhereConditionByParam(param, sqlParamList); + + String sourceSql = LogStoreSqlsFactory.getLogStoreSqls(dbType).getAllGlobalSessionSql(globalTable, whereCondition); + String querySessionSql = PageUtil.pageSql(sourceSql, dbType, param.getPageNum(), param.getPageSize()); + String sessionCountSql = PageUtil.countSql(sourceSql, dbType); + + List list = new ArrayList<>(); + int count = 0; + + + ResultSet rs = null; + ResultSet countRs = null; + + try (Connection conn = dataSource.getConnection(); + PreparedStatement ps = conn.prepareStatement(querySessionSql); + PreparedStatement countPs = conn.prepareStatement(sessionCountSql)) { + PageUtil.setObject(ps, sqlParamList); + rs = ps.executeQuery(); + while (rs.next()) { + list.add(GlobalSessionVO.convert(rs)); + } + + PageUtil.setObject(countPs, sqlParamList); + countRs = countPs.executeQuery(); + if (countRs.next()) { + count = countRs.getInt(1); + } + if (param.isWithBranch()) { + for (GlobalSessionVO globalSessionVO : list) { + PageResult pageResp = branchSessionService.queryByXid(globalSessionVO.getXid()); + globalSessionVO.setBranchSessionVOs(new HashSet<>(pageResp.getData())); + } + } + } catch (SQLException e) { + throw new StoreException(e); + } finally { + IOUtil.close(rs, countRs); + } + return PageResult.success(list, count, param.getPageNum(), param.getPageSize()); + } + + private String getWhereConditionByParam(GlobalSessionParam param, List sqlParamList) { + StringBuilder whereConditionBuilder = new StringBuilder(); + if (StringUtils.isNotBlank(param.getXid())) { + whereConditionBuilder.append(" and xid = ? "); + sqlParamList.add(param.getXid()); + } + if (StringUtils.isNotBlank(param.getApplicationId())) { + whereConditionBuilder.append(" and application_id = ? "); + sqlParamList.add(param.getApplicationId()); + } + if (param.getStatus() != null) { + whereConditionBuilder.append(" and status = ? "); + sqlParamList.add(param.getStatus()); + } + if (StringUtils.isNotBlank(param.getTransactionName())) { + whereConditionBuilder.append(" and transaction_name = ? "); + sqlParamList.add(param.getTransactionName()); + } + if (param.getTimeStart() != null) { + whereConditionBuilder.append(" and gmt_create >= ? "); + sqlParamList.add(new Date(param.getTimeStart())); + } + if (param.getTimeEnd() != null) { + whereConditionBuilder.append(" and gmt_create <= ? "); + sqlParamList.add(new Date(param.getTimeEnd())); + } + String whereCondition = whereConditionBuilder.toString(); + return whereCondition.replaceFirst("and", "where"); + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/file/BranchSessionFileServiceImpl.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/file/BranchSessionFileServiceImpl.java new file mode 100644 index 000000000..2f710d0e2 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/file/BranchSessionFileServiceImpl.java @@ -0,0 +1,39 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.console.impl.file; + +import io.seata.common.exception.NotSupportYetException; +import io.seata.console.result.PageResult; +import io.seata.server.console.service.BranchSessionService; +import io.seata.server.console.vo.BranchSessionVO; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; + +/** + * Branch Session File ServiceImpl + * + * @author zhongxiang.wang + */ +@Component +@org.springframework.context.annotation.Configuration +@ConditionalOnExpression("#{'file'.equals('${sessionMode}')}") +public class BranchSessionFileServiceImpl implements BranchSessionService { + + @Override + public PageResult queryByXid(String xid) { + throw new NotSupportYetException(); + } +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/file/GlobalLockFileServiceImpl.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/file/GlobalLockFileServiceImpl.java new file mode 100644 index 000000000..59d91540d --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/file/GlobalLockFileServiceImpl.java @@ -0,0 +1,170 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.console.impl.file; + +import io.seata.common.util.CollectionUtils; +import io.seata.common.util.StringUtils; +import io.seata.console.result.PageResult; +import io.seata.core.lock.RowLock; +import io.seata.server.console.param.GlobalLockParam; +import io.seata.server.console.service.GlobalLockService; +import io.seata.server.console.vo.GlobalLockVO; +import io.seata.server.lock.LockerManagerFactory; +import io.seata.server.session.BranchSession; +import io.seata.server.session.GlobalSession; +import io.seata.server.session.SessionHolder; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static io.seata.common.util.StringUtils.isBlank; +import static io.seata.server.console.vo.GlobalLockVO.convert; +import static java.util.Objects.isNull; + +/** + * Global Lock File ServiceImpl + * + * @author zhongxiang.wang + * @author miaoxueyu + */ +@Component +@org.springframework.context.annotation.Configuration +@ConditionalOnExpression("#{'file'.equals('${lockMode}')}") +public class GlobalLockFileServiceImpl implements GlobalLockService { + + @Override + public PageResult query(GlobalLockParam param) { + checkParam(param); + + final Collection allSessions = SessionHolder.getRootSessionManager().allSessions(); + + final AtomicInteger total = new AtomicInteger(); + List result = allSessions + .parallelStream() + .filter(obtainGlobalSessionPredicate(param)) + .flatMap(globalSession -> globalSession.getBranchSessions().stream()) + .filter(obtainBranchSessionPredicate(param)) + .flatMap(branchSession -> filterAndMap(param, branchSession)) + .peek(globalSession -> total.incrementAndGet()) + .collect(Collectors.toList()); + + return PageResult.build(convert(result), param.getPageNum(), param.getPageSize()); + + } + + /** + * filter with tableName and generate RowLock + * + * @param param the query param + * @param branchSession the branch session + * @return the RowLock list + */ + private Stream filterAndMap(GlobalLockParam param, BranchSession branchSession) { + + final String tableName = param.getTableName(); + + // get rowLock from branchSession + final List rowLocks = LockerManagerFactory.getLockManager().collectRowLocks(branchSession); + + if (StringUtils.isNotBlank(tableName)) { + return rowLocks.parallelStream().filter(rowLock -> rowLock.getTableName().contains(param.getTableName())); + } + + return rowLocks.stream(); + } + + + /** + * check the param + * + * @param param the param + */ + private void checkParam(GlobalLockParam param) { + if (param.getPageSize() <= 0 || param.getPageNum() <= 0) { + throw new IllegalArgumentException("wrong pageSize or pageNum"); + } + + // verification data type + try { + Long.parseLong(param.getTransactionId()); + } catch (NumberFormatException e) { + param.setTransactionId(null); + } + try { + Long.parseLong(param.getBranchId()); + } catch (NumberFormatException e) { + param.setBranchId(null); + } + + + } + + /** + * obtain the branch session condition + * + * @param param condition for query branch session + * @return the filter condition + */ + private Predicate obtainBranchSessionPredicate(GlobalLockParam param) { + return branchSession -> { + // transactionId + return (isBlank(param.getTransactionId()) || + String.valueOf(branchSession.getTransactionId()).contains(param.getTransactionId())) + + && + // branch id + (isBlank(param.getBranchId()) || + String.valueOf(branchSession.getBranchId()).contains(param.getBranchId())) + ; + }; + } + + + /** + * obtain the global session condition + * + * @param param condition for query global session + * @return the filter condition + */ + private Predicate obtainGlobalSessionPredicate(GlobalLockParam param) { + + return globalSession -> { + // first, there must be withBranchSession + return CollectionUtils.isNotEmpty(globalSession.getBranchSessions()) + + && + // The second is other conditions + // xid + (isBlank(param.getXid()) || globalSession.getXid().contains(param.getXid())) + + && + // timeStart + (isNull(param.getTimeStart()) || param.getTimeStart() <= globalSession.getBeginTime()) + + && + // timeEnd + (isNull(param.getTimeEnd()) || param.getTimeEnd() >= globalSession.getBeginTime()); + }; + } + + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/file/GlobalSessionFileServiceImpl.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/file/GlobalSessionFileServiceImpl.java new file mode 100644 index 000000000..51ece1d56 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/file/GlobalSessionFileServiceImpl.java @@ -0,0 +1,102 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.console.impl.file; + +import io.seata.console.result.PageResult; +import io.seata.server.console.param.GlobalSessionParam; +import io.seata.server.console.service.GlobalSessionService; +import io.seata.server.console.vo.GlobalSessionVO; +import io.seata.server.session.GlobalSession; +import io.seata.server.session.SessionHolder; +import io.seata.server.storage.SessionConverter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import static io.seata.common.util.StringUtils.isBlank; +import static java.util.Objects.isNull; + +/** + * Global Session File ServiceImpl + * + * @author zhongxiang.wang + * @author miaoxueyu + */ +@Component +@org.springframework.context.annotation.Configuration +@ConditionalOnExpression("#{'file'.equals('${sessionMode}')}") +public class GlobalSessionFileServiceImpl implements GlobalSessionService { + + @Override + public PageResult query(GlobalSessionParam param) { + if (param.getPageSize() <= 0 || param.getPageNum() <= 0) { + throw new IllegalArgumentException("wrong pageSize or pageNum"); + } + + final Collection allSessions = SessionHolder.getRootSessionManager().allSessions(); + + final List filteredSessions = allSessions + .parallelStream() + .filter(obtainPredicate(param)) + .collect(Collectors.toList()); + + return PageResult.build(SessionConverter.convertGlobalSession(filteredSessions), param.getPageNum(), param.getPageSize()); + } + + + + /** + * obtain the condition + * + * @param param condition for query global session + * @return the filter condition + */ + private Predicate obtainPredicate(GlobalSessionParam param) { + + return session -> { + return + // xid + (isBlank(param.getXid()) || session.getXid().contains(param.getXid())) + + && + // applicationId + (isBlank(param.getApplicationId()) || session.getApplicationId().contains(param.getApplicationId())) + + && + // status + (isNull(param.getStatus()) || Objects.equals(session.getStatus().getCode(), param.getStatus())) + + && + // transactionName + (isBlank(param.getTransactionName()) || session.getTransactionName().contains(param.getTransactionName())) + + && + // timeStart + (isNull(param.getTimeStart()) || param.getTimeStart() <= session.getBeginTime()) + + && + // timeEnd + (isNull(param.getTimeEnd()) || param.getTimeEnd() >= session.getBeginTime()); + + }; + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/redis/BranchSessionRedisServiceImpl.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/redis/BranchSessionRedisServiceImpl.java new file mode 100644 index 000000000..c8e9e91ac --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/redis/BranchSessionRedisServiceImpl.java @@ -0,0 +1,65 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.console.impl.redis; + +import io.seata.common.util.CollectionUtils; +import io.seata.common.util.StringUtils; +import io.seata.console.result.PageResult; +import io.seata.core.store.BranchTransactionDO; +import io.seata.server.console.service.BranchSessionService; +import io.seata.server.console.vo.BranchSessionVO; +import io.seata.server.storage.redis.store.RedisTransactionStoreManager; +import org.springframework.beans.BeanUtils; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +/** + * Branch Session Redis ServiceImpl + * + * @author zhongxiang.wang + * @author doubleDimple + */ +@Component +@org.springframework.context.annotation.Configuration +@ConditionalOnExpression("#{'redis'.equals('${sessionMode}')}") +public class BranchSessionRedisServiceImpl implements BranchSessionService { + + @Override + public PageResult queryByXid(String xid) { + if (StringUtils.isBlank(xid)) { + return PageResult.success(); + } + + List branchSessionVos = new ArrayList<>(); + + RedisTransactionStoreManager instance = RedisTransactionStoreManager.getInstance(); + + List branchSessionDos = instance.findBranchSessionByXid(xid); + + if (CollectionUtils.isNotEmpty(branchSessionDos)) { + for (BranchTransactionDO branchSessionDo : branchSessionDos) { + BranchSessionVO branchSessionVO = new BranchSessionVO(); + BeanUtils.copyProperties(branchSessionDo, branchSessionVO); + branchSessionVos.add(branchSessionVO); + } + } + + return PageResult.success(branchSessionVos, branchSessionVos.size(), 0, branchSessionVos.size()); + } +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/redis/GlobalLockRedisServiceImpl.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/redis/GlobalLockRedisServiceImpl.java new file mode 100644 index 000000000..17f30b892 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/redis/GlobalLockRedisServiceImpl.java @@ -0,0 +1,119 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.console.impl.redis; + +import io.seata.common.util.BeanUtils; +import io.seata.common.util.CollectionUtils; +import io.seata.console.result.PageResult; +import io.seata.server.console.param.GlobalLockParam; +import io.seata.server.console.service.GlobalLockService; +import io.seata.server.console.vo.GlobalLockVO; +import io.seata.server.storage.redis.JedisPooledFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; +import redis.clients.jedis.Jedis; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import static io.seata.common.Constants.ROW_LOCK_KEY_SPLIT_CHAR; +import static io.seata.common.exception.FrameworkErrorCode.ParameterRequired; +import static io.seata.common.util.StringUtils.isNotBlank; +import static io.seata.console.result.PageResult.checkPage; +import static io.seata.core.constants.RedisKeyConstants.*; + +/** + * Global Lock Redis Service Impl + * @author zhongxiang.wang + * @author doubleDimple + */ +@Component +@org.springframework.context.annotation.Configuration +@ConditionalOnExpression("#{'redis'.equals('${lockMode}')}") +public class GlobalLockRedisServiceImpl implements GlobalLockService { + + @Override + public PageResult query(GlobalLockParam param) { + + int total = 0; + List globalLockVos; + checkPage(param); + if (isNotBlank(param.getXid())) { + globalLockVos = queryGlobalByXid(param.getXid()); + total = globalLockVos.size(); + return PageResult.success(globalLockVos,total,param.getPageNum(),param.getPageSize()); + } else if (isNotBlank(param.getTableName()) && isNotBlank(param.getPk()) && isNotBlank(param.getResourceId())) { + //SEATA_ROW_LOCK_jdbc:mysql://116.62.62.26/seata-order^^^order^^^2188 + String tableName = param.getTableName(); + String pk = param.getPk(); + String resourceId = param.getResourceId(); + globalLockVos = queryGlobalLockByRowKey(buildRowKey(tableName,pk,resourceId)); + total = globalLockVos.size(); + return PageResult.success(globalLockVos,total,param.getPageNum(),param.getPageSize()); + } else { + return PageResult.failure(ParameterRequired.getErrCode(),"only three parameters of tableName,pk,resourceId or Xid are supported"); + } + } + + private List queryGlobalLockByRowKey(String buildRowKey) { + return readGlobalLockByRowKey(buildRowKey); + } + + private String buildRowKey(String tableName, String pk,String resourceId) { + return DEFAULT_REDIS_SEATA_ROW_LOCK_PREFIX + resourceId + SPLIT + tableName + SPLIT + pk; + } + + + private List queryGlobalByXid(String xid) { + return readGlobalLockByXid(DEFAULT_REDIS_SEATA_GLOBAL_LOCK_PREFIX + xid); + } + + private List readGlobalLockByXid(String key) { + List vos = new ArrayList<>(); + try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { + Map mapGlobalKeys = jedis.hgetAll(key); + if (CollectionUtils.isNotEmpty(mapGlobalKeys)) { + List rowLockKeys = new ArrayList<>(); + mapGlobalKeys.forEach((k,v) -> rowLockKeys.addAll(Arrays.asList(v.split(ROW_LOCK_KEY_SPLIT_CHAR)))); + for (String rowLoclKey : rowLockKeys) { + Map mapRowLockKey = jedis.hgetAll(rowLoclKey); + GlobalLockVO vo = (GlobalLockVO)BeanUtils.mapToObject(mapRowLockKey, GlobalLockVO.class); + if (vo != null) { + vos.add(vo); + } + } + } + } + + return vos; + } + + + private List readGlobalLockByRowKey(String key) { + List vos = new ArrayList<>(); + try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { + Map map = jedis.hgetAll(key); + GlobalLockVO vo = (GlobalLockVO)BeanUtils.mapToObject(map, GlobalLockVO.class); + if (vo != null) { + vos.add(vo); + } + } + return vos; + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/redis/GlobalSessionRedisServiceImpl.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/redis/GlobalSessionRedisServiceImpl.java new file mode 100644 index 000000000..9ca2fdb70 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/impl/redis/GlobalSessionRedisServiceImpl.java @@ -0,0 +1,110 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.console.impl.redis; + +import io.seata.common.util.CollectionUtils; +import io.seata.console.result.PageResult; +import io.seata.core.model.GlobalStatus; +import io.seata.server.console.param.GlobalSessionParam; +import io.seata.server.console.service.GlobalSessionService; +import io.seata.server.console.vo.GlobalSessionVO; +import io.seata.server.session.GlobalSession; +import io.seata.server.session.SessionCondition; +import io.seata.server.storage.redis.store.RedisTransactionStoreManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import static io.seata.common.exception.FrameworkErrorCode.ParameterRequired; +import static io.seata.common.util.StringUtils.isBlank; +import static io.seata.common.util.StringUtils.isNotBlank; +import static io.seata.console.result.PageResult.checkPage; +import static io.seata.server.storage.SessionConverter.convertToGlobalSessionVo; + +/** + * Global Session Redis ServiceImpl + * @author zhongxiang.wang + * @author doubleDimple + */ +@Component +@org.springframework.context.annotation.Configuration +@ConditionalOnExpression("#{'redis'.equals('${sessionMode}')}") +public class GlobalSessionRedisServiceImpl implements GlobalSessionService { + + private static final Logger LOGGER = LoggerFactory.getLogger(GlobalSessionRedisServiceImpl.class); + + @Override + public PageResult query(GlobalSessionParam param) { + List result = new ArrayList<>(); + Long total = 0L; + if (param.getTimeStart() != null || param.getTimeEnd() != null) { + //not support time range query + LOGGER.debug("not supported according to time range query"); + return PageResult.failure(ParameterRequired.getErrCode(),"not supported according to time range query"); + } + List globalSessions = new ArrayList<>(); + + RedisTransactionStoreManager instance = RedisTransactionStoreManager.getInstance(); + + checkPage(param); + + if (isBlank(param.getXid()) && param.getStatus() == null) { + total = instance.countByGlobalSessions(GlobalStatus.values()); + globalSessions = instance.findGlobalSessionByPage(param.getPageNum(), param.getPageSize(),param.isWithBranch()); + } else { + List globalSessionsNew = new ArrayList<>(); + if (isNotBlank(param.getXid())) { + SessionCondition sessionCondition = new SessionCondition(); + sessionCondition.setXid(param.getXid()); + sessionCondition.setLazyLoadBranch(!param.isWithBranch()); + globalSessions = instance.readSession(sessionCondition); + total = (long)globalSessions.size(); + } + + if (param.getStatus() != null && GlobalStatus.get(param.getStatus()) != null) { + if (CollectionUtils.isNotEmpty(globalSessions)) { + globalSessionsNew = globalSessions.stream().filter(globalSession -> globalSession.getStatus().getCode() == (param.getStatus())).collect(Collectors.toList()); + total = (long)globalSessionsNew.size(); + } else { + total = instance.countByGlobalSessions(new GlobalStatus[] {GlobalStatus.get(param.getStatus())}); + globalSessionsNew = instance.readSessionStatusByPage(param); + } + } + + if (LOGGER.isDebugEnabled()) { + if (isNotBlank(param.getApplicationId())) { + //not support + LOGGER.debug("not supported according to applicationId query"); + } + if (isNotBlank(param.getTransactionName())) { + //not support + LOGGER.debug("not supported according to transactionName query"); + } + } + globalSessions = globalSessionsNew.size() > 0 ? globalSessionsNew : globalSessions; + } + + convertToGlobalSessionVo(result,globalSessions); + + return PageResult.success(result,total.intValue(),param.getPageNum(),param.getPageSize()); + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/param/GlobalLockParam.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/param/GlobalLockParam.java new file mode 100644 index 000000000..dcc43012a --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/param/GlobalLockParam.java @@ -0,0 +1,114 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.console.param; + +import io.seata.console.param.BaseParam; + +import java.io.Serializable; + +/** + * Global lock param + * @author zhongxiang.wang + */ +public class GlobalLockParam extends BaseParam implements Serializable { + + private static final long serialVersionUID = 615412528070131284L; + + /** + * the xid + */ + private String xid; + /** + * the table name + */ + private String tableName; + /** + * the transaction id + */ + private String transactionId; + /** + * the branch id + */ + private String branchId; + /** + * the primary Key + */ + private String pk; + /** + * the resourceId + */ + private String resourceId; + + public String getTransactionId() { + return transactionId; + } + + public void setTransactionId(String transactionId) { + this.transactionId = transactionId; + } + + public String getBranchId() { + return branchId; + } + + public void setBranchId(String branchId) { + this.branchId = branchId; + } + + public String getXid() { + return xid; + } + + public void setXid(String xid) { + this.xid = xid; + } + + public String getTableName() { + return tableName; + } + + public void setTableName(String tableName) { + this.tableName = tableName; + } + + public String getPk() { + return pk; + } + + public void setPk(String pk) { + this.pk = pk; + } + + public String getResourceId() { + return resourceId; + } + + public void setResourceId(String resourceId) { + this.resourceId = resourceId; + } + + @Override + public String toString() { + return "GlobalLockParam{" + + "xid='" + xid + '\'' + + ", tableName='" + tableName + '\'' + + ", transactionId='" + transactionId + '\'' + + ", branchId='" + branchId + '\'' + + ", pk='" + pk + '\'' + + ", resourceId='" + resourceId + '\'' + + '}'; + } +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/param/GlobalSessionParam.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/param/GlobalSessionParam.java new file mode 100644 index 000000000..88673039d --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/param/GlobalSessionParam.java @@ -0,0 +1,102 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.console.param; + +import io.seata.console.param.BaseParam; + +import java.io.Serializable; + +/** + * Global session param + * @author zhongxiang.wang + */ +public class GlobalSessionParam extends BaseParam implements Serializable { + + private static final long serialVersionUID = 115488252809011284L; + /** + * the xid + */ + private String xid; + /** + * the application id + */ + private String applicationId; + /** + * the global session status + */ + private Integer status; + /** + * the transaction name + */ + private String transactionName; + /** + * if with branch + * true: with branch session + * false: no branch session + */ + private boolean withBranch; + + public String getXid() { + return xid; + } + + public void setXid(String xid) { + this.xid = xid; + } + + public String getTransactionName() { + return transactionName; + } + + public void setTransactionName(String transactionName) { + this.transactionName = transactionName; + } + + public String getApplicationId() { + return applicationId; + } + + public void setApplicationId(String applicationId) { + this.applicationId = applicationId; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public boolean isWithBranch() { + return withBranch; + } + + public void setWithBranch(boolean withBranch) { + this.withBranch = withBranch; + } + + @Override + public String toString() { + return "GlobalSessionParam{" + + "xid='" + xid + '\'' + + ", applicationId='" + applicationId + '\'' + + ", status=" + status + + ", transactionName='" + transactionName + '\'' + + ", withBranch=" + withBranch + + '}'; + } +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ansi/AnsiElement.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/service/BranchSessionService.java similarity index 61% rename from pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ansi/AnsiElement.java rename to pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/service/BranchSessionService.java index 8af1d13d4..5f1b3fdc8 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ansi/AnsiElement.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/service/BranchSessionService.java @@ -13,20 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.server.logging.logback.ansi; +package io.seata.server.console.service; + +import io.seata.console.result.PageResult; +import io.seata.server.console.vo.BranchSessionVO; /** - * An ANSI encodable element. - * - * @author Phillip Webb - * @origin Copied from spring-boot.jar by wang.liang + * Branch session service + * @author wangzhongxiang */ -public interface AnsiElement { +public interface BranchSessionService { - /** - * @return the ANSI escape code - */ - @Override - String toString(); + /** + * Query branch session by xid + * @param xid the xid + * @return the BranchSessionVO list + */ + PageResult queryByXid(String xid); } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/service/GlobalLockService.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/service/GlobalLockService.java new file mode 100644 index 000000000..87b3a790d --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/service/GlobalLockService.java @@ -0,0 +1,37 @@ +/* + * 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 query(GlobalLockParam param); + + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/service/GlobalSessionService.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/service/GlobalSessionService.java new file mode 100644 index 000000000..452605384 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/service/GlobalSessionService.java @@ -0,0 +1,35 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.console.service; + +import io.seata.console.result.PageResult; +import io.seata.server.console.param.GlobalSessionParam; +import io.seata.server.console.vo.GlobalSessionVO; + +/** + * Global session service + * @author wangzhongxiang + */ +public interface GlobalSessionService { + + /** + * Query global session + * @param param the param + * @return the GlobalSessionVO list + */ + PageResult query(GlobalSessionParam param); + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/vo/BranchSessionVO.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/vo/BranchSessionVO.java new file mode 100644 index 000000000..fa1e7b872 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/vo/BranchSessionVO.java @@ -0,0 +1,241 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.console.vo; + +import io.seata.core.constants.ServerTableColumnsName; + +import java.sql.Date; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Objects; + +/** + * BranchSessionVO + * @author zhongxiang.wang + */ +public class BranchSessionVO { + + private String xid; + + private String transactionId; + + private String branchId; + + private String resourceGroupId; + + private String resourceId; + + private String branchType; + + private Integer status; + + private String clientId; + + private String applicationData; + + private Long gmtCreate; + + private Long gmtModified; + + + public BranchSessionVO(){ + + } + + public BranchSessionVO(String xid, + Long transactionId, + Long branchId, + String resourceGroupId, + String resourceId, + String branchType, + Integer status, + String clientId, + String applicationData) { + this.xid = xid; + this.transactionId = String.valueOf(transactionId); + this.branchId = String.valueOf(branchId); + this.resourceGroupId = resourceGroupId; + this.resourceId = resourceId; + this.branchType = branchType; + this.status = status; + this.clientId = clientId; + this.applicationData = applicationData; + } + + public String getXid() { + return xid; + } + + public void setXid(String xid) { + this.xid = xid; + } + + public String getTransactionId() { + return transactionId; + } + + public void setTransactionId(Long transactionId) { + this.transactionId = String.valueOf(transactionId); + } + + public String getBranchId() { + return branchId; + } + + public void setBranchId(Long branchId) { + this.branchId = String.valueOf(branchId); + } + + public String getResourceGroupId() { + return resourceGroupId; + } + + public void setResourceGroupId(String resourceGroupId) { + this.resourceGroupId = resourceGroupId; + } + + public String getResourceId() { + return resourceId; + } + + public void setResourceId(String resourceId) { + this.resourceId = resourceId; + } + + public String getBranchType() { + return branchType; + } + + public void setBranchType(String branchType) { + this.branchType = branchType; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public String getApplicationData() { + return applicationData; + } + + public void setApplicationData(String applicationData) { + this.applicationData = applicationData; + } + + public Long getGmtCreate() { + return gmtCreate; + } + + public void setGmtCreate(Long gmtCreate) { + this.gmtCreate = gmtCreate; + } + + public Long getGmtModified() { + return gmtModified; + } + + public void setGmtModified(Long gmtModified) { + this.gmtModified = gmtModified; + } + + public static BranchSessionVO convert(ResultSet rs) throws SQLException { + BranchSessionVO branchSessionVO = new BranchSessionVO(); + branchSessionVO.setXid(rs.getString(ServerTableColumnsName.BRANCH_TABLE_XID)); + branchSessionVO.setTransactionId(rs.getLong(ServerTableColumnsName.BRANCH_TABLE_TRANSACTION_ID)); + branchSessionVO.setBranchId(rs.getLong(ServerTableColumnsName.BRANCH_TABLE_BRANCH_ID)); + branchSessionVO.setResourceGroupId(rs.getString(ServerTableColumnsName.BRANCH_TABLE_RESOURCE_GROUP_ID)); + branchSessionVO.setResourceId(rs.getString(ServerTableColumnsName.BRANCH_TABLE_RESOURCE_ID)); + branchSessionVO.setBranchType(rs.getString(ServerTableColumnsName.BRANCH_TABLE_BRANCH_TYPE)); + branchSessionVO.setStatus(rs.getInt(ServerTableColumnsName.BRANCH_TABLE_STATUS)); + branchSessionVO.setClientId(rs.getString(ServerTableColumnsName.BRANCH_TABLE_CLIENT_ID)); + branchSessionVO.setApplicationData(rs.getString(ServerTableColumnsName.BRANCH_TABLE_APPLICATION_DATA)); + Date gmtCreateTimestamp = rs.getDate(ServerTableColumnsName.BRANCH_TABLE_GMT_CREATE); + if (gmtCreateTimestamp != null) { + branchSessionVO.setGmtCreate(gmtCreateTimestamp.getTime()); + } + Date gmtModifiedTimestamp = rs.getDate(ServerTableColumnsName.BRANCH_TABLE_GMT_MODIFIED); + if (gmtModifiedTimestamp != null) { + branchSessionVO.setGmtModified(gmtModifiedTimestamp.getTime()); + } + return branchSessionVO; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + BranchSessionVO that = (BranchSessionVO) o; + return Objects.equals(xid, that.xid) + && Objects.equals(transactionId, that.transactionId) + && Objects.equals(branchId, that.branchId) + && Objects.equals(resourceGroupId, that.resourceGroupId) + && Objects.equals(resourceId, that.resourceId) + && Objects.equals(branchType, that.branchType) + && Objects.equals(status, that.status) + && Objects.equals(clientId, that.clientId) + && Objects.equals(applicationData, that.applicationData) + && Objects.equals(gmtCreate, that.gmtCreate) + && Objects.equals(gmtModified, that.gmtModified); + } + + @Override + public int hashCode() { + return Objects.hash(xid, + transactionId, + branchId, + resourceGroupId, + resourceId, + branchType, + status, + clientId, + applicationData, + gmtCreate, + gmtModified); + } + + @Override + public String toString() { + return "BranchSessionVO{" + + "xid='" + xid + '\'' + + ", transactionId=" + transactionId + + ", branchId=" + branchId + + ", resourceGroupId='" + resourceGroupId + '\'' + + ", resourceId='" + resourceId + '\'' + + ", branchType='" + branchType + '\'' + + ", status=" + status + + ", clientId='" + clientId + '\'' + + ", applicationData='" + applicationData + '\'' + + ", gmtCreate=" + gmtCreate + + ", gmtModified=" + gmtModified + + '}'; + } +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/vo/GlobalLockVO.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/vo/GlobalLockVO.java new file mode 100644 index 000000000..265cc77c4 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/vo/GlobalLockVO.java @@ -0,0 +1,196 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.console.vo; + +import io.seata.common.util.CollectionUtils; +import io.seata.core.constants.ServerTableColumnsName; +import io.seata.core.lock.RowLock; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * GlobalLockVO + * @author zhongxiang.wang + * @author miaoxueyu + */ +public class GlobalLockVO { + + private String xid; + + private String transactionId; + + private String branchId; + + private String resourceId; + + private String tableName; + + private String pk; + + private String rowKey; + + private Long gmtCreate; + + private Long gmtModified; + + /** + * convert RowLock list to GlobalLockVO list + * @param rowLocks the RowLock list + * @return the GlobalLockVO list + */ + public static List convert(List rowLocks) { + if (CollectionUtils.isEmpty(rowLocks)) { + return Collections.emptyList(); + } + final List result = new ArrayList<>(rowLocks.size()); + for (RowLock rowLock : rowLocks) { + result.add(convert(rowLock)); + } + + return result; + } + + + /** + * convert RowLock to GlobalLockVO + * @param rowLock the RowLock + * @return the GlobalLockVO + */ + public static GlobalLockVO convert(RowLock rowLock) { + final GlobalLockVO globalLockVO = new GlobalLockVO(); + globalLockVO.setXid(rowLock.getXid()); + globalLockVO.setTransactionId(rowLock.getTransactionId()); + globalLockVO.setBranchId(rowLock.getBranchId()); + globalLockVO.setResourceId(rowLock.getResourceId()); + globalLockVO.setTableName(rowLock.getTableName()); + globalLockVO.setPk(rowLock.getPk()); + globalLockVO.setRowKey(rowLock.getRowKey()); + return globalLockVO; + } + + + public String getXid() { + return xid; + } + + public void setXid(String xid) { + this.xid = xid; + } + + public String getTransactionId() { + return transactionId; + } + + public void setTransactionId(Long transactionId) { + this.transactionId = String.valueOf(transactionId); + } + + public String getBranchId() { + return branchId; + } + + public void setBranchId(Long branchId) { + this.branchId = String.valueOf(branchId); + } + + public String getResourceId() { + return resourceId; + } + + public void setResourceId(String resourceId) { + this.resourceId = resourceId; + } + + public String getTableName() { + return tableName; + } + + public void setTableName(String tableName) { + this.tableName = tableName; + } + + public String getPk() { + return pk; + } + + public void setPk(String pk) { + this.pk = pk; + } + + public String getRowKey() { + return rowKey; + } + + public void setRowKey(String rowKey) { + this.rowKey = rowKey; + } + + public Long getGmtCreate() { + return gmtCreate; + } + + public void setGmtCreate(Long gmtCreate) { + this.gmtCreate = gmtCreate; + } + + public Long getGmtModified() { + return gmtModified; + } + + public void setGmtModified(Long gmtModified) { + this.gmtModified = gmtModified; + } + + public static GlobalLockVO convert(ResultSet rs) throws SQLException { + GlobalLockVO globalLockVO = new GlobalLockVO(); + globalLockVO.setRowKey(rs.getString(ServerTableColumnsName.LOCK_TABLE_ROW_KEY)); + globalLockVO.setXid(rs.getString(ServerTableColumnsName.LOCK_TABLE_XID)); + globalLockVO.setTransactionId(rs.getLong(ServerTableColumnsName.LOCK_TABLE_TRANSACTION_ID)); + globalLockVO.setBranchId(rs.getLong(ServerTableColumnsName.LOCK_TABLE_BRANCH_ID)); + globalLockVO.setResourceId(rs.getString(ServerTableColumnsName.LOCK_TABLE_RESOURCE_ID)); + globalLockVO.setTableName(rs.getString(ServerTableColumnsName.LOCK_TABLE_TABLE_NAME)); + globalLockVO.setPk(rs.getString(ServerTableColumnsName.LOCK_TABLE_PK)); + Timestamp gmtCreateTimestamp = rs.getTimestamp(ServerTableColumnsName.LOCK_TABLE_GMT_CREATE); + if (gmtCreateTimestamp != null) { + globalLockVO.setGmtCreate(gmtCreateTimestamp.getTime()); + } + Timestamp gmtModifiedTimestamp = rs.getTimestamp(ServerTableColumnsName.LOCK_TABLE_GMT_MODIFIED); + if (gmtModifiedTimestamp != null) { + globalLockVO.setGmtModified(gmtModifiedTimestamp.getTime()); + } + return globalLockVO; + } + + @Override + public String toString() { + return "GlobalLockVO{" + + "xid='" + xid + '\'' + + ", transactionId=" + transactionId + + ", branchId=" + branchId + + ", resourceId='" + resourceId + '\'' + + ", tableName='" + tableName + '\'' + + ", pk='" + pk + '\'' + + ", rowKey='" + rowKey + '\'' + + ", gmtCreate=" + gmtCreate + + ", gmtModified=" + gmtModified + + '}'; + } +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/vo/GlobalSessionVO.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/vo/GlobalSessionVO.java new file mode 100644 index 000000000..b040cb5f3 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/console/vo/GlobalSessionVO.java @@ -0,0 +1,217 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.console.vo; + +import io.seata.core.constants.ServerTableColumnsName; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.Set; + +/** + * GlobalSessionVO + * @author zhongxiang.wang + */ +public class GlobalSessionVO { + + private String xid; + + private String transactionId; + + private Integer status; + + private String applicationId; + + private String transactionServiceGroup; + + private String transactionName; + + private Long timeout; + + private Long beginTime; + + private String applicationData; + + private Long gmtCreate; + + private Long gmtModified; + + private Set branchSessionVOs; + + + public GlobalSessionVO() { + + } + + public GlobalSessionVO(String xid, + Long transactionId, + Integer status, + String applicationId, + String transactionServiceGroup, + String transactionName, + Long timeout, + Long beginTime, + String applicationData, + Set branchSessionVOs) { + this.xid = xid; + this.transactionId = String.valueOf(transactionId); + this.status = status; + this.applicationId = applicationId; + this.transactionServiceGroup = transactionServiceGroup; + this.transactionName = transactionName; + this.timeout = timeout; + this.beginTime = beginTime; + this.applicationData = applicationData; + this.branchSessionVOs = branchSessionVOs; + } + + public String getXid() { + return xid; + } + + public void setXid(String xid) { + this.xid = xid; + } + + public String getTransactionId() { + return transactionId; + } + + public void setTransactionId(Long transactionId) { + this.transactionId = String.valueOf(transactionId); + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getApplicationId() { + return applicationId; + } + + public void setApplicationId(String applicationId) { + this.applicationId = applicationId; + } + + public String getTransactionServiceGroup() { + return transactionServiceGroup; + } + + public void setTransactionServiceGroup(String transactionServiceGroup) { + this.transactionServiceGroup = transactionServiceGroup; + } + + public String getTransactionName() { + return transactionName; + } + + public void setTransactionName(String transactionName) { + this.transactionName = transactionName; + } + + public Long getTimeout() { + return timeout; + } + + public void setTimeout(Long timeout) { + this.timeout = timeout; + } + + public Long getBeginTime() { + return beginTime; + } + + public void setBeginTime(Long beginTime) { + this.beginTime = beginTime; + } + + public String getApplicationData() { + return applicationData; + } + + public void setApplicationData(String applicationData) { + this.applicationData = applicationData; + } + + public Long getGmtCreate() { + return gmtCreate; + } + + public void setGmtCreate(Long gmtCreate) { + this.gmtCreate = gmtCreate; + } + + public Long getGmtModified() { + return gmtModified; + } + + public void setGmtModified(Long gmtModified) { + this.gmtModified = gmtModified; + } + + public Set getBranchSessionVOs() { + return branchSessionVOs; + } + + public void setBranchSessionVOs(Set branchSessionVOs) { + this.branchSessionVOs = branchSessionVOs; + } + + public static GlobalSessionVO convert(ResultSet rs) throws SQLException { + GlobalSessionVO globalSessionVO = new GlobalSessionVO(); + globalSessionVO.setXid(rs.getString(ServerTableColumnsName.GLOBAL_TABLE_XID)); + globalSessionVO.setTransactionId(rs.getLong(ServerTableColumnsName.GLOBAL_TABLE_TRANSACTION_ID)); + globalSessionVO.setStatus(rs.getInt(ServerTableColumnsName.GLOBAL_TABLE_STATUS)); + globalSessionVO.setApplicationId(rs.getString(ServerTableColumnsName.GLOBAL_TABLE_APPLICATION_ID)); + globalSessionVO.setTransactionServiceGroup(rs.getString(ServerTableColumnsName.GLOBAL_TABLE_TRANSACTION_SERVICE_GROUP)); + globalSessionVO.setTransactionName(rs.getString(ServerTableColumnsName.GLOBAL_TABLE_TRANSACTION_NAME)); + globalSessionVO.setTimeout(rs.getLong(ServerTableColumnsName.GLOBAL_TABLE_TIMEOUT)); + globalSessionVO.setBeginTime(rs.getLong(ServerTableColumnsName.GLOBAL_TABLE_BEGIN_TIME)); + globalSessionVO.setApplicationData(rs.getString(ServerTableColumnsName.GLOBAL_TABLE_APPLICATION_DATA)); + Timestamp gmtCreateTimestamp = rs.getTimestamp(ServerTableColumnsName.GLOBAL_TABLE_GMT_CREATE); + if (gmtCreateTimestamp != null) { + globalSessionVO.setGmtCreate(gmtCreateTimestamp.getTime()); + } + Timestamp gmtModifiedTimestamp = rs.getTimestamp(ServerTableColumnsName.GLOBAL_TABLE_GMT_MODIFIED); + if (gmtModifiedTimestamp != null) { + globalSessionVO.setGmtModified(gmtModifiedTimestamp.getTime()); + } + return globalSessionVO; + } + + @Override + public String toString() { + return "GlobalSessionVO{" + + "xid='" + xid + '\'' + + ", transactionId=" + transactionId + + ", status=" + status + + ", applicationId='" + applicationId + '\'' + + ", transactionServiceGroup='" + transactionServiceGroup + '\'' + + ", transactionName='" + transactionName + '\'' + + ", timeout=" + timeout + + ", beginTime=" + beginTime + + ", applicationData='" + applicationData + '\'' + + ", gmtCreate=" + gmtCreate + + ", gmtModified=" + gmtModified + + ", branchSessionVOs=" + branchSessionVOs + + '}'; + } +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/controller/HealthController.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/controller/HealthController.java new file mode 100644 index 000000000..155045af3 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/controller/HealthController.java @@ -0,0 +1,43 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.controller; + +import io.seata.server.ServerRunner; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +/** + * @author spilledyear@outlook.com + */ +@Controller +@RequestMapping +public class HealthController { + + private static final String OK = "ok"; + private static final String NOT_OK = "not_ok"; + + @Autowired + private ServerRunner serverRunner; + + + @RequestMapping("/health") + @ResponseBody + String healthCheck() { + return serverRunner.started() ? OK : NOT_OK; + } +} \ No newline at end of file diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/coordinator/AbstractCore.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/coordinator/AbstractCore.java index 59247fc4b..e5d0b3b89 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/coordinator/AbstractCore.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/coordinator/AbstractCore.java @@ -15,9 +15,6 @@ */ package io.seata.server.coordinator; -import java.io.IOException; -import java.util.concurrent.TimeoutException; - import io.seata.core.context.RootContext; import io.seata.core.exception.BranchTransactionException; import io.seata.core.exception.GlobalTransactionException; @@ -41,12 +38,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; -import static io.seata.core.exception.TransactionExceptionCode.BranchTransactionNotExist; -import static io.seata.core.exception.TransactionExceptionCode.FailedToAddBranch; -import static io.seata.core.exception.TransactionExceptionCode.GlobalTransactionNotActive; -import static io.seata.core.exception.TransactionExceptionCode.GlobalTransactionStatusInvalid; -import static io.seata.core.exception.TransactionExceptionCode.FailedToSendBranchCommitRequest; -import static io.seata.core.exception.TransactionExceptionCode.FailedToSendBranchRollbackRequest; +import java.io.IOException; +import java.util.concurrent.TimeoutException; + +import static io.seata.core.exception.TransactionExceptionCode.*; /** * The type abstract core. @@ -55,201 +50,190 @@ import static io.seata.core.exception.TransactionExceptionCode.FailedToSendBranc */ public abstract class AbstractCore implements Core { - protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractCore.class); + protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractCore.class); - protected LockManager lockManager = LockerManagerFactory.getLockManager(); + protected LockManager lockManager = LockerManagerFactory.getLockManager(); - protected RemotingServer remotingServer; + protected RemotingServer remotingServer; - public AbstractCore(RemotingServer remotingServer) { - if (remotingServer == null) { - throw new IllegalArgumentException("remotingServer must be not null"); - } - this.remotingServer = remotingServer; - } + public AbstractCore(RemotingServer remotingServer) { + if (remotingServer == null) { + throw new IllegalArgumentException("remotingServer must be not null"); + } + this.remotingServer = remotingServer; + } - public abstract BranchType getHandleBranchType(); + 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(); - }); - } + @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 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 branchSessionLock(GlobalSession globalSession, BranchSession branchSession) throws TransactionException { - } + } - protected void branchSessionUnlock(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; - } + 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)); - } - globalSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); - globalSession.changeBranchStatus(branchSession, status); + @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()); - } - } + 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 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); - } - } + @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); - return response.getBranchStatus(); - } + protected BranchStatus branchCommitSend(BranchCommitRequest request, GlobalSession globalSession, + BranchSession branchSession) throws IOException, TimeoutException { + BranchCommitResponse response = (BranchCommitResponse) remotingServer.sendSyncRequest( + branchSession.getResourceId(), branchSession.getClientId(), request); + 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); - } - } + @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); - return response.getBranchStatus(); - } + protected BranchStatus branchRollbackSend(BranchRollbackRequest request, GlobalSession globalSession, + BranchSession branchSession) throws IOException, TimeoutException { + BranchRollbackResponse response = (BranchRollbackResponse) remotingServer.sendSyncRequest( + branchSession.getResourceId(), branchSession.getClientId(), request); + return response.getBranchStatus(); + } - @Override - public String begin(String applicationId, String transactionServiceGroup, String name, int timeout) - throws TransactionException { - return null; - } + @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 GlobalStatus commit(String xid) throws TransactionException { + return null; + } - @Override - public boolean doGlobalCommit(GlobalSession globalSession, boolean retrying) throws TransactionException { - return true; - } + @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 globalReport(String xid, GlobalStatus globalStatus) throws TransactionException { + return null; + } - @Override - public GlobalStatus rollback(String xid) 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 boolean doGlobalRollback(GlobalSession globalSession, boolean retrying) throws TransactionException { + return true; + } - @Override - public GlobalStatus getStatus(String xid) throws TransactionException { - return null; - } + @Override + public GlobalStatus getStatus(String xid) throws TransactionException { + return null; + } - @Override - public void doGlobalReport(GlobalSession globalSession, String xid, GlobalStatus globalStatus) - throws TransactionException { - - } + @Override + public void doGlobalReport(GlobalSession globalSession, String xid, GlobalStatus globalStatus) throws TransactionException { + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/coordinator/Core.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/coordinator/Core.java index 8def2e56f..8ad19cbd0 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/coordinator/Core.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/coordinator/Core.java @@ -26,31 +26,34 @@ import io.seata.server.session.GlobalSession; */ 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 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 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; + /** + * 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; } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/coordinator/DefaultCoordinator.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/coordinator/DefaultCoordinator.java index e3930085c..01223e823 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/coordinator/DefaultCoordinator.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/coordinator/DefaultCoordinator.java @@ -15,11 +15,6 @@ */ package io.seata.server.coordinator; -import java.time.Duration; -import java.util.Collection; -import java.util.Map; -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.TimeUnit; import io.netty.channel.Channel; import io.seata.common.thread.NamedThreadFactory; import io.seata.common.util.CollectionUtils; @@ -27,31 +22,11 @@ import io.seata.common.util.DurationUtil; import io.seata.config.ConfigurationFactory; import io.seata.core.constants.ConfigurationKeys; import io.seata.core.context.RootContext; -import io.seata.core.event.EventBus; -import io.seata.core.event.GlobalTransactionEvent; import io.seata.core.exception.TransactionException; import io.seata.core.model.GlobalStatus; import io.seata.core.protocol.AbstractMessage; import io.seata.core.protocol.AbstractResultMessage; -import io.seata.core.protocol.transaction.AbstractTransactionRequestToTC; -import io.seata.core.protocol.transaction.AbstractTransactionResponse; -import io.seata.core.protocol.transaction.BranchRegisterRequest; -import io.seata.core.protocol.transaction.BranchRegisterResponse; -import io.seata.core.protocol.transaction.BranchReportRequest; -import io.seata.core.protocol.transaction.BranchReportResponse; -import io.seata.core.protocol.transaction.GlobalBeginRequest; -import io.seata.core.protocol.transaction.GlobalBeginResponse; -import io.seata.core.protocol.transaction.GlobalCommitRequest; -import io.seata.core.protocol.transaction.GlobalCommitResponse; -import io.seata.core.protocol.transaction.GlobalLockQueryRequest; -import io.seata.core.protocol.transaction.GlobalLockQueryResponse; -import io.seata.core.protocol.transaction.GlobalReportRequest; -import io.seata.core.protocol.transaction.GlobalReportResponse; -import io.seata.core.protocol.transaction.GlobalRollbackRequest; -import io.seata.core.protocol.transaction.GlobalRollbackResponse; -import io.seata.core.protocol.transaction.GlobalStatusRequest; -import io.seata.core.protocol.transaction.GlobalStatusResponse; -import io.seata.core.protocol.transaction.UndoLogDeleteRequest; +import io.seata.core.protocol.transaction.*; import io.seata.core.rpc.Disposable; import io.seata.core.rpc.RemotingServer; import io.seata.core.rpc.RpcContext; @@ -59,469 +34,564 @@ import io.seata.core.rpc.TransactionMessageHandler; import io.seata.core.rpc.netty.ChannelManager; import io.seata.core.rpc.netty.NettyRemotingServer; import io.seata.server.AbstractTCInboundHandler; -import io.seata.server.event.EventBusManager; -import io.seata.server.session.GlobalSession; -import io.seata.server.session.SessionHelper; -import io.seata.server.session.SessionHolder; +import io.seata.server.metrics.MetricsPublisher; +import io.seata.server.session.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; +import java.time.Duration; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import static io.seata.common.Constants.*; +import static io.seata.common.DefaultValues.*; + /** * The type Default coordinator. */ public class DefaultCoordinator extends AbstractTCInboundHandler implements TransactionMessageHandler, Disposable { - private static final Logger LOGGER = LoggerFactory.getLogger(DefaultCoordinator.class); + private static final Logger LOGGER = LoggerFactory.getLogger(DefaultCoordinator.class); - private static final int TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS = 5000; + 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, - 1000L); + /** + * 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.ASYN_COMMITING_RETRY_PERIOD, 1000L); + /** + * 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, - 1000L); + /** + * 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, 1000L); + /** + * 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, 24 * 60 * 60 * 1000); + /** + * 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; + /** + * 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; + private static final int ALWAYS_RETRY_BOUNDARY = 0; - private static final Duration MAX_COMMIT_RETRY_TIMEOUT = ConfigurationFactory.getInstance() - .getDuration(ConfigurationKeys.MAX_COMMIT_RETRY_TIMEOUT, DurationUtil.DEFAULT_DURATION, 100); + /** + * default branch async queue size + */ + private static final int DEFAULT_BRANCH_ASYNC_QUEUE_SIZE = 5000; - private static final Duration MAX_ROLLBACK_RETRY_TIMEOUT = ConfigurationFactory.getInstance() - .getDuration(ConfigurationKeys.MAX_ROLLBACK_RETRY_TIMEOUT, DurationUtil.DEFAULT_DURATION, 100); + /** + * the pool size of branch asynchronous remove thread pool + */ + private static final int BRANCH_ASYNC_POOL_SIZE = Runtime.getRuntime().availableProcessors(); - private static final boolean ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE = ConfigurationFactory.getInstance() - .getBoolean(ConfigurationKeys.ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE, false); + private static final Duration MAX_COMMIT_RETRY_TIMEOUT = ConfigurationFactory.getInstance().getDuration( + ConfigurationKeys.MAX_COMMIT_RETRY_TIMEOUT, DurationUtil.DEFAULT_DURATION, DEFAULT_MAX_COMMIT_RETRY_TIMEOUT); - private ScheduledThreadPoolExecutor retryRollbacking = new ScheduledThreadPoolExecutor(1, - new NamedThreadFactory("RetryRollbacking", 1)); + private static final Duration MAX_ROLLBACK_RETRY_TIMEOUT = ConfigurationFactory.getInstance().getDuration( + ConfigurationKeys.MAX_ROLLBACK_RETRY_TIMEOUT, DurationUtil.DEFAULT_DURATION, DEFAULT_MAX_ROLLBACK_RETRY_TIMEOUT); - private ScheduledThreadPoolExecutor retryCommitting = new ScheduledThreadPoolExecutor(1, - new NamedThreadFactory("RetryCommitting", 1)); + private static final boolean ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE = ConfigurationFactory.getInstance().getBoolean( + ConfigurationKeys.ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE, DEFAULT_ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE); - private ScheduledThreadPoolExecutor asyncCommitting = new ScheduledThreadPoolExecutor(1, - new NamedThreadFactory("AsyncCommitting", 1)); + private final ScheduledThreadPoolExecutor retryRollbacking = + new ScheduledThreadPoolExecutor(1, new NamedThreadFactory(RETRY_ROLLBACKING, 1)); - private ScheduledThreadPoolExecutor timeoutCheck = new ScheduledThreadPoolExecutor(1, - new NamedThreadFactory("TxTimeoutCheck", 1)); + private final ScheduledThreadPoolExecutor retryCommitting = + new ScheduledThreadPoolExecutor(1, new NamedThreadFactory(RETRY_COMMITTING, 1)); - private ScheduledThreadPoolExecutor undoLogDelete = new ScheduledThreadPoolExecutor(1, - new NamedThreadFactory("UndoLogDelete", 1)); + private final ScheduledThreadPoolExecutor asyncCommitting = + new ScheduledThreadPoolExecutor(1, new NamedThreadFactory(ASYNC_COMMITTING, 1)); - private RemotingServer remotingServer; + private final ScheduledThreadPoolExecutor timeoutCheck = + new ScheduledThreadPoolExecutor(1, new NamedThreadFactory(TX_TIMEOUT_CHECK, 1)); - private DefaultCore core; + private final ScheduledThreadPoolExecutor undoLogDelete = + new ScheduledThreadPoolExecutor(1, new NamedThreadFactory(UNDOLOG_DELETE, 1)); - private EventBus eventBus = EventBusManager.get(); + private final GlobalStatus[] rollbackingStatuses = new GlobalStatus[] {GlobalStatus.TimeoutRollbacking, + GlobalStatus.TimeoutRollbackRetrying, GlobalStatus.RollbackRetrying, GlobalStatus.Rollbacking}; - /** - * Instantiates a new Default coordinator. - * @param remotingServer the remoting server - */ - public DefaultCoordinator(RemotingServer remotingServer) { - this.remotingServer = remotingServer; - this.core = new DefaultCore(remotingServer); - } + private final GlobalStatus[] retryCommittingStatuses = + new GlobalStatus[] {GlobalStatus.Committing, GlobalStatus.CommitRetrying}; - @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()); - } - } + private final ThreadPoolExecutor branchRemoveExecutor = new ThreadPoolExecutor(BRANCH_ASYNC_POOL_SIZE, BRANCH_ASYNC_POOL_SIZE, + Integer.MAX_VALUE, TimeUnit.MILLISECONDS, + new ArrayBlockingQueue<>( + CONFIG.getInt(ConfigurationKeys.SESSION_BRANCH_ASYNC_QUEUE_SIZE, DEFAULT_BRANCH_ASYNC_QUEUE_SIZE) + ), new NamedThreadFactory("branchSessionRemove", BRANCH_ASYNC_POOL_SIZE), + new ThreadPoolExecutor.CallerRunsPolicy()); - @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())); - } + private RemotingServer remotingServer; - @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())); - } + private final DefaultCore core; - @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())); - } + private static volatile DefaultCoordinator instance; - @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())); - } + /** + * 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); + } - @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())); - } + public static DefaultCoordinator getInstance(RemotingServer remotingServer) { + if (null == instance) { + synchronized (DefaultCoordinator.class) { + if (null == instance) { + instance = new DefaultCoordinator(remotingServer); + } + } + } + return instance; + } - @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()); - } + public static DefaultCoordinator getInstance() { + if (null == instance) { + throw new IllegalArgumentException("The instance has not been created."); + } + return instance; + } - @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())); - } + /** + * 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)); + } - /** - * Timeout check. - * @throws TransactionException the transaction exception - */ - protected void timeoutCheck() throws TransactionException { - Collection allSessions = SessionHolder.getRootSessionManager().allSessions(); - if (CollectionUtils.isEmpty(allSessions)) { - return; - } - if (allSessions.size() > 0 && LOGGER.isDebugEnabled()) { - LOGGER.debug("Global transaction timeout check begin, size: {}", allSessions.size()); - } - SessionHelper.forEach(allSessions, globalSession -> { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug(globalSession.getXid() + " " + globalSession.getStatus() + " " - + globalSession.getBeginTime() + " " + globalSession.getTimeout()); - } - boolean shouldTimeout = SessionHolder.lockAndExecute(globalSession, () -> { - if (globalSession.getStatus() != GlobalStatus.Begin || !globalSession.isTimeout()) { - return false; - } - globalSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); - globalSession.close(); - globalSession.changeStatus(GlobalStatus.TimeoutRollbacking); + /** + * Asynchronous remove all branch + * + * @param globalSession the globalSession + */ + public void doBranchRemoveAllAsync(GlobalSession globalSession) { + if (globalSession == null) { + return; + } + branchRemoveExecutor.execute(new BranchRemoveTask(globalSession)); + } - // transaction timeout and start rollbacking event - eventBus.post(new GlobalTransactionEvent(globalSession.getTransactionId(), - GlobalTransactionEvent.ROLE_TC, globalSession.getTransactionName(), - globalSession.getApplicationId(), globalSession.getTransactionServiceGroup(), - globalSession.getBeginTime(), null, globalSession.getStatus())); + @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()); + } + } - return true; - }); - if (!shouldTimeout) { - // The function of this 'return' is 'continue'. - return; - } - LOGGER.info("Global transaction[{}] is timeout and will be rollback.", globalSession.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())); + } - globalSession.addSessionLifecycleListener(SessionHolder.getRetryRollbackingSessionManager()); - SessionHolder.getRetryRollbackingSessionManager().addGlobalSession(globalSession); - }); - if (allSessions.size() > 0 && LOGGER.isDebugEnabled()) { - LOGGER.debug("Global transaction timeout check end. "); - } + @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())); + } - /** - * Handle retry rollbacking. - */ - protected void handleRetryRollbacking() { - Collection rollbackingSessions = SessionHolder.getRetryRollbackingSessionManager().allSessions(); - if (CollectionUtils.isEmpty(rollbackingSessions)) { - return; - } - long now = System.currentTimeMillis(); - SessionHelper.forEach(rollbackingSessions, rollbackingSession -> { - try { - // prevent repeated rollback - if (rollbackingSession.getStatus().equals(GlobalStatus.Rollbacking) - && !rollbackingSession.isDeadSession()) { - // The function of this 'return' is 'continue'. - return; - } - if (isRetryTimeout(now, MAX_ROLLBACK_RETRY_TIMEOUT.toMillis(), rollbackingSession.getBeginTime())) { - if (ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE) { - rollbackingSession.clean(); - } - /** - * Prevent thread safety issues - */ - SessionHolder.getRetryRollbackingSessionManager().removeGlobalSession(rollbackingSession); - LOGGER.info("Global transaction rollback retry timeout and has removed [{}]", - rollbackingSession.getXid()); - // The function of this 'return' is 'continue'. - return; - } - rollbackingSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); - core.doGlobalRollback(rollbackingSession, true); - } - catch (TransactionException ex) { - LOGGER.info("Failed to retry rollbacking [{}] {} {}", rollbackingSession.getXid(), ex.getCode(), - ex.getMessage()); - } - }); - } + @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())); + } - /** - * Handle retry committing. - */ - protected void handleRetryCommitting() { - Collection committingSessions = SessionHolder.getRetryCommittingSessionManager().allSessions(); - if (CollectionUtils.isEmpty(committingSessions)) { - return; - } - long now = System.currentTimeMillis(); - SessionHelper.forEach(committingSessions, committingSession -> { - try { - // prevent repeated commit - if (committingSession.getStatus().equals(GlobalStatus.Committing) - && !committingSession.isDeadSession()) { - // The function of this 'return' is 'continue'. - return; - } - if (isRetryTimeout(now, MAX_COMMIT_RETRY_TIMEOUT.toMillis(), committingSession.getBeginTime())) { - /** - * Prevent thread safety issues - */ - SessionHolder.getRetryCommittingSessionManager().removeGlobalSession(committingSession); - LOGGER.error("Global transaction commit retry timeout and has removed [{}]", - committingSession.getXid()); - // The function of this 'return' is 'continue'. - return; - } - committingSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); - core.doGlobalCommit(committingSession, true); - } - catch (TransactionException ex) { - LOGGER.info("Failed to retry committing [{}] {} {}", committingSession.getXid(), ex.getCode(), - ex.getMessage()); - } - }); - } + @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())); + } - private boolean isRetryTimeout(long now, long timeout, long beginTime) { - return timeout >= ALWAYS_RETRY_BOUNDARY && now - beginTime > timeout; - } + @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()); + } - /** - * Handle async committing. - */ - protected void handleAsyncCommitting() { - Collection asyncCommittingSessions = SessionHolder.getAsyncCommittingSessionManager() - .allSessions(); - if (CollectionUtils.isEmpty(asyncCommittingSessions)) { - return; - } - SessionHelper.forEach(asyncCommittingSessions, asyncCommittingSession -> { - try { - // Instruction reordering in DefaultCore#asyncCommit may cause this - // situation - if (GlobalStatus.AsyncCommitting != asyncCommittingSession.getStatus()) { - // The function of this 'return' is 'continue'. - return; - } - asyncCommittingSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); - core.doGlobalCommit(asyncCommittingSession, true); - } - catch (TransactionException ex) { - LOGGER.error("Failed to async committing [{}] {} {}", asyncCommittingSession.getXid(), ex.getCode(), - ex.getMessage(), ex); - } - }); - } + @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())); + } - /** - * Undo log delete. - */ - protected void undoLogDelete() { - Map 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 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()); - } - } - } + /** + * Timeout check. + */ + protected void timeoutCheck() { + SessionCondition sessionCondition = new SessionCondition(GlobalStatus.Begin); + sessionCondition.setLazyLoadBranch(true); + Collection 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; + } - /** - * Init. - */ - public void init() { - retryRollbacking.scheduleAtFixedRate(() -> { - boolean lock = SessionHolder.retryRollbackingLock(); - if (lock) { - try { - handleRetryRollbacking(); - } - catch (Exception e) { - LOGGER.info("Exception retry rollbacking ... ", e); - } - finally { - SessionHolder.unRetryRollbackingLock(); - } - } - }, 0, ROLLBACKING_RETRY_PERIOD, TimeUnit.MILLISECONDS); + LOGGER.info("Global transaction[{}] is timeout and will be rollback.", globalSession.getXid()); - retryCommitting.scheduleAtFixedRate(() -> { - boolean lock = SessionHolder.retryCommittingLock(); - if (lock) { - try { - handleRetryCommitting(); - } - catch (Exception e) { - LOGGER.info("Exception retry committing ... ", e); - } - finally { - SessionHolder.unRetryCommittingLock(); - } - } - }, 0, COMMITTING_RETRY_PERIOD, TimeUnit.MILLISECONDS); + globalSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); + globalSession.close(); + globalSession.setStatus(GlobalStatus.TimeoutRollbacking); - asyncCommitting.scheduleAtFixedRate(() -> { - boolean lock = SessionHolder.asyncCommittingLock(); - if (lock) { - try { - handleAsyncCommitting(); - } - catch (Exception e) { - LOGGER.info("Exception async committing ... ", e); - } - finally { - SessionHolder.unAsyncCommittingLock(); - } - } - }, 0, ASYNC_COMMITTING_RETRY_PERIOD, TimeUnit.MILLISECONDS); + globalSession.addSessionLifecycleListener(SessionHolder.getRetryRollbackingSessionManager()); + SessionHolder.getRetryRollbackingSessionManager().addGlobalSession(globalSession); - timeoutCheck.scheduleAtFixedRate(() -> { - boolean lock = SessionHolder.txTimeoutCheckLock(); - if (lock) { - try { - timeoutCheck(); - } - catch (Exception e) { - LOGGER.info("Exception timeout checking ... ", e); - } - finally { - SessionHolder.unTxTimeoutCheckLock(); - } - } - }, 0, TIMEOUT_RETRY_PERIOD, TimeUnit.MILLISECONDS); + // transaction timeout and start rollbacking event + MetricsPublisher.postSessionDoingEvent(globalSession, GlobalStatus.TimeoutRollbacking.name(), false, false); - undoLogDelete.scheduleAtFixedRate(() -> { - boolean lock = SessionHolder.undoLogDeleteLock(); - if (lock) { - try { - undoLogDelete(); - } - catch (Exception e) { - LOGGER.info("Exception undoLog deleting ... ", e); - } - finally { - SessionHolder.unUndoLogDeleteLock(); - } - } - }, UNDO_LOG_DELAY_DELETE_PERIOD, UNDO_LOG_DELETE_PERIOD, TimeUnit.MILLISECONDS); - } + return true; + }); + }); + if (!beginGlobalsessions.isEmpty() && LOGGER.isDebugEnabled()) { + LOGGER.debug("Global transaction timeout check end. "); + } - @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(); - } + /** + * Handle retry rollbacking. + */ + protected void handleRetryRollbacking() { + SessionCondition sessionCondition = new SessionCondition(rollbackingStatuses); + sessionCondition.setLazyLoadBranch(true); + Collection 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.toMillis(), rollbackingSession.getBeginTime())) { + if (ROLLBACK_RETRY_TIMEOUT_UNLOCK_ENABLE) { + rollbackingSession.clean(); + } + // Prevent thread safety issues + SessionHolder.getRetryRollbackingSessionManager().removeGlobalSession(rollbackingSession); + LOGGER.error("Global transaction rollback retry timeout and has removed [{}]", rollbackingSession.getXid()); - } + SessionHelper.endRollbackFailed(rollbackingSession, true, true); - @Override - public void destroy() { - // 1. first shutdown timed task - retryRollbacking.shutdown(); - retryCommitting.shutdown(); - asyncCommitting.shutdown(); - timeoutCheck.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); - } - catch (InterruptedException ignore) { + //The function of this 'return' is 'continue'. + return; + } + rollbackingSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); + core.doGlobalRollback(rollbackingSession, true); + } catch (TransactionException ex) { + LOGGER.info("Failed to retry rollbacking [{}] {} {}", rollbackingSession.getXid(), ex.getCode(), ex.getMessage()); + } + }); + } - } - // 2. second close netty flow - if (remotingServer instanceof NettyRemotingServer) { - ((NettyRemotingServer) remotingServer).destroy(); - } - // 3. last destroy SessionHolder - SessionHolder.destroy(); - } + /** + * Handle retry committing. + */ + protected void handleRetryCommitting() { + SessionCondition retryCommittingSessionCondition = new SessionCondition(retryCommittingStatuses); + retryCommittingSessionCondition.setLazyLoadBranch(true); + Collection committingSessions = + SessionHolder.getRetryCommittingSessionManager().findGlobalSessions(retryCommittingSessionCondition); + if (CollectionUtils.isEmpty(committingSessions)) { + return; + } + long now = System.currentTimeMillis(); + SessionHelper.forEach(committingSessions, committingSession -> { + try { + // prevent repeated commit + if (committingSession.getStatus() == GlobalStatus.Committing + && !committingSession.isDeadSession()) { + // The function of this 'return' is 'continue'. + return; + } + if (isRetryTimeout(now, MAX_COMMIT_RETRY_TIMEOUT.toMillis(), committingSession.getBeginTime())) { + // Prevent thread safety issues + SessionHolder.getRetryCommittingSessionManager().removeGlobalSession(committingSession); + LOGGER.error("Global transaction commit retry timeout and has removed [{}]", committingSession.getXid()); + // commit retry timeout event + SessionHelper.endCommitFailed(committingSession, true, true); + + //The function of this 'return' is 'continue'. + return; + } + committingSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); + core.doGlobalCommit(committingSession, true); + } catch (TransactionException ex) { + LOGGER.info("Failed to retry committing [{}] {} {}", committingSession.getXid(), ex.getCode(), ex.getMessage()); + } + }); + } + + /** + * Handle async committing. + */ + protected void handleAsyncCommitting() { + SessionCondition sessionCondition = new SessionCondition(GlobalStatus.AsyncCommitting); + Collection 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 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 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(); + branchRemoveExecutor.shutdown(); + try { + retryRollbacking.awaitTermination(TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS, TimeUnit.MILLISECONDS); + retryCommitting.awaitTermination(TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS, TimeUnit.MILLISECONDS); + asyncCommitting.awaitTermination(TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS, TimeUnit.MILLISECONDS); + timeoutCheck.awaitTermination(TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS, TimeUnit.MILLISECONDS); + undoLogDelete.awaitTermination(TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS, TimeUnit.MILLISECONDS); + branchRemoveExecutor.awaitTermination(TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS, TimeUnit.MILLISECONDS); + } catch (InterruptedException ignore) { + + } + // 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; + this.branchSession = branchSession; + } + + /** + * If you use this construct, the task will remove all branchSession + * @param globalSession the globalSession + */ + public BranchRemoveTask(GlobalSession globalSession) { + this.globalSession = globalSession; + this.branchSession = null; + } + + @Override + public void run() { + if (globalSession == null) { + return; + } + try { + MDC.put(RootContext.MDC_KEY_XID, globalSession.getXid()); + if (branchSession != null) { + doRemove(branchSession); + } else { + globalSession.getSortedBranches().forEach(this::doRemove); + } + } catch (Exception unKnowException) { + LOGGER.error("Asynchronous delete branchSession error, xid = {}", globalSession.getXid(), unKnowException); + } finally { + MDC.remove(RootContext.MDC_KEY_XID); + } + } + + private void doRemove(BranchSession bt) { + try { + MDC.put(RootContext.MDC_KEY_BRANCH_ID, String.valueOf(bt.getBranchId())); + globalSession.removeBranch(bt); + LOGGER.info("Asynchronous delete branchSession successfully, xid = {}, branchId = {}", + globalSession.getXid(), bt.getBranchId()); + } catch (TransactionException transactionException) { + LOGGER.error("Asynchronous delete branchSession error, xid = {}, branchId = {}", + globalSession.getXid(), bt.getBranchId(), transactionException); + } finally { + MDC.remove(RootContext.MDC_KEY_BRANCH_ID); + } + } + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/coordinator/DefaultCore.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/coordinator/DefaultCore.java index 45492891e..4998bd374 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/coordinator/DefaultCore.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/coordinator/DefaultCore.java @@ -15,23 +15,19 @@ */ package io.seata.server.coordinator; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - +import io.seata.common.DefaultValues; import io.seata.common.exception.NotSupportYetException; import io.seata.common.loader.EnhancedServiceLoader; import io.seata.common.util.CollectionUtils; +import io.seata.config.ConfigurationFactory; import io.seata.core.context.RootContext; -import io.seata.core.event.EventBus; -import io.seata.core.event.GlobalTransactionEvent; import io.seata.core.exception.TransactionException; import io.seata.core.logger.StackTraceLogger; import io.seata.core.model.BranchStatus; import io.seata.core.model.BranchType; import io.seata.core.model.GlobalStatus; import io.seata.core.rpc.RemotingServer; -import io.seata.server.event.EventBusManager; +import io.seata.server.metrics.MetricsPublisher; import io.seata.server.session.BranchSession; import io.seata.server.session.GlobalSession; import io.seata.server.session.SessionHelper; @@ -40,6 +36,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import static io.seata.core.constants.ConfigurationKeys.XAER_NOTA_RETRY_TIMEOUT; import static io.seata.server.session.BranchSessionHandler.CONTINUE; /** @@ -49,381 +50,349 @@ import static io.seata.server.session.BranchSessionHandler.CONTINUE; */ public class DefaultCore implements Core { - private static final Logger LOGGER = LoggerFactory.getLogger(DefaultCore.class); + private static final Logger LOGGER = LoggerFactory.getLogger(DefaultCore.class); - private EventBus eventBus = EventBusManager.get(); + private static final int RETRY_XAER_NOTA_TIMEOUT = ConfigurationFactory.getInstance().getInt(XAER_NOTA_RETRY_TIMEOUT, + DefaultValues.DEFAULT_XAER_NOTA_RETRY_TIMEOUT); - private static Map coreMap = new ConcurrentHashMap<>(); + private static Map coreMap = new ConcurrentHashMap<>(); - /** - * get the Default core. - * @param remotingServer the remoting server - */ - public DefaultCore(RemotingServer remotingServer) { - List 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 the Default core. + * + * @param remotingServer the remoting server + */ + public DefaultCore(RemotingServer remotingServer) { + List 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; - } + /** + * 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); - } + /** + * 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 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 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 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 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 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()); + @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(); + session.begin(); - // transaction start event - eventBus.post(new GlobalTransactionEvent(session.getTransactionId(), GlobalTransactionEvent.ROLE_TC, - session.getTransactionName(), applicationId, transactionServiceGroup, session.getBeginTime(), null, - session.getStatus())); + // transaction start event + MetricsPublisher.postSessionDoingEvent(session, false); - return session.getXid(); - } + return session.getXid(); + } - @Override - public GlobalStatus commit(String xid) throws TransactionException { - GlobalSession globalSession = SessionHolder.findGlobalSession(xid); - if (globalSession == null) { - return GlobalStatus.Finished; - } - globalSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); - // just lock changeStatus - boolean shouldCommit = SessionHolder.lockAndExecute(globalSession, () -> { - // Highlight: Firstly, close the session, then no more branch can be - // registered. - globalSession.closeAndClean(); - if (globalSession.getStatus() == GlobalStatus.Begin) { - if (globalSession.canBeCommittedAsync()) { - globalSession.asyncCommit(); - return false; - } - else { - globalSession.changeStatus(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 GlobalStatus commit(String xid) throws TransactionException { + GlobalSession globalSession = SessionHolder.findGlobalSession(xid); + if (globalSession == null) { + return GlobalStatus.Finished; + } - @Override - public boolean doGlobalCommit(GlobalSession globalSession, boolean retrying) throws TransactionException { - boolean success = true; - // start committing event - eventBus.post(new GlobalTransactionEvent(globalSession.getTransactionId(), GlobalTransactionEvent.ROLE_TC, - globalSession.getTransactionName(), globalSession.getApplicationId(), - globalSession.getTransactionServiceGroup(), globalSession.getBeginTime(), null, - globalSession.getStatus())); + if (globalSession.isTimeout()) { + LOGGER.info("TC detected timeout, xid = {}", globalSession.getXid()); + return GlobalStatus.TimeoutRollbacking; + } - 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; - } + globalSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); + // just lock changeStatus - BranchStatus currentStatus = branchSession.getStatus(); - if (currentStatus == BranchStatus.PhaseOne_Failed) { - globalSession.removeBranch(branchSession); - return CONTINUE; - } - try { - BranchStatus branchStatus = getCore(branchSession.getBranchType()).branchCommit(globalSession, - branchSession); + 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; + }); - switch (branchStatus) { - case PhaseTwo_Committed: - globalSession.removeBranch(branchSession); - return CONTINUE; - case PhaseTwo_CommitFailed_Unretryable: - if (globalSession.canBeCommittedAsync()) { - LOGGER.error( - "Committing branch transaction[{}], status: PhaseTwo_CommitFailed_Unretryable, please check the business log.", - branchSession.getBranchId()); - return CONTINUE; - } - else { - SessionHelper.endCommitFailed(globalSession); - LOGGER.error( - "Committing global transaction[{}] finally failed, caused by branch transaction[{}] commit failed.", - globalSession.getXid(), branchSession.getBranchId()); - return false; - } - 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 success and there is no branch, end the global transaction. - if (success && globalSession.getBranchSessions().isEmpty()) { - SessionHelper.endCommitted(globalSession); + 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(); + } + } - // committed event - eventBus.post(new GlobalTransactionEvent(globalSession.getTransactionId(), GlobalTransactionEvent.ROLE_TC, - globalSession.getTransactionName(), globalSession.getApplicationId(), - globalSession.getTransactionServiceGroup(), globalSession.getBeginTime(), - System.currentTimeMillis(), globalSession.getStatus())); + @Override + public boolean doGlobalCommit(GlobalSession globalSession, boolean retrying) throws TransactionException { + boolean success = true; + // start committing event + MetricsPublisher.postSessionDoingEvent(globalSession, retrying); - LOGGER.info("Committing global transaction is successfully done, xid = {}.", globalSession.getXid()); - } - return success; - } + 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; + } - @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.changeStatus(GlobalStatus.Rollbacking); - return true; - } - return false; - }); - if (!shouldRollBack) { - return globalSession.getStatus(); - } + 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); + 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; - doGlobalRollback(globalSession, false); - return globalSession.getStatus(); - } + 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 boolean doGlobalRollback(GlobalSession globalSession, boolean retrying) throws TransactionException { - boolean success = true; - // start rollback event - eventBus.post(new GlobalTransactionEvent(globalSession.getTransactionId(), GlobalTransactionEvent.ROLE_TC, - globalSession.getTransactionName(), globalSession.getApplicationId(), - globalSession.getTransactionServiceGroup(), globalSession.getBeginTime(), null, - globalSession.getStatus())); + @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(); + } - 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) { - globalSession.removeBranch(branchSession); - return CONTINUE; - } - try { - BranchStatus branchStatus = branchRollback(globalSession, branchSession); - switch (branchStatus) { - case PhaseTwo_Rollbacked: - globalSession.removeBranch(branchSession); - LOGGER.info("Rollback branch transaction successfully, xid = {} branchId = {}", - globalSession.getXid(), branchSession.getBranchId()); - return CONTINUE; - case PhaseTwo_RollbackFailed_Unretryable: - SessionHelper.endRollbackFailed(globalSession); - LOGGER.info("Rollback branch transaction fail and stop retry, xid = {} branchId = {}", - globalSession.getXid(), branchSession.getBranchId()); - return false; - default: - LOGGER.info("Rollback branch transaction fail and will retry, xid = {} branchId = {}", - globalSession.getXid(), branchSession.getBranchId()); - 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; - } + @Override + public boolean doGlobalRollback(GlobalSession globalSession, boolean retrying) throws TransactionException { + boolean success = true; + // start rollback event + MetricsPublisher.postSessionDoingEvent(globalSession, retrying); - // In db mode, there is a problem of inconsistent data in multiple copies, - // resulting in new branch - // transaction registration when rolling back. - // 1. New branch transaction and rollback branch transaction have no data - // association - // 2. New branch transaction has data association with rollback branch - // transaction - // The second query can solve the first problem, and if it is the second - // problem, it may cause a rollback - // failure due to data changes. - GlobalSession globalSessionTwice = SessionHolder.findGlobalSession(globalSession.getXid()); - if (globalSessionTwice != null && globalSessionTwice.hasBranch()) { - LOGGER.info("Rollbacking global transaction is NOT done, xid = {}.", globalSession.getXid()); - return false; - } - } - if (success) { - SessionHelper.endRollbacked(globalSession); + 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.info("Rollback branch transaction fail and stop retry, xid = {} branchId = {}", globalSession.getXid(), branchSession.getBranchId()); + return false; + default: + LOGGER.info("Rollback branch transaction fail and will retry, xid = {} branchId = {}", globalSession.getXid(), branchSession.getBranchId()); + 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; + } + } - // rollbacked event - eventBus.post(new GlobalTransactionEvent(globalSession.getTransactionId(), GlobalTransactionEvent.ROLE_TC, - globalSession.getTransactionName(), globalSession.getApplicationId(), - globalSession.getTransactionServiceGroup(), globalSession.getBeginTime(), - System.currentTimeMillis(), globalSession.getStatus())); + // 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; + } - 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 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 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); - } - } + @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; + } + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/coordinator/TransactionCoordinatorOutbound.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/coordinator/TransactionCoordinatorOutbound.java index 042e0d955..a213bd403 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/coordinator/TransactionCoordinatorOutbound.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/coordinator/TransactionCoordinatorOutbound.java @@ -28,24 +28,27 @@ import io.seata.server.session.GlobalSession; */ 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; + /** + * 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; - /** - * 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; } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/env/ContainerHelper.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/env/ContainerHelper.java index b7dd20149..a95242da3 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/env/ContainerHelper.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/env/ContainerHelper.java @@ -18,13 +18,7 @@ package io.seata.server.env; import io.seata.common.util.NumberUtils; import io.seata.common.util.StringUtils; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.stream.Stream; - -import static io.seata.common.DefaultValues.SERVER_DEFAULT_PORT; +import static io.seata.core.constants.ConfigurationKeys.ENV_SEATA_PORT_KEY; /** * @author xingfudeshi@gmail.com @@ -32,78 +26,78 @@ import static io.seata.common.DefaultValues.SERVER_DEFAULT_PORT; */ public class ContainerHelper { - private static final String C_GROUP_PATH = "/proc/1/cgroup"; + 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 DOCKER_PATH = "/docker"; + 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"; - private static final String KUBEPODS_PATH = "/kubepods"; + /** + * Gets env from container. + * + * @return the env + */ + public static String getEnv() { + return StringUtils.trimToNull(System.getenv(ENV_SYSTEM_KEY)); + } - private static final String ENV_SYSTEM_KEY = "SEATA_ENV"; + /** + * Gets host from container. + * + * @return the env + */ + public static String getHost() { + return StringUtils.trimToNull(System.getenv(ENV_SEATA_IP_KEY)); + } - private static final String ENV_SEATA_IP_KEY = "SEATA_IP"; + /** + * Gets port from container. + * + * @return the env + */ + public static int getPort() { + return NumberUtils.toInt(System.getenv(ENV_SEATA_PORT_KEY), 0); + } - private static final String ENV_SERVER_NODE_KEY = "SERVER_NODE"; + /** + * Gets server node from container. + * + * @return the env + */ + public static Long getServerNode() { + return NumberUtils.toLong(System.getenv(ENV_SERVER_NODE_KEY)); + } - private static final String ENV_SEATA_PORT_KEY = "SEATA_PORT"; + /** + * Gets store mode from container. + * + * @return the env + */ + public static String getStoreMode() { + return StringUtils.trimToNull(System.getenv(ENV_STORE_MODE_KEY)); + } - private static final String ENV_STORE_MODE_KEY = "STORE_MODE"; + /** + * Gets session store mode from container. + * + * @return the env + */ + public static String getSessionStoreMode() { + return StringUtils.trimToNull(System.getenv(ENV_SESSION_STORE_MODE_KEY)); + } - /** - * Judge if application is run in container. - * @return If application is run in container - */ - public static boolean isRunningInContainer() { - Path path = Paths.get(C_GROUP_PATH); - if (Files.exists(path)) { - try (Stream stream = Files.lines(path)) { - return stream.anyMatch(line -> line.contains(DOCKER_PATH) || line.contains(KUBEPODS_PATH)); - } - catch (IOException e) { - System.err.println("Judge if running in container failed: " + e.getMessage()); - e.printStackTrace(); - } - } - return false; - } - - /** - * 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), SERVER_DEFAULT_PORT); - } - - /** - * 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 lock store mode from container. + * + * @return the env + */ + public static String getLockStoreMode() { + return StringUtils.trimToNull(System.getenv(ENV_LOCK_STORE_MODE_KEY)); + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/env/PortHelper.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/env/PortHelper.java index 58fc56a07..e3c24733e 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/env/PortHelper.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/env/PortHelper.java @@ -15,28 +15,116 @@ */ package io.seata.server.env; +import io.seata.common.util.CollectionUtils; +import io.seata.common.util.MapUtil; import io.seata.common.util.NumberUtils; +import io.seata.common.util.StringUtils; +import org.springframework.util.ResourceUtils; +import org.yaml.snakeyaml.Yaml; -import static io.seata.common.DefaultValues.SERVER_DEFAULT_PORT; +import java.io.*; +import java.util.Map; +import java.util.Properties; /** * @author wang.liang */ public class PortHelper { - public static int getPort(String[] args) { - if (ContainerHelper.isRunningInContainer()) { - return ContainerHelper.getPort(); - } - else if (args != null && args.length >= 2) { - for (int i = 0; i < args.length; ++i) { - if ("-p".equalsIgnoreCase(args[i]) && i < args.length - 1) { - return NumberUtils.toInt(args[i + 1], SERVER_DEFAULT_PORT); - } - } - } + 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 yamlMap = new Yaml().load(inputStream); + Map configMap = MapUtil.getFlattenedMap(yamlMap); + if (CollectionUtils.isNotEmpty(configMap)) { + Object serverPort = configMap.get("server.port"); + if (null != serverPort) { + portNum = serverPort.toString(); + } + } + } else { + Properties properties = new Properties(); + properties.load(inputStream); + portNum = properties.getProperty("server.port"); + } + if (null != portNum) { + try { + port = Integer.parseInt(portNum); + } catch (NumberFormatException exx) { + //ignore + } + } + } finally { + if (null != inputStream) { + inputStream.close(); + } + } + return port; + + } + private static File getConfigFromStartup() { + + String configLocation = System.getProperty("spring.config.location"); + if (StringUtils.isNotBlank(configLocation)) { + try { + File configFile = ResourceUtils.getFile(configLocation); + if (!configFile.isFile()) { + return null; + } + String fileName = configFile.getName(); + if (!(fileName.endsWith("yml") || fileName.endsWith("properties"))) { + return null; + } + return configFile; + } catch (FileNotFoundException e) { + return null; + } + } + return null; + + } - return SERVER_DEFAULT_PORT; - } } + diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/event/EventBusManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/event/EventBusManager.java index 3531c7b57..5500bc759 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/event/EventBusManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/event/EventBusManager.java @@ -24,15 +24,11 @@ import io.seata.core.event.GuavaEventBus; * @author zhengyangyong */ public class EventBusManager { + private static class SingletonHolder { + private static EventBus INSTANCE = new GuavaEventBus("tc",true); + } - private static class SingletonHolder { - - private static EventBus INSTANCE = new GuavaEventBus("tc"); - - } - - public static EventBus get() { - return SingletonHolder.INSTANCE; - } - + public static EventBus get() { + return SingletonHolder.INSTANCE; + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/lock/AbstractLockManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/lock/AbstractLockManager.java index 002d7e61a..65c3b63fb 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/lock/AbstractLockManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/lock/AbstractLockManager.java @@ -15,19 +15,21 @@ */ package io.seata.server.lock; -import java.util.ArrayList; -import java.util.List; - import io.seata.common.XID; import io.seata.common.util.CollectionUtils; import io.seata.common.util.StringUtils; import io.seata.core.exception.TransactionException; import io.seata.core.lock.Locker; import io.seata.core.lock.RowLock; +import io.seata.core.model.LockStatus; import io.seata.server.session.BranchSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + /** * The type Abstract lock manager. * @@ -35,153 +37,162 @@ import org.slf4j.LoggerFactory; */ public abstract class AbstractLockManager implements LockManager { - /** - * The constant LOGGER. - */ - protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractLockManager.class); + /** + * The constant LOGGER. + */ + protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractLockManager.class); - @Override - public boolean acquireLock(BranchSession branchSession) 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 locks = collectRowLocks(branchSession); - if (CollectionUtils.isEmpty(locks)) { - // no lock - return true; - } - return getLocker(branchSession).acquireLock(locks); - } + @Override + public boolean acquireLock(BranchSession branchSession) throws TransactionException { + return acquireLock(branchSession, true, false); + } - @Override - public boolean releaseLock(BranchSession branchSession) throws TransactionException { - if (branchSession == null) { - throw new IllegalArgumentException("branchSession can't be null for memory/file locker."); - } - List locks = collectRowLocks(branchSession); - try { - return getLocker(branchSession).releaseLock(locks); - } - catch (Exception t) { - LOGGER.error("unLock error, branchSession:{}", branchSession, t); - return 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 locks = collectRowLocks(branchSession); + if (CollectionUtils.isEmpty(locks)) { + // no lock + return true; + } + return getLocker(branchSession).acquireLock(locks, autoCommit, skipCheckLock); + } - @Override - public boolean isLockable(String xid, String resourceId, String lockKey) throws TransactionException { - if (StringUtils.isBlank(lockKey)) { - // no lock - return true; - } - List 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 boolean releaseLock(BranchSession branchSession) throws TransactionException { + if (branchSession == null) { + throw new IllegalArgumentException("branchSession can't be null for memory/file locker."); + } + List locks = collectRowLocks(branchSession); + try { + return getLocker(branchSession).releaseLock(locks); + } catch (Exception t) { + LOGGER.error("unLock error, branchSession:{}", branchSession, t); + return false; + } + } - @Override - public void cleanAllLocks() throws TransactionException { - getLocker().cleanAllLocks(); - } + @Override + public boolean isLockable(String xid, String resourceId, String lockKey) throws TransactionException { + if (StringUtils.isBlank(lockKey)) { + // no lock + return true; + } + List 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; + } + } - /** - * 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 void cleanAllLocks() throws TransactionException { + getLocker().cleanAllLocks(); + } - /** - * Collect row locks list.` - * @param branchSession the branch session - * @return the list - */ - protected List collectRowLocks(BranchSession branchSession) { - List locks = new ArrayList<>(); - if (branchSession == null || StringUtils.isBlank(branchSession.getLockKey())) { - return locks; - } - String xid = branchSession.getXid(); - String resourceId = branchSession.getResourceId(); - long transactionId = branchSession.getTransactionId(); + /** + * Gets locker. + * + * @return the locker + */ + protected Locker getLocker() { + return getLocker(null); + } - String lockKey = branchSession.getLockKey(); + /** + * Gets locker. + * + * @param branchSession the branch session + * @return the locker + */ + protected abstract Locker getLocker(BranchSession branchSession); - return collectRowLocks(lockKey, resourceId, xid, transactionId, branchSession.getBranchId()); - } + @Override + public List collectRowLocks(BranchSession branchSession) { + if (branchSession == null || StringUtils.isBlank(branchSession.getLockKey())) { + return Collections.emptyList(); + } - /** - * Collect row locks list. - * @param lockKey the lock key - * @param resourceId the resource id - * @param xid the xid - * @return the list - */ - protected List collectRowLocks(String lockKey, String resourceId, String xid) { - return collectRowLocks(lockKey, resourceId, xid, XID.getTransactionId(xid), null); - } + String lockKey = branchSession.getLockKey(); + String resourceId = branchSession.getResourceId(); + String xid = branchSession.getXid(); + long transactionId = branchSession.getTransactionId(); + long branchId = branchSession.getBranchId(); - /** - * 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 collectRowLocks(String lockKey, String resourceId, String xid, Long transactionId, - Long branchID) { - List locks = new ArrayList(); + return collectRowLocks(lockKey, resourceId, xid, transactionId, branchId); + } - 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; - } + /** + * Collect row locks list. + * + * @param lockKey the lock key + * @param resourceId the resource id + * @param xid the xid + * @return the list + */ + protected List 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 collectRowLocks(String lockKey, String resourceId, String xid, Long transactionId, + Long branchID) { + List 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); + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/lock/LockManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/lock/LockManager.java index 80669d829..a655d93fa 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/lock/LockManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/lock/LockManager.java @@ -16,9 +16,13 @@ package io.seata.server.lock; import io.seata.core.exception.TransactionException; +import io.seata.core.lock.RowLock; +import io.seata.core.model.LockStatus; import io.seata.server.session.BranchSession; import io.seata.server.session.GlobalSession; +import java.util.List; + /** * The interface Lock manager. * @@ -26,44 +30,77 @@ import io.seata.server.session.GlobalSession; */ 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 + * @return the boolean + * @throws TransactionException the transaction exception + */ + boolean acquireLock(BranchSession branchSession) throws TransactionException; - /** - * Un lock boolean. - * @param branchSession the branch session - * @return the boolean - * @throws TransactionException the transaction exception - */ - boolean releaseLock(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 globalSession the global session - * @return the boolean - * @throws TransactionException the transaction exception - */ - boolean releaseGlobalSessionLock(GlobalSession globalSession) throws TransactionException; + /** + * Un lock boolean. + * + * @param branchSession the branch session + * @return the boolean + * @throws TransactionException the transaction exception + */ + boolean releaseLock(BranchSession branchSession) 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; + /** + * Un lock boolean. + * + * @param globalSession the global session + * @return the boolean + * @throws TransactionException the transaction exception + */ + boolean releaseGlobalSessionLock(GlobalSession globalSession) throws TransactionException; - /** - * Clean all locks. - * @throws TransactionException the transaction exception - */ - void cleanAllLocks() 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 collectRowLocks(BranchSession branchSession); + + /** + * update lock status. + * @param xid the xid + * @param lockStatus the lock status + * @throws TransactionException the transaction exception + * + */ + void updateLockStatus(String xid, LockStatus lockStatus) throws TransactionException; } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/lock/LockerManagerFactory.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/lock/LockerManagerFactory.java index b9e0e0b24..cec4f0daf 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/lock/LockerManagerFactory.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/lock/LockerManagerFactory.java @@ -16,8 +16,10 @@ package io.seata.server.lock; import io.seata.common.loader.EnhancedServiceLoader; +import io.seata.config.Configuration; import io.seata.config.ConfigurationFactory; -import io.seata.core.constants.ConfigurationKeys; +import io.seata.server.store.StoreConfig; +import io.seata.server.store.StoreConfig.LockMode; /** * The type Lock manager factory. @@ -26,18 +28,43 @@ import io.seata.core.constants.ConfigurationKeys; */ public class LockerManagerFactory { - /** - * the lock manager - */ - private static final LockManager LOCK_MANAGER = EnhancedServiceLoader.load(LockManager.class, - ConfigurationFactory.getInstance().getConfig(ConfigurationKeys.STORE_MODE)); + private static final Configuration CONFIG = ConfigurationFactory.getInstance(); - /** - * Get lock manager. - * @return the lock manager - */ - public static LockManager getLockManager() { - return LOCK_MANAGER; - } + /** + * 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(); + } + //if not exist the lock mode, throw exception + if (null != StoreConfig.StoreMode.get(lockMode.name())) { + LOCK_MANAGER = EnhancedServiceLoader.load(LockManager.class, lockMode.getName()); + } + } + } + } + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/lock/distributed/DistributedLockerFactory.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/lock/distributed/DistributedLockerFactory.java new file mode 100644 index 000000000..362d21327 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/lock/distributed/DistributedLockerFactory.java @@ -0,0 +1,69 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.lock.distributed; + +import io.seata.common.loader.EnhancedServiceLoader; +import io.seata.common.loader.EnhancedServiceNotFoundException; +import io.seata.core.store.DefaultDistributedLocker; +import io.seata.core.store.DistributedLocker; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Distributed locker factory + * @author zhongxiang.wang + */ +public class DistributedLockerFactory { + + /** + * The constant LOGGER. + */ + private static final Logger LOGGER = LoggerFactory.getLogger(DistributedLockerFactory.class); + + private static volatile DistributedLocker DISTRIBUTED_LOCKER = null; + + /** + * Get the distributed locker by lockerType + * + * @param lockerType the locker type + * @return the distributed locker + */ + public static DistributedLocker getDistributedLocker(String lockerType) { + if (DISTRIBUTED_LOCKER == null) { + synchronized (DistributedLocker.class) { + if (DISTRIBUTED_LOCKER == null) { + DistributedLocker distributedLocker = null; + try { + if (!"file".equals(lockerType)) { + distributedLocker = EnhancedServiceLoader.load(DistributedLocker.class, lockerType); + } + } catch (EnhancedServiceNotFoundException ex) { + LOGGER.error("Get distributed locker failed: {}", ex.getMessage(), ex); + } + if (distributedLocker == null) { + distributedLocker = new DefaultDistributedLocker(); + } + DISTRIBUTED_LOCKER = distributedLocker; + } + } + } + return DISTRIBUTED_LOCKER; + } + + public static void cleanLocker() { + DISTRIBUTED_LOCKER = null; + } +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/listener/SystemPropertyLoggerContextListener.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/listener/SystemPropertyLoggerContextListener.java index 4ed809cc6..c0c965987 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/listener/SystemPropertyLoggerContextListener.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/listener/SystemPropertyLoggerContextListener.java @@ -29,48 +29,47 @@ import io.seata.core.constants.ConfigurationKeys; */ public class SystemPropertyLoggerContextListener extends ContextAwareBase implements LoggerContextListener, LifeCycle { - private boolean started = false; + private boolean started = false; - @Override - public void start() { - if (started) { - return; - } + @Override + public void start() { + if (started) { + return; + } - Context context = getContext(); - context.putProperty("PORT", System.getProperty(ConfigurationKeys.SERVER_PORT)); + Context context = getContext(); + context.putProperty("RPC_PORT", System.getProperty(ConfigurationKeys.SERVER_SERVICE_PORT_CAMEL)); - started = true; - } + started = true; + } - @Override - public void stop() { - } + @Override + public void stop() { + } - @Override - public boolean isStarted() { - return started; - } + @Override + public boolean isStarted() { + return started; + } - @Override - public boolean isResetResistant() { - return true; - } + @Override + public boolean isResetResistant() { + return true; + } - @Override - public void onStart(LoggerContext context) { - } + @Override + public void onStart(LoggerContext context) { + } - @Override - public void onReset(LoggerContext context) { - } + @Override + public void onReset(LoggerContext context) { + } - @Override - public void onStop(LoggerContext context) { - } - - @Override - public void onLevelChange(Logger logger, Level level) { - } + @Override + public void onStop(LoggerContext context) { + } + @Override + public void onLevelChange(Logger logger, Level level) { + } } \ No newline at end of file diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ColorConverter.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ColorConverter.java deleted file mode 100644 index ddaf2ff82..000000000 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ColorConverter.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 1999-2019 Seata.io Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.seata.server.logging.logback; - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.pattern.CompositeConverter; -import io.netty.util.internal.PlatformDependent; -import io.seata.server.logging.logback.ansi.AnsiColor; -import io.seata.server.logging.logback.ansi.AnsiElement; -import io.seata.server.logging.logback.ansi.AnsiOutput; -import io.seata.server.logging.logback.ansi.AnsiStyle; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -/** - * Logback {@link CompositeConverter} colors output using the {@link AnsiOutput} class. A - * single 'color' option can be provided to the converter, or if not specified color will - * be picked based on the logging level. - * - * @author Phillip Webb - * @origin Copied from spring-boot.jar by wang.liang - */ -public class ColorConverter extends CompositeConverter { - - private static final Map ELEMENTS; - - private static final String DISABLE_PROPERTY_NAME = "logback.color.disable-for-bat"; - - static { - Map ansiElements = new HashMap<>(); - ansiElements.put("faint", AnsiStyle.FAINT); - ansiElements.put("red", AnsiColor.RED); - ansiElements.put("green", AnsiColor.GREEN); - ansiElements.put("yellow", AnsiColor.YELLOW); - ansiElements.put("blue", AnsiColor.BLUE); - ansiElements.put("magenta", AnsiColor.MAGENTA); - ansiElements.put("cyan", AnsiColor.CYAN); - ELEMENTS = Collections.unmodifiableMap(ansiElements); - } - - private static final Map LEVELS; - - static { - Map ansiLevels = new HashMap<>(); - ansiLevels.put(Level.ERROR_INTEGER, AnsiColor.RED); - ansiLevels.put(Level.WARN_INTEGER, AnsiColor.YELLOW); - LEVELS = Collections.unmodifiableMap(ansiLevels); - } - - private final boolean disable; - - public ColorConverter() { - // If is windows and run by seata-server.bat, then disable the color log. - disable = PlatformDependent.isWindows() && Boolean.parseBoolean(System.getProperty(DISABLE_PROPERTY_NAME)); - } - - @Override - protected String transform(ILoggingEvent event, String in) { - if (disable) { - // return the original log - return in; - } - - AnsiElement element = ELEMENTS.get(getFirstOption()); - if (element == null) { - // Assume highlighting - element = LEVELS.get(event.getLevel().toInteger()); - element = (element != null) ? element : AnsiColor.GREEN; - } - return toAnsiString(in, element); - } - - protected String toAnsiString(String in, AnsiElement element) { - return AnsiOutput.toString(element, in); - } - -} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ExtendedWhitespaceThrowableProxyConverter.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ExtendedWhitespaceThrowableProxyConverter.java index a2a0ca489..c6bb4a106 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ExtendedWhitespaceThrowableProxyConverter.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ExtendedWhitespaceThrowableProxyConverter.java @@ -22,16 +22,14 @@ 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 - * @origin Copied from spring-boot.jar by wang.liang */ public class ExtendedWhitespaceThrowableProxyConverter extends ExtendedThrowableProxyConverter { - @Override - protected String throwableProxyToString(IThrowableProxy tp) { - return "==>" + CoreConstants.LINE_SEPARATOR + super.throwableProxyToString(tp) + "<==" - + CoreConstants.LINE_SEPARATOR + CoreConstants.LINE_SEPARATOR; - } - + @Override + protected String throwableProxyToString(IThrowableProxy tp) { + return "==>" + CoreConstants.LINE_SEPARATOR + super.throwableProxyToString(tp) + + "<==" + CoreConstants.LINE_SEPARATOR + CoreConstants.LINE_SEPARATOR; + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/WhitespaceThrowableProxyConverter.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/WhitespaceThrowableProxyConverter.java deleted file mode 100644 index 97aa6eeb4..000000000 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/WhitespaceThrowableProxyConverter.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 1999-2019 Seata.io Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.seata.server.logging.logback; - -import ch.qos.logback.classic.pattern.ThrowableProxyConverter; -import ch.qos.logback.classic.spi.IThrowableProxy; -import ch.qos.logback.core.CoreConstants; - -/** - * {@link ThrowableProxyConverter} that adds some additional whitespace around the - * stacktrace. - * - * @author Phillip Webb - * @origin Copied from spring-boot.jar by wang.liang - */ -public class WhitespaceThrowableProxyConverter extends ThrowableProxyConverter { - - @Override - protected String throwableProxyToString(IThrowableProxy tp) { - return CoreConstants.LINE_SEPARATOR + super.throwableProxyToString(tp) + CoreConstants.LINE_SEPARATOR - + CoreConstants.LINE_SEPARATOR; - } - -} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ansi/AnsiColor.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ansi/AnsiColor.java deleted file mode 100644 index 381c6111f..000000000 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ansi/AnsiColor.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 1999-2019 Seata.io Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.seata.server.logging.logback.ansi; - -/** - * {@link AnsiElement Ansi} colors. - * - * @author Phillip Webb - * @author Geoffrey Chandler - * @origin Copied from spring-boot.jar by wang.liang - */ -public enum AnsiColor implements AnsiElement { - - /** - * default is light-grey - */ - DEFAULT("39"), - - /** - * black - */ - BLACK("30"), - - /** - * red - */ - RED("31"), - - /** - * green - */ - GREEN("32"), - - /** - * yellow - */ - YELLOW("33"), - - /** - * blue - */ - BLUE("34"), - - /** - * magenta - */ - MAGENTA("35"), - - /** - * cyan - */ - CYAN("36"), - - /** - * white - */ - WHITE("37"), - - /** - * bright black - */ - BRIGHT_BLACK("90"), - - /** - * bright red - */ - BRIGHT_RED("91"), - - /** - * bright green - */ - BRIGHT_GREEN("92"), - - /** - * bright yellow - */ - BRIGHT_YELLOW("93"), - - /** - * bright blue - */ - BRIGHT_BLUE("94"), - - /** - * bright magenta - */ - BRIGHT_MAGENTA("95"), - - /** - * bright cyan - */ - BRIGHT_CYAN("96"), - - /** - * bright white - */ - BRIGHT_WHITE("97"); - - /** - * code of color - */ - private final String code; - - AnsiColor(String code) { - this.code = code; - } - - @Override - public String toString() { - return this.code; - } - -} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ansi/AnsiOutput.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ansi/AnsiOutput.java deleted file mode 100644 index c4b9303ba..000000000 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/ansi/AnsiOutput.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright 1999-2019 Seata.io Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.seata.server.logging.logback.ansi; - -import com.alipay.lookout.common.Assert; - -import java.util.Locale; - -/** - * Generates ANSI encoded output, automatically attempting to detect if the terminal - * supports ANSI. - * - * @author Phillip Webb - * @origin Copied from spring-boot.jar by wang.liang - */ -public class AnsiOutput { - - private static final String ENCODE_JOIN = ";"; - - private static Enabled enabled = Enabled.ALWAYS; - - private static Boolean consoleAvailable; - - private static Boolean ansiCapable; - - private static final String OPERATING_SYSTEM_NAME = System.getProperty("os.name").toLowerCase(Locale.ENGLISH); - - private static final String ENCODE_START = "\033["; - - private static final String ENCODE_END = "m"; - - private static final String RESET = "0;" + AnsiColor.DEFAULT; - - /** - * Sets if ANSI output is enabled. - * @param enabled if ANSI is enabled, disabled or detected - */ - public static void setEnabled(Enabled enabled) { - Assert.notNull(enabled, "Enabled must not be null"); - AnsiOutput.enabled = enabled; - } - - /** - * Sets if the System.console() is known to be available. - * @param consoleAvailable if the console is known to be available or {@code null} to - * use standard detection logic. - */ - public static void setConsoleAvailable(Boolean consoleAvailable) { - AnsiOutput.consoleAvailable = consoleAvailable; - } - - static Enabled getEnabled() { - return AnsiOutput.enabled; - } - - /** - * Encode a single {@link AnsiElement} if output is enabled. - * @param element the element to encode - * @return the encoded element or an empty string - */ - public static String encode(AnsiElement element) { - if (isEnabled()) { - return ENCODE_START + element + ENCODE_END; - } - return ""; - } - - /** - * Create a new ANSI string from the specified elements. Any {@link AnsiElement}s will - * be encoded as required. - * @param elements the elements to encode - * @return a string of the encoded elements - */ - public static String toString(Object... elements) { - StringBuilder sb = new StringBuilder(); - if (isEnabled()) { - buildEnabled(sb, elements); - } - else { - buildDisabled(sb, elements); - } - return sb.toString(); - } - - private static void buildEnabled(StringBuilder sb, Object[] elements) { - boolean writingAnsi = false; - boolean containsEncoding = false; - for (Object element : elements) { - if (element instanceof AnsiElement) { - containsEncoding = true; - if (!writingAnsi) { - sb.append(ENCODE_START); - writingAnsi = true; - } - else { - sb.append(ENCODE_JOIN); - } - } - else { - if (writingAnsi) { - sb.append(ENCODE_END); - writingAnsi = false; - } - } - sb.append(element); - } - if (containsEncoding) { - sb.append(writingAnsi ? ENCODE_JOIN : ENCODE_START); - sb.append(RESET); - sb.append(ENCODE_END); - } - } - - private static void buildDisabled(StringBuilder sb, Object[] elements) { - for (Object element : elements) { - if (!(element instanceof AnsiElement) && element != null) { - sb.append(element); - } - } - } - - private static boolean isEnabled() { - if (enabled == Enabled.DETECT) { - if (ansiCapable == null) { - ansiCapable = detectIfAnsiCapable(); - } - return ansiCapable; - } - return enabled == Enabled.ALWAYS; - } - - private static boolean detectIfAnsiCapable() { - try { - if (Boolean.FALSE.equals(consoleAvailable)) { - return false; - } - if ((consoleAvailable == null) && (System.console() == null)) { - return false; - } - return !(OPERATING_SYSTEM_NAME.indexOf("win") >= 0); - } - catch (Throwable ex) { - return false; - } - } - - /** - * Possible values to pass to {@link AnsiOutput#setEnabled}. Determines when to output - * ANSI escape sequences for coloring application output. - */ - public enum Enabled { - - /** - * Try to detect whether ANSI coloring capabilities are available. The default - * value for {@link AnsiOutput}. - */ - DETECT, - - /** - * Enable ANSI-colored output. - */ - ALWAYS, - - /** - * Disable ANSI-colored output. - */ - NEVER - - } - -} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/appender/EnhancedLogstashEncoder.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/appender/EnhancedLogstashEncoder.java index 74ccff27e..0b7689c1a 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/appender/EnhancedLogstashEncoder.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/logging/logback/appender/EnhancedLogstashEncoder.java @@ -15,12 +15,12 @@ */ package io.seata.server.logging.logback.appender; -import java.util.ArrayList; - import net.logstash.logback.composite.JsonProvider; import net.logstash.logback.composite.JsonProviders; import net.logstash.logback.encoder.LogstashEncoder; +import java.util.ArrayList; + /** * The type Enhanced logstash encoder * @@ -29,17 +29,17 @@ import net.logstash.logback.encoder.LogstashEncoder; */ 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); - } - } - } - + /** + * 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); + } + } + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/metrics/MeterIdConstants.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/metrics/MeterIdConstants.java index 8bdb5d0dd..96f6ac5fe 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/metrics/MeterIdConstants.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/metrics/MeterIdConstants.java @@ -15,8 +15,8 @@ */ package io.seata.server.metrics; -import io.seata.metrics.IdConstants; import io.seata.metrics.Id; +import io.seata.metrics.IdConstants; /** * Constants for meter id in tc @@ -24,37 +24,85 @@ import io.seata.metrics.Id; * @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_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_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_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 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 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_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 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 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 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 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); } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/metrics/MetricsManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/metrics/MetricsManager.java index 4b30f2b37..bad1c27c0 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/metrics/MetricsManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/metrics/MetricsManager.java @@ -15,8 +15,6 @@ */ package io.seata.server.metrics; -import java.util.List; - import io.seata.config.ConfigurationFactory; import io.seata.core.constants.ConfigurationKeys; import io.seata.metrics.exporter.Exporter; @@ -25,44 +23,43 @@ 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(); + } - private static class SingletonHolder { + public static final MetricsManager get() { + return SingletonHolder.INSTANCE; + } - private static MetricsManager INSTANCE = new MetricsManager(); + private Registry registry; - } - - public static final MetricsManager get() { - return MetricsManager.SingletonHolder.INSTANCE; - } - - private Registry registry; - - public Registry getRegistry() { - return registry; - } - - public void init() { - boolean enabled = ConfigurationFactory.getInstance() - .getBoolean(ConfigurationKeys.METRICS_PREFIX + ConfigurationKeys.METRICS_ENABLED, false); - if (enabled) { - registry = RegistryFactory.getInstance(); - if (registry != null) { - List 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)); - } - } - } - } + 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 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)); + } + } + } + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/metrics/MetricsPublisher.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/metrics/MetricsPublisher.java new file mode 100644 index 000000000..f0db1e523 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/metrics/MetricsPublisher.java @@ -0,0 +1,97 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.metrics; + +import io.seata.core.event.EventBus; +import io.seata.core.event.GlobalTransactionEvent; +import io.seata.core.model.GlobalStatus; +import io.seata.server.event.EventBusManager; +import io.seata.server.session.GlobalSession; + +/** + * The type Metrics publisher. + * + * @author slievrly + */ +public class MetricsPublisher { + + private static final EventBus EVENT_BUS = EventBusManager.get(); + + /** + * post end event + * + * @param globalSession the global session + * @param retryGlobal the retry global + * @param retryBranch the retry branch + */ + public static void postSessionDoneEvent(final GlobalSession globalSession, boolean retryGlobal, + boolean retryBranch) { + postSessionDoneEvent(globalSession, globalSession.getStatus(), retryGlobal, retryBranch); + } + + /** + * post end event (force specified state) + * + * @param globalSession the global session + * @param status the global status + * @param retryGlobal the retry global + * @param retryBranch the retry branch + */ + public static void postSessionDoneEvent(final GlobalSession globalSession, GlobalStatus status, boolean retryGlobal, + boolean retryBranch) { + postSessionDoneEvent(globalSession, status.name(), retryGlobal, globalSession.getBeginTime(), retryBranch); + } + + /** + * Post session done event. + * + * @param globalSession the global session + * @param status the status + * @param retryGlobal the retry global + * @param beginTime the begin time + * @param retryBranch the retry branch + */ + public static void postSessionDoneEvent(final GlobalSession globalSession, String status, boolean retryGlobal, long beginTime, boolean retryBranch) { + EVENT_BUS.post(new GlobalTransactionEvent(globalSession.getTransactionId(), GlobalTransactionEvent.ROLE_TC, + globalSession.getTransactionName(), globalSession.getApplicationId(), + globalSession.getTransactionServiceGroup(), beginTime, System.currentTimeMillis(), status, retryGlobal, retryBranch)); + } + + /** + * Post session doing event. + * + * @param globalSession the global session + * @param retryGlobal the retry global + */ + public static void postSessionDoingEvent(final GlobalSession globalSession, boolean retryGlobal) { + postSessionDoingEvent(globalSession, globalSession.getStatus().name(), retryGlobal, false); + } + + /** + * Post session doing event. + * + * @param globalSession the global session + * @param status the status + * @param retryGlobal the retry global + * @param retryBranch the retry branch + */ + public static void postSessionDoingEvent(final GlobalSession globalSession, String status, boolean retryGlobal, + boolean retryBranch) { + EVENT_BUS.post(new GlobalTransactionEvent(globalSession.getTransactionId(), GlobalTransactionEvent.ROLE_TC, + globalSession.getTransactionName(), globalSession.getApplicationId(), + globalSession.getTransactionServiceGroup(), globalSession.getBeginTime(), null, status, retryGlobal, retryBranch)); + } +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/metrics/MetricsSubscriber.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/metrics/MetricsSubscriber.java index 9776edb5e..277cee083 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/metrics/MetricsSubscriber.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/metrics/MetricsSubscriber.java @@ -19,14 +19,16 @@ import com.google.common.eventbus.Subscribe; import io.seata.core.event.GlobalTransactionEvent; import io.seata.core.model.GlobalStatus; import io.seata.metrics.registry.Registry; +import io.seata.server.event.EventBusManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; -import static io.seata.metrics.IdConstants.APP_ID_KEY; -import static io.seata.metrics.IdConstants.GROUP_KEY; +import static io.seata.metrics.IdConstants.*; /** * Event subscriber for metrics @@ -35,77 +37,178 @@ import static io.seata.metrics.IdConstants.GROUP_KEY; */ public class MetricsSubscriber { - private final Registry registry; + private static final Logger LOGGER = LoggerFactory.getLogger(MetricsSubscriber.class); + private final Registry registry; - private final Map> consumers; + private final Map> consumers; - public MetricsSubscriber(Registry registry) { - this.registry = registry; - consumers = new HashMap<>(); - consumers.put(GlobalStatus.Begin, this::processGlobalStatusBegin); - consumers.put(GlobalStatus.Committed, this::processGlobalStatusCommitted); - consumers.put(GlobalStatus.Rollbacked, this::processGlobalStatusRollbacked); + 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, this::processGlobalStatusCommitFailed); - consumers.put(GlobalStatus.RollbackFailed, this::processGlobalStatusRollbackFailed); - consumers.put(GlobalStatus.TimeoutRollbacked, this::processGlobalStatusTimeoutRollbacked); - consumers.put(GlobalStatus.TimeoutRollbackFailed, this::processGlobalStatusTimeoutRollbackFailed); - } + consumers.put(GlobalStatus.CommitFailed.name(), this::processGlobalStatusCommitFailed); + consumers.put(GlobalStatus.RollbackFailed.name(), this::processGlobalStatusRollbackFailed); + consumers.put(GlobalStatus.TimeoutRollbacked.name(), this::processGlobalStatusTimeoutRollbacked); + consumers.put(GlobalStatus.TimeoutRollbackFailed.name(), this::processGlobalStatusTimeoutRollbackFailed); - private void processGlobalStatusBegin(GlobalTransactionEvent event) { - registry.getCounter(MeterIdConstants.COUNTER_ACTIVE.withTag(APP_ID_KEY, event.getApplicationId()) - .withTag(GROUP_KEY, event.getGroup())).increase(1); - } + consumers.put(GlobalStatus.CommitRetryTimeout.name(), this::processGlobalStatusCommitRetryTimeout); + consumers.put(GlobalStatus.RollbackRetryTimeout.name(), this::processGlobalStatusTimeoutRollbackRetryTimeout); - private void processGlobalStatusCommitted(GlobalTransactionEvent event) { - registry.getCounter(MeterIdConstants.COUNTER_ACTIVE.withTag(APP_ID_KEY, event.getApplicationId()) - .withTag(GROUP_KEY, event.getGroup())).decrease(1); - 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); - } + consumers.put(STATUS_VALUE_AFTER_COMMITTED_KEY, this::processAfterGlobalCommitted); + consumers.put(STATUS_VALUE_AFTER_ROLLBACKED_KEY, this::processAfterGlobalRollbacked); + } - private void processGlobalStatusRollbacked(GlobalTransactionEvent event) { - registry.getCounter(MeterIdConstants.COUNTER_ACTIVE.withTag(APP_ID_KEY, event.getApplicationId()) - .withTag(GROUP_KEY, event.getGroup())).decrease(1); - 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 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 processGlobalStatusCommitFailed(GlobalTransactionEvent event) { - registry.getCounter(MeterIdConstants.COUNTER_ACTIVE.withTag(APP_ID_KEY, event.getApplicationId()) - .withTag(GROUP_KEY, event.getGroup())).decrease(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 processGlobalStatusRollbackFailed(GlobalTransactionEvent event) { - registry.getCounter(MeterIdConstants.COUNTER_ACTIVE.withTag(APP_ID_KEY, event.getApplicationId()) - .withTag(GROUP_KEY, event.getGroup())).decrease(1); - } + 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 processGlobalStatusTimeoutRollbacked(GlobalTransactionEvent event) { - registry.getCounter(MeterIdConstants.COUNTER_ACTIVE.withTag(APP_ID_KEY, event.getApplicationId()) - .withTag(GROUP_KEY, event.getGroup())).decrease(1); - } + 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 processGlobalStatusTimeoutRollbackFailed(GlobalTransactionEvent event) { - registry.getCounter(MeterIdConstants.COUNTER_ACTIVE.withTag(APP_ID_KEY, event.getApplicationId()) - .withTag(GROUP_KEY, event.getGroup())).decrease(1); - } + 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); + } - @Subscribe - public void recordGlobalTransactionEventForMetrics(GlobalTransactionEvent event) { - if (registry != null && consumers.containsKey(event.getStatus())) { - consumers.get(event.getStatus()).accept(event); - } - } + 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(); + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/AbstractSessionManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/AbstractSessionManager.java index a719da4db..2668c2c5c 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/AbstractSessionManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/AbstractSessionManager.java @@ -21,6 +21,7 @@ import io.seata.core.exception.TransactionException; import io.seata.core.exception.TransactionExceptionCode; import io.seata.core.model.BranchStatus; import io.seata.core.model.GlobalStatus; +import io.seata.core.model.LockStatus; import io.seata.server.store.SessionStorable; import io.seata.server.store.TransactionStoreManager; import io.seata.server.store.TransactionStoreManager.LogOperation; @@ -32,164 +33,167 @@ import org.slf4j.LoggerFactory; */ public abstract class AbstractSessionManager implements SessionManager, SessionLifecycleListener { - /** - * The constant LOGGER. - */ - protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractSessionManager.class); + /** + * The constant LOGGER. + */ + protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractSessionManager.class); - /** - * The Transaction store manager. - */ - protected TransactionStoreManager transactionStoreManager; + /** + * The Transaction store manager. + */ + protected TransactionStoreManager transactionStoreManager; - /** - * The Name. - */ - protected String name; + /** + * The Name. + */ + protected String name; - /** - * Instantiates a new Abstract session manager. - */ - public AbstractSessionManager() { - } + /** + * 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; - } + /** + * 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[" + name + "] SESSION[" + session + "] " + LogOperation.GLOBAL_ADD); - } - writeSession(LogOperation.GLOBAL_ADD, session); - } + @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[" + name + "] SESSION[" + session + "] " + LogOperation.GLOBAL_UPDATE); - } - writeSession(LogOperation.GLOBAL_UPDATE, 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[" + name + "] SESSION[" + session + "] " + LogOperation.GLOBAL_REMOVE); - } - writeSession(LogOperation.GLOBAL_REMOVE, 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[" + name + "] SESSION[" + branchSession + "] " + LogOperation.BRANCH_ADD); - } - writeSession(LogOperation.BRANCH_ADD, branchSession); - } + @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[" + name + "] SESSION[" + branchSession + "] " + LogOperation.BRANCH_UPDATE); - } - writeSession(LogOperation.BRANCH_UPDATE, 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[" + name + "] SESSION[" + branchSession + "] " + LogOperation.BRANCH_REMOVE); - } - writeSession(LogOperation.BRANCH_REMOVE, 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 onBegin(GlobalSession globalSession) throws TransactionException { + addGlobalSession(globalSession); + } - @Override - public void onStatusChange(GlobalSession globalSession, GlobalStatus status) throws TransactionException { - updateGlobalSessionStatus(globalSession, status); - } + @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 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 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 onRemoveBranch(GlobalSession globalSession, BranchSession branchSession) throws TransactionException { + removeBranchSession(globalSession, branchSession); + } - @Override - public void onClose(GlobalSession globalSession) throws TransactionException { - globalSession.setActive(false); - } + @Override + public void onClose(GlobalSession globalSession) throws TransactionException { + globalSession.setActive(false); + } - @Override - public void onEnd(GlobalSession globalSession) throws TransactionException { - removeGlobalSession(globalSession); - } + @Override + public void onSuccessEnd(GlobalSession globalSession) throws TransactionException { + removeGlobalSession(globalSession); + } - 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 onFailEnd(GlobalSession globalSession) throws TransactionException { + LOGGER.info("xid:{} fail end, transaction:{}",globalSession.getXid(),globalSession.toString()); + } - @Override - public void destroy() { - } + 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()); + } + } + } - /** - * Sets transaction store manager. - * @param transactionStoreManager the transaction store manager - */ - public void setTransactionStoreManager(TransactionStoreManager transactionStoreManager) { - this.transactionStoreManager = transactionStoreManager; - } + @Override + public void destroy() { + } + /** + * Sets transaction store manager. + * + * @param transactionStoreManager the transaction store manager + */ + public void setTransactionStoreManager(TransactionStoreManager transactionStoreManager) { + this.transactionStoreManager = transactionStoreManager; + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/BranchSession.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/BranchSession.java index 0b0af3228..4df48e0fc 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/BranchSession.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/BranchSession.java @@ -15,22 +15,25 @@ */ package io.seata.server.session; +import io.seata.common.util.CompressUtil; +import io.seata.core.exception.TransactionException; +import io.seata.core.model.BranchStatus; +import io.seata.core.model.BranchType; +import io.seata.core.model.LockStatus; +import io.seata.server.lock.LockerManagerFactory; +import io.seata.server.storage.file.lock.FileLocker; +import io.seata.server.store.SessionStorable; +import io.seata.server.store.StoreConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.IOException; import java.nio.ByteBuffer; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import io.seata.server.storage.file.lock.FileLocker; -import io.seata.common.util.CompressUtil; -import io.seata.core.exception.TransactionException; -import io.seata.core.model.BranchStatus; -import io.seata.core.model.BranchType; -import io.seata.server.lock.LockerManagerFactory; -import io.seata.server.store.SessionStorable; -import io.seata.server.store.StoreConfig; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import static io.seata.core.model.LockStatus.Locked; /** * The type Branch session. @@ -39,398 +42,428 @@ import org.slf4j.LoggerFactory; */ public class BranchSession implements Lockable, Comparable, SessionStorable { - private static final Logger LOGGER = LoggerFactory.getLogger(BranchSession.class); + private static final Logger LOGGER = LoggerFactory.getLogger(BranchSession.class); - private static final int MAX_BRANCH_SESSION_SIZE = StoreConfig.getMaxBranchSessionSize(); + private static final int MAX_BRANCH_SESSION_SIZE = StoreConfig.getMaxBranchSessionSize(); - private static ThreadLocal byteBufferThreadLocal = ThreadLocal - .withInitial(() -> ByteBuffer.allocate(MAX_BRANCH_SESSION_SIZE)); + private static ThreadLocal byteBufferThreadLocal = ThreadLocal.withInitial(() -> ByteBuffer.allocate( + MAX_BRANCH_SESSION_SIZE)); - private String xid; + private String xid; - private long transactionId; + private long transactionId; - private long branchId; + private long branchId; - private String resourceGroupId; + private String resourceGroupId; - private String resourceId; + private String resourceId; - private String lockKey; + private String lockKey; - private BranchType branchType; + private BranchType branchType; - private BranchStatus status = BranchStatus.Unknown; + private BranchStatus status = BranchStatus.Unknown; - private String clientId; + private String clientId; - private String applicationData; + private String applicationData; - private ConcurrentMap> lockHolder = new ConcurrentHashMap<>(); + private LockStatus lockStatus = Locked; - /** - * Gets application data. - * @return the application data - */ - public String getApplicationData() { - return applicationData; - } + private ConcurrentMap> lockHolder + = new ConcurrentHashMap<>(); - /** - * Sets application data. - * @param applicationData the application data - */ - public void setApplicationData(String applicationData) { - this.applicationData = applicationData; - } + /** + * Gets application data. + * + * @return the application data + */ + public String getApplicationData() { + return applicationData; + } - /** - * Gets resource group id. - * @return the resource group id - */ - public String getResourceGroupId() { - return resourceGroupId; - } + /** + * Sets application data. + * + * @param applicationData the application data + */ + public void setApplicationData(String applicationData) { + this.applicationData = applicationData; + } - /** - * Sets resource group id. - * @param resourceGroupId the resource group id - */ - public void setResourceGroupId(String resourceGroupId) { - this.resourceGroupId = resourceGroupId; - } + /** + * Gets resource group id. + * + * @return the resource group id + */ + public String getResourceGroupId() { + return resourceGroupId; + } - /** - * Gets client id. - * @return the client id - */ - public String getClientId() { - return clientId; - } + /** + * Sets resource group id. + * + * @param resourceGroupId the resource group id + */ + public void setResourceGroupId(String resourceGroupId) { + this.resourceGroupId = resourceGroupId; + } - /** - * Sets client id. - * @param clientId the client id - */ - public void setClientId(String clientId) { - this.clientId = clientId; - } + /** + * Gets client id. + * + * @return the client id + */ + public String getClientId() { + return clientId; + } - /** - * Gets resource id. - * @return the resource id - */ - public String getResourceId() { - return resourceId; - } + /** + * Sets client id. + * + * @param clientId the client id + */ + public void setClientId(String clientId) { + this.clientId = clientId; + } - /** - * Sets resource id. - * @param resourceId the resource id - */ - public void setResourceId(String resourceId) { - this.resourceId = resourceId; - } + /** + * Gets resource id. + * + * @return the resource id + */ + public String getResourceId() { + return resourceId; + } - /** - * Gets lock key. - * @return the lock key - */ - public String getLockKey() { - return lockKey; - } + /** + * Sets resource id. + * + * @param resourceId the resource id + */ + public void setResourceId(String resourceId) { + this.resourceId = resourceId; + } - /** - * Sets lock key. - * @param lockKey the lock key - */ - public void setLockKey(String lockKey) { - this.lockKey = lockKey; - } + /** + * Gets lock key. + * + * @return the lock key + */ + public String getLockKey() { + return lockKey; + } - /** - * Gets branch type. - * @return the branch type - */ - public BranchType getBranchType() { - return branchType; - } + /** + * Sets lock key. + * + * @param lockKey the lock key + */ + public void setLockKey(String lockKey) { + this.lockKey = lockKey; + } - /** - * Sets branch type. - * @param branchType the branch type - */ - public void setBranchType(BranchType branchType) { - this.branchType = branchType; - } + /** + * Gets branch type. + * + * @return the branch type + */ + public BranchType getBranchType() { + return branchType; + } - /** - * Gets status. - * @return the status - */ - public BranchStatus getStatus() { - return status; - } + /** + * Sets branch type. + * + * @param branchType the branch type + */ + public void setBranchType(BranchType branchType) { + this.branchType = branchType; + } - /** - * Sets status. - * @param status the status - */ - public void setStatus(BranchStatus status) { - this.status = status; - } + /** + * Gets status. + * + * @return the status + */ + public BranchStatus getStatus() { + return status; + } - /** - * Gets transaction id. - * @return the transaction id - */ - public long getTransactionId() { - return transactionId; - } + /** + * Sets status. + * + * @param status the status + */ + public void setStatus(BranchStatus status) { + this.status = status; + } - /** - * Sets transaction id. - * @param transactionId the transaction id - */ - public void setTransactionId(long transactionId) { - this.transactionId = transactionId; - } + /** + * Gets transaction id. + * + * @return the transaction id + */ + public long getTransactionId() { + return transactionId; + } - /** - * Gets branch id. - * @return the branch id - */ - public long getBranchId() { - return branchId; - } + /** + * Sets transaction id. + * + * @param transactionId the transaction id + */ + public void setTransactionId(long transactionId) { + this.transactionId = transactionId; + } - /** - * Sets branch id. - * @param branchId the branch id - */ - public void setBranchId(long branchId) { - this.branchId = branchId; - } + /** + * Gets branch id. + * + * @return the branch id + */ + public long getBranchId() { + return branchId; + } - /** - * Gets xid. - * @return the xid - */ - public String getXid() { - return xid; - } + /** + * Sets branch id. + * + * @param branchId the branch id + */ + public void setBranchId(long branchId) { + this.branchId = branchId; + } - /** - * Sets xid. - * @param xid the xid - */ - public void setXid(String xid) { - this.xid = xid; - } + /** + * Gets xid. + * + * @return the xid + */ + public String getXid() { + return xid; + } - @Override - public String toString() { - return "BR:" + branchId + "/" + transactionId; - } + /** + * Sets xid. + * + * @param xid the xid + */ + public void setXid(String xid) { + this.xid = xid; + } - @Override - public int compareTo(BranchSession o) { - return Long.compare(this.branchId, o.branchId); - } + @Override + public String toString() { + return "BR:" + branchId + "/" + transactionId; + } - public boolean canBeCommittedAsync() { - return branchType == BranchType.AT || status == BranchStatus.PhaseOne_Failed; - } + @Override + public int compareTo(BranchSession o) { + return Long.compare(this.branchId, o.branchId); + } - /** - * Gets lock holder. - * @return the lock holder - */ - public ConcurrentMap> getLockHolder() { - return lockHolder; - } + public boolean canBeCommittedAsync() { + return branchType == BranchType.AT || status == BranchStatus.PhaseOne_Failed; + } - @Override - public boolean lock() throws TransactionException { - if (this.getBranchType().equals(BranchType.AT)) { - return LockerManagerFactory.getLockManager().acquireLock(this); - } - return true; - } + /** + * Gets lock holder. + * + * @return the lock holder + */ + public ConcurrentMap> getLockHolder() { + return lockHolder; + } - @Override - public boolean unlock() throws TransactionException { - if (this.getBranchType() == BranchType.AT) { - return LockerManagerFactory.getLockManager().releaseLock(this); - } - return true; - } + @Override + public boolean lock() throws TransactionException { + return this.lock(true, false); + } - @Override - public byte[] encode() { + public boolean lock(boolean autoCommit, boolean skipCheckLock) throws TransactionException { + if (this.getBranchType().equals(BranchType.AT)) { + return LockerManagerFactory.getLockManager().acquireLock(this, autoCommit, skipCheckLock); + } + return true; + } - byte[] resourceIdBytes = resourceId != null ? resourceId.getBytes() : null; + @Override + public boolean unlock() throws TransactionException { + if (this.getBranchType() == BranchType.AT) { + return LockerManagerFactory.getLockManager().releaseLock(this); + } + return true; + } - byte[] lockKeyBytes = lockKey != null ? lockKey.getBytes() : null; + public LockStatus getLockStatus() { + return lockStatus; + } - byte[] clientIdBytes = clientId != null ? clientId.getBytes() : null; + public void setLockStatus(LockStatus lockStatus) { + this.lockStatus = lockStatus; + } - byte[] applicationDataBytes = applicationData != null ? applicationData.getBytes() : null; + @Override + public byte[] encode() { - byte[] xidBytes = xid != null ? xid.getBytes() : null; + byte[] resourceIdBytes = resourceId != null ? resourceId.getBytes() : null; - byte branchTypeByte = branchType != null ? (byte) branchType.ordinal() : -1; + byte[] lockKeyBytes = lockKey != null ? lockKey.getBytes() : null; - int size = calBranchSessionSize(resourceIdBytes, lockKeyBytes, clientIdBytes, applicationDataBytes, xidBytes); + byte[] clientIdBytes = clientId != null ? clientId.getBytes() : null; - 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; - } + byte[] applicationDataBytes = applicationData != null ? applicationData.getBytes() : null; - if (size > MAX_BRANCH_SESSION_SIZE) { - throw new RuntimeException("compress branch session size exceeded, compressSize : " + size - + " maxBranchSessionSize : " + MAX_BRANCH_SESSION_SIZE); - } - } + byte[] xidBytes = xid != null ? xid.getBytes() : null; - ByteBuffer byteBuffer = byteBufferThreadLocal.get(); - // recycle - byteBuffer.clear(); + byte branchTypeByte = branchType != null ? (byte) branchType.ordinal() : -1; - byteBuffer.putLong(transactionId); - byteBuffer.putLong(branchId); + int size = calBranchSessionSize(resourceIdBytes, lockKeyBytes, clientIdBytes, applicationDataBytes, xidBytes); - if (resourceIdBytes != null) { - byteBuffer.putInt(resourceIdBytes.length); - byteBuffer.put(resourceIdBytes); - } - else { - byteBuffer.putInt(0); - } + 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 (lockKeyBytes != null) { - byteBuffer.putInt(lockKeyBytes.length); - byteBuffer.put(lockKeyBytes); - } - else { - byteBuffer.putInt(0); - } + if (size > MAX_BRANCH_SESSION_SIZE) { + throw new RuntimeException( + "compress branch session size exceeded, compressSize : " + size + " maxBranchSessionSize : " + + MAX_BRANCH_SESSION_SIZE); + } + } - if (clientIdBytes != null) { - byteBuffer.putShort((short) clientIdBytes.length); - byteBuffer.put(clientIdBytes); - } - else { - byteBuffer.putShort((short) 0); - } + ByteBuffer byteBuffer = byteBufferThreadLocal.get(); + //recycle + byteBuffer.clear(); - if (applicationDataBytes != null) { - byteBuffer.putInt(applicationDataBytes.length); - byteBuffer.put(applicationDataBytes); - } - else { - byteBuffer.putInt(0); - } + byteBuffer.putLong(transactionId); + byteBuffer.putLong(branchId); - if (xidBytes != null) { - byteBuffer.putInt(xidBytes.length); - byteBuffer.put(xidBytes); - } - else { - byteBuffer.putInt(0); - } + if (resourceIdBytes != null) { + byteBuffer.putInt(resourceIdBytes.length); + byteBuffer.put(resourceIdBytes); + } else { + byteBuffer.putInt(0); + } - byteBuffer.put(branchTypeByte); + if (lockKeyBytes != null) { + byteBuffer.putInt(lockKeyBytes.length); + byteBuffer.put(lockKeyBytes); + } else { + byteBuffer.putInt(0); + } - byteBuffer.put((byte) status.getCode()); - byteBuffer.flip(); - byte[] result = new byte[byteBuffer.limit()]; - byteBuffer.get(result); - return result; - } + if (clientIdBytes != null) { + byteBuffer.putShort((short)clientIdBytes.length); + byteBuffer.put(clientIdBytes); + } else { + byteBuffer.putShort((short)0); + } - 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; - } + if (applicationDataBytes != null) { + byteBuffer.putInt(applicationDataBytes.length); + byteBuffer.put(applicationDataBytes); + } else { + byteBuffer.putInt(0); + } - @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); - } + if (xidBytes != null) { + byteBuffer.putInt(xidBytes.length); + byteBuffer.put(xidBytes); + } else { + byteBuffer.putInt(0); + } - } - 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()); + 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()); + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/BranchSessionHandler.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/BranchSessionHandler.java index ccb53bd83..3523018b2 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/BranchSessionHandler.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/BranchSessionHandler.java @@ -26,14 +26,14 @@ import io.seata.core.exception.TransactionException; @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; + 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; } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/GlobalSession.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/GlobalSession.java index 240657045..6019477ba 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/GlobalSession.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/GlobalSession.java @@ -15,15 +15,6 @@ */ package io.seata.server.session; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - import io.seata.common.Constants; import io.seata.common.DefaultValues; import io.seata.common.XID; @@ -36,6 +27,7 @@ import io.seata.core.exception.TransactionExceptionCode; import io.seata.core.model.BranchStatus; import io.seata.core.model.BranchType; import io.seata.core.model.GlobalStatus; +import io.seata.core.model.LockStatus; import io.seata.server.UUIDGenerator; import io.seata.server.lock.LockerManagerFactory; import io.seata.server.store.SessionStorable; @@ -43,9 +35,13 @@ import io.seata.server.store.StoreConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static io.seata.core.model.GlobalStatus.AsyncCommitting; -import static io.seata.core.model.GlobalStatus.CommitRetrying; -import static io.seata.core.model.GlobalStatus.Committing; +import java.nio.ByteBuffer; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import static io.seata.core.model.GlobalStatus.*; /** * The type Global session. @@ -54,631 +50,720 @@ import static io.seata.core.model.GlobalStatus.Committing; */ 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 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 volatile boolean active = true; - - private final ArrayList branchSessions = new ArrayList<>(); - - private GlobalSessionLock globalSessionLock = new GlobalSessionLock(); - - /** - * Add boolean. - * @param branchSession the branch session - * @return the boolean - */ - public boolean add(BranchSession branchSession) { - return branchSessions.add(branchSession); - } - - /** - * Remove boolean. - * @param branchSession the branch session - * @return the boolean - */ - public boolean remove(BranchSession branchSession) { - return branchSessions.remove(branchSession); - } - - private Set lifecycleListeners = new HashSet<>(); - - /** - * Can be committed async boolean. - * @return the boolean - */ - public boolean canBeCommittedAsync() { - for (BranchSession branchSession : branchSessions) { - if (!branchSession.canBeCommittedAsync()) { - return false; - } - } - return true; - } - - /** - * Has AT branch - * @return the boolean - */ - public boolean hasATBranch() { - for (BranchSession branchSession : branchSessions) { - if (branchSession.getBranchType() == BranchType.AT) { - return true; - } - } - return false; - } - - /** - * Is saga type transaction - * @return is saga - */ - public boolean isSaga() { - 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 changeStatus(GlobalStatus status) throws TransactionException { - 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 { - // Clean locks first - clean(); - - for (SessionLifecycleListener lifecycleListener : lifecycleListeners) { - lifecycleListener.onEnd(this); - } - } - - public void clean() throws TransactionException { - if (this.hasATBranch()) { - 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(); - 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); - } - - @Override - public void removeBranch(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()); - } - } - for (SessionLifecycleListener lifecycleListener : lifecycleListeners) { - lifecycleListener.onRemoveBranch(this, branchSession); - } - remove(branchSession); - } - - /** - * Gets branch. - * @param branchId the branch id - * @return the branch - */ - public BranchSession getBranch(long branchId) { - synchronized (branchSessions) { - for (BranchSession branchSession : branchSessions) { - if (branchSession.getBranchId() == branchId) { - return branchSession; - } - } - - return null; - } - } - - /** - * Gets sorted branches. - * @return the sorted branches - */ - public ArrayList getSortedBranches() { - return new ArrayList<>(branchSessions); - } - - /** - * Gets reverse sorted branches. - * @return the reverse sorted branches - */ - public ArrayList getReverseSortedBranches() { - ArrayList reversed = new ArrayList<>(branchSessions); - Collections.reverse(reversed); - return reversed; - } - - /** - * Instantiates a new Global session. - */ - public GlobalSession() { - } - - /** - * 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.transactionId = UUIDGenerator.generateUUID(); - this.status = GlobalStatus.Begin; - - this.applicationId = applicationId; - this.transactionServiceGroup = transactionServiceGroup; - this.transactionName = transactionName; - this.timeout = timeout; - this.xid = XID.generateXID(transactionId); - } - - /** - * 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; - } - - /** - * 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); - 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) { - 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 branchSessions.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 call() throws TransactionException; - - } - - public ArrayList getBranchSessions() { - return branchSessions; - } - - public void asyncCommit() throws TransactionException { - this.addSessionLifecycleListener(SessionHolder.getAsyncCommittingSessionManager()); - SessionHolder.getAsyncCommittingSessionManager().addGlobalSession(this); - this.changeStatus(GlobalStatus.AsyncCommitting); - } - - public void queueToRetryCommit() throws TransactionException { - this.addSessionLifecycleListener(SessionHolder.getRetryCommittingSessionManager()); - SessionHolder.getRetryCommittingSessionManager().addGlobalSession(this); - this.changeStatus(GlobalStatus.CommitRetrying); - } - - public void queueToRetryRollback() throws TransactionException { - this.addSessionLifecycleListener(SessionHolder.getRetryRollbackingSessionManager()); - SessionHolder.getRetryRollbackingSessionManager().addGlobalSession(this); - GlobalStatus currentStatus = this.getStatus(); - if (SessionHelper.isTimeoutGlobalStatus(currentStatus)) { - this.changeStatus(GlobalStatus.TimeoutRollbackRetrying); - } - else { - this.changeStatus(GlobalStatus.RollbackRetrying); - } - } - + private static final Logger LOGGER = LoggerFactory.getLogger(GlobalSession.class); + + private static final int MAX_GLOBAL_SESSION_SIZE = StoreConfig.getMaxGlobalSessionSize(); + + private static ThreadLocal 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 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 lifecycleListeners = new HashSet<>(); + + /** + * Can be committed async boolean. + * + * @return the boolean + */ + public boolean canBeCommittedAsync() { + List branchSessions = getBranchSessions(); + for (BranchSession branchSession : branchSessions) { + if (!branchSession.canBeCommittedAsync()) { + return false; + } + } + return true; + } + + /** + * Has AT branch + * + * @return the boolean + */ + public boolean hasATBranch() { + List 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 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 (isSuccessEnd()) { + // Clean locks first + clean(); + for (SessionLifecycleListener lifecycleListener : lifecycleListeners) { + lifecycleListener.onSuccessEnd(this); + } + } else { + for (SessionLifecycleListener lifecycleListener : lifecycleListeners) { + lifecycleListener.onFailEnd(this); + } + } + } + + public boolean isSuccessEnd() { + if (status == GlobalStatus.Committed || status == GlobalStatus.Rollbacked + || status == GlobalStatus.TimeoutRollbacked) { + return true; + } + return false; + } + + public void clean() throws TransactionException { + if (!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 removeBranch(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()); + } + } + for (SessionLifecycleListener lifecycleListener : lifecycleListeners) { + lifecycleListener.onRemoveBranch(this, branchSession); + } + remove(branchSession); + } + + /** + * Gets branch. + * + * @param branchId the branch id + * @return the branch + */ + public BranchSession getBranch(long branchId) { + synchronized (this) { + List branchSessions = getBranchSessions(); + for (BranchSession branchSession : branchSessions) { + if (branchSession.getBranchId() == branchId) { + return branchSession; + } + } + + return null; + } + } + + /** + * Gets sorted branches. + * + * @return the sorted branches + */ + public List getSortedBranches() { + return new ArrayList<>(getBranchSessions()); + } + + /** + * Gets reverse sorted branches. + * + * @return the reverse sorted branches + */ + public List getReverseSortedBranches() { + List 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 call() throws TransactionException; + } + + public List 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 + '}'; + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/GlobalSessionHandler.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/GlobalSessionHandler.java index a7ddd2d78..515b155bb 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/GlobalSessionHandler.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/GlobalSessionHandler.java @@ -26,11 +26,11 @@ import io.seata.core.exception.TransactionException; @FunctionalInterface public interface GlobalSessionHandler { - /** - * Handle global session. - * @param globalSession the global session - * @throws TransactionException the transaction exception - */ - void handle(GlobalSession globalSession) throws TransactionException; - + /** + * Handle global session. + * + * @param globalSession the global session + * @throws TransactionException the transaction exception + */ + void handle(GlobalSession globalSession) throws TransactionException; } \ No newline at end of file diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/Lockable.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/Lockable.java index 71205a9e2..d023e632f 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/Lockable.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/Lockable.java @@ -24,18 +24,19 @@ import io.seata.core.exception.TransactionException; */ 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; + /** + * 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; } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/Reloadable.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/Reloadable.java index 74f87de19..5701c69d6 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/Reloadable.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/Reloadable.java @@ -22,9 +22,8 @@ package io.seata.server.session; */ public interface Reloadable { - /** - * Reload states. - */ - void reload(); - + /** + * Reload states. + */ + void reload(); } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionCondition.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionCondition.java index 3f6d36ead..6542a72c3 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionCondition.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionCondition.java @@ -23,110 +23,123 @@ import io.seata.core.model.GlobalStatus; * @author slievrly */ public class SessionCondition { + private Long transactionId; + private String xid; + private GlobalStatus status; + private GlobalStatus[] statuses; + private Long overTimeAliveMills; + private boolean lazyLoadBranch; - private Long transactionId; + /** + * Instantiates a new Session condition. + */ + public SessionCondition() { + } - private String xid; + /** + * Instantiates a new Session condition. + * + * @param xid the xid + */ + public SessionCondition(String xid) { + this.xid = xid; + } - private GlobalStatus status; + /** + * Instantiates a new Session condition. + * + * @param status the status + */ + public SessionCondition(GlobalStatus status) { + this.status = status; + this.statuses = new GlobalStatus[] {status}; + } - private GlobalStatus[] statuses; + /** + * Instantiates a new Session condition. + * + * @param statuses the statuses + */ + public SessionCondition(GlobalStatus... statuses) { + this.statuses = statuses; + } - private long overTimeAliveMills; + /** + * Instantiates a new Session condition. + * + * @param overTimeAliveMills the over time alive mills + */ + public SessionCondition(long overTimeAliveMills) { + this.overTimeAliveMills = overTimeAliveMills; + } - /** - * Instantiates a new Session condition. - */ - public SessionCondition() { - } + /** + * Gets status. + * + * @return the status + */ + public GlobalStatus getStatus() { + return status; + } - /** - * Instantiates a new Session condition. - * @param xid the xid - */ - public SessionCondition(String xid) { - this.xid = xid; - } + /** + * Sets status. + * + * @param status the status + */ + public void setStatus(GlobalStatus status) { + this.status = status; + this.statuses = new GlobalStatus[] {status}; + } - /** - * Instantiates a new Session condition. - * @param status the status - */ - public SessionCondition(GlobalStatus status) { - this.status = status; - statuses = new GlobalStatus[] { status }; - } + /** + * Gets over time alive mills. + * + * @return the over time alive mills + */ + public Long getOverTimeAliveMills() { + return overTimeAliveMills; + } - /** - * Instantiates a new Session condition. - * @param statuses the statuses - */ - public SessionCondition(GlobalStatus[] statuses) { - this.statuses = statuses; - } + /** + * Sets over time alive mills. + * + * @param overTimeAliveMills the over time alive mills + */ + public void setOverTimeAliveMills(Long overTimeAliveMills) { + this.overTimeAliveMills = overTimeAliveMills; + } - /** - * Instantiates a new Session condition. - * @param overTimeAliveMills the over time alive mills - */ - public SessionCondition(long overTimeAliveMills) { - this.overTimeAliveMills = overTimeAliveMills; - } + public Long getTransactionId() { + return transactionId; + } - /** - * Gets status. - * @return the status - */ - public GlobalStatus getStatus() { - return status; - } + public void setTransactionId(Long transactionId) { + this.transactionId = transactionId; + } - /** - * Sets status. - * @param status the status - */ - public void setStatus(GlobalStatus status) { - this.status = status; - } + public String getXid() { + return xid; + } - /** - * Gets over time alive mills. - * @return the over time alive mills - */ - public long getOverTimeAliveMills() { - return overTimeAliveMills; - } + public void setXid(String xid) { + this.xid = xid; + } - /** - * Sets over time alive mills. - * @param overTimeAliveMills the over time alive mills - */ - public void setOverTimeAliveMills(long overTimeAliveMills) { - this.overTimeAliveMills = overTimeAliveMills; - } + public GlobalStatus[] getStatuses() { + return statuses; + } - public Long getTransactionId() { - return transactionId; - } + public void setStatuses(GlobalStatus... statuses) { + this.statuses = statuses; + } - public void setTransactionId(Long transactionId) { - this.transactionId = transactionId; - } + public boolean isLazyLoadBranch() { + return lazyLoadBranch; + } - 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 void setLazyLoadBranch(boolean lazyLoadBranch) { + this.lazyLoadBranch = lazyLoadBranch; + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionHelper.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionHelper.java index 63f77945d..c8370f1f1 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionHelper.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionHelper.java @@ -15,180 +15,309 @@ */ package io.seata.server.session; -import java.util.Collection; - +import io.seata.common.util.CollectionUtils; +import io.seata.config.Configuration; +import io.seata.config.ConfigurationFactory; +import io.seata.core.constants.ConfigurationKeys; import io.seata.core.context.RootContext; import io.seata.core.exception.TransactionException; import io.seata.core.model.BranchType; import io.seata.core.model.GlobalStatus; +import io.seata.metrics.IdConstants; import io.seata.server.UUIDGenerator; +import io.seata.server.coordinator.DefaultCoordinator; +import io.seata.server.metrics.MetricsPublisher; +import io.seata.server.store.StoreConfig; +import io.seata.server.store.StoreConfig.SessionMode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +import static io.seata.common.DefaultValues.DEFAULT_ENABLE_BRANCH_ASYNC_REMOVE; + /** * The type Session helper. * * @author sharajava */ public class SessionHelper { + private static final Logger LOGGER = LoggerFactory.getLogger(SessionHelper.class); - private static final Logger LOGGER = LoggerFactory.getLogger(SessionHelper.class); + /** + * The constant CONFIG. + */ + private static final Configuration CONFIG = ConfigurationFactory.getInstance(); - private SessionHelper() { - } + private static final Boolean ENABLE_BRANCH_ASYNC_REMOVE = CONFIG.getBoolean( + ConfigurationKeys.ENABLE_BRANCH_ASYNC_REMOVE, DEFAULT_ENABLE_BRANCH_ASYNC_REMOVE); - public static BranchSession newBranchByGlobal(GlobalSession globalSession, BranchType branchType, String resourceId, - String lockKeys, String clientId) { - return newBranchByGlobal(globalSession, branchType, resourceId, null, lockKeys, clientId); - } + /** + * The instance of DefaultCoordinator + */ + private static final DefaultCoordinator COORDINATOR = DefaultCoordinator.getInstance(); - /** - * 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(); + private static final boolean DELAY_HANDLE_SESSION = StoreConfig.getSessionMode() != SessionMode.FILE; - 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); + private SessionHelper() { + } - return branchSession; - } + public static BranchSession newBranchByGlobal(GlobalSession globalSession, BranchType branchType, String resourceId, String lockKeys, String clientId) { + return newBranchByGlobal(globalSession, branchType, resourceId, null, lockKeys, clientId); + } - /** - * 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; - } + /** + * 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(); - /** - * End committed. - * @param globalSession the global session - * @throws TransactionException the transaction exception - */ - public static void endCommitted(GlobalSession globalSession) throws TransactionException { - globalSession.changeStatus(GlobalStatus.Committed); - globalSession.end(); - } + 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); - /** - * End commit failed. - * @param globalSession the global session - * @throws TransactionException the transaction exception - */ - public static void endCommitFailed(GlobalSession globalSession) throws TransactionException { - globalSession.changeStatus(GlobalStatus.CommitFailed); - globalSession.end(); - } + return branchSession; + } - /** - * End rollbacked. - * @param globalSession the global session - * @throws TransactionException the transaction exception - */ - public static void endRollbacked(GlobalSession globalSession) throws TransactionException { - GlobalStatus currentStatus = globalSession.getStatus(); - if (isTimeoutGlobalStatus(currentStatus)) { - globalSession.changeStatus(GlobalStatus.TimeoutRollbacked); - } - else { - globalSession.changeStatus(GlobalStatus.Rollbacked); - } - globalSession.end(); - } + /** + * 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 rollback failed. - * @param globalSession the global session - * @throws TransactionException the transaction exception - */ - public static void endRollbackFailed(GlobalSession globalSession) throws TransactionException { - GlobalStatus currentStatus = globalSession.getStatus(); - if (isTimeoutGlobalStatus(currentStatus)) { - globalSession.changeStatus(GlobalStatus.TimeoutRollbackFailed); - } - else { - globalSession.changeStatus(GlobalStatus.RollbackFailed); - } - globalSession.end(); - } + /** + * 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 { + MetricsPublisher.postSessionDoneEvent(globalSession, false, false); + } + } - public static boolean isTimeoutGlobalStatus(GlobalStatus status) { - return status == GlobalStatus.TimeoutRollbacked || status == GlobalStatus.TimeoutRollbackFailed - || status == GlobalStatus.TimeoutRollbacking || status == GlobalStatus.TimeoutRollbackRetrying; - } + /** + * 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); + } - /** - * Foreach global sessions. - * @param sessions the global sessions - * @param handler the handler - * @since 1.5.0 - */ - public static void forEach(Collection sessions, GlobalSessionHandler handler) { - for (GlobalSession globalSession : sessions) { - 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); - } - } - } + /** + * 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()); - /** - * Foreach branch sessions. - * @param sessions the branch session - * @param handler the handler - * @since 1.5.0 - */ - public static Boolean forEach(Collection 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; - } + 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 { + 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 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 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 { + if (Objects.equals(Boolean.TRUE, ENABLE_BRANCH_ASYNC_REMOVE) && isAsync) { + COORDINATOR.doBranchRemoveAsync(globalSession, branchSession); + } else { + globalSession.removeBranch(branchSession); + } + } + + /** + * remove branchSession from globalSession + * @param globalSession the globalSession + * @param isAsync if asynchronous remove + */ + public static void removeAllBranch(GlobalSession globalSession, boolean isAsync) + throws TransactionException { + List branchSessions = globalSession.getSortedBranches(); + if (branchSessions == null || branchSessions.isEmpty()) { + return; + } + if (Objects.equals(Boolean.TRUE, ENABLE_BRANCH_ASYNC_REMOVE) && isAsync) { + COORDINATOR.doBranchRemoveAllAsync(globalSession); + } else { + for (BranchSession branchSession : branchSessions) { + globalSession.removeBranch(branchSession); + } + } + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionHolder.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionHolder.java index 6393eeda6..849a78969 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionHolder.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionHolder.java @@ -15,12 +15,8 @@ */ package io.seata.server.session; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; - +import io.seata.common.ConfigurationKeys; +import io.seata.common.XID; import io.seata.common.exception.ShouldNeverHappenException; import io.seata.common.exception.StoreException; import io.seata.common.loader.EnhancedServiceLoader; @@ -28,18 +24,23 @@ import io.seata.common.util.CollectionUtils; import io.seata.common.util.StringUtils; import io.seata.config.Configuration; import io.seata.config.ConfigurationFactory; -import io.seata.core.constants.ConfigurationKeys; import io.seata.core.exception.TransactionException; import io.seata.core.model.GlobalStatus; -import io.seata.core.store.StoreMode; +import io.seata.core.model.LockStatus; +import io.seata.core.store.DistributedLockDO; +import io.seata.core.store.DistributedLocker; +import io.seata.server.lock.distributed.DistributedLockerFactory; +import io.seata.server.store.StoreConfig; +import io.seata.server.store.StoreConfig.SessionMode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static io.seata.common.Constants.ASYNC_COMMITTING; -import static io.seata.common.Constants.RETRY_COMMITTING; -import static io.seata.common.Constants.RETRY_ROLLBACKING; -import static io.seata.common.Constants.TX_TIMEOUT_CHECK; -import static io.seata.common.Constants.UNDOLOG_DELETE; +import java.io.IOException; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import static io.seata.common.DefaultValues.DEFAULT_DISTRIBUTED_LOCK_EXPIRE_TIME; /** * The type Session holder. @@ -48,394 +49,377 @@ import static io.seata.common.Constants.UNDOLOG_DELETE; */ public class SessionHolder { - private static final Logger LOGGER = LoggerFactory.getLogger(SessionHolder.class); + private static final Logger LOGGER = LoggerFactory.getLogger(SessionHolder.class); - /** - * The constant CONFIG. - */ - protected static final Configuration CONFIG = ConfigurationFactory.getInstance(); + /** + * 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 constant ROOT_SESSION_MANAGER_NAME. - */ - public static final String ROOT_SESSION_MANAGER_NAME = "root.data"; + /** + * The default session store dir + */ + public static final String DEFAULT_SESSION_STORE_FILE_DIR = "sessionStore"; - /** - * The constant ASYNC_COMMITTING_SESSION_MANAGER_NAME. - */ - public static final String ASYNC_COMMITTING_SESSION_MANAGER_NAME = "async.commit.data"; + /** + * 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); - /** - * The constant RETRY_COMMITTING_SESSION_MANAGER_NAME. - */ - public static final String RETRY_COMMITTING_SESSION_MANAGER_NAME = "retry.commit.data"; + 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; - /** - * The constant RETRY_ROLLBACKING_SESSION_MANAGER_NAME. - */ - public static final String RETRY_ROLLBACKING_SESSION_MANAGER_NAME = "retry.rollback.data"; + private static DistributedLocker DISTRIBUTED_LOCKER; - /** - * The default session store dir - */ - public static final String DEFAULT_SESSION_STORE_FILE_DIR = "sessionStore"; + 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(); + } + 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}); - private static SessionManager ROOT_SESSION_MANAGER; + 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; - private static SessionManager ASYNC_COMMITTING_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}); - private static SessionManager RETRY_COMMITTING_SESSION_MANAGER; + DISTRIBUTED_LOCKER = DistributedLockerFactory.getDistributedLocker(SessionMode.REDIS.getName()); + } else { + // unknown store + throw new IllegalArgumentException("unknown store mode:" + sessionMode.getName()); + } + reload(sessionMode); + } - private static SessionManager RETRY_ROLLBACKING_SESSION_MANAGER; + //region reload - /** - * Init. - * @param mode the store mode: file, db - * @throws IOException the io exception - */ - public static void init(String mode) { - if (StringUtils.isBlank(mode)) { - mode = CONFIG.getConfig(ConfigurationKeys.STORE_MODE); - } - StoreMode storeMode = StoreMode.get(mode); - if (StoreMode.DB.equals(storeMode)) { - ROOT_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.DB.getName()); - ASYNC_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.DB.getName(), - new Object[] { ASYNC_COMMITTING_SESSION_MANAGER_NAME }); - RETRY_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.DB.getName(), - new Object[] { RETRY_COMMITTING_SESSION_MANAGER_NAME }); - RETRY_ROLLBACKING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.DB.getName(), - new Object[] { RETRY_ROLLBACKING_SESSION_MANAGER_NAME }); - } - else if (StoreMode.FILE.equals(storeMode)) { - String sessionStorePath = CONFIG.getConfig(ConfigurationKeys.STORE_FILE_DIR, - DEFAULT_SESSION_STORE_FILE_DIR); - if (StringUtils.isBlank(sessionStorePath)) { - throw new StoreException("the {store.file.dir} is empty."); - } - ROOT_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.FILE.getName(), - new Object[] { ROOT_SESSION_MANAGER_NAME, sessionStorePath }); - ASYNC_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, - StoreMode.FILE.getName(), new Class[] { String.class, String.class }, - new Object[] { ASYNC_COMMITTING_SESSION_MANAGER_NAME, null }); - RETRY_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, - StoreMode.FILE.getName(), new Class[] { String.class, String.class }, - new Object[] { RETRY_COMMITTING_SESSION_MANAGER_NAME, null }); - RETRY_ROLLBACKING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, - StoreMode.FILE.getName(), new Class[] { String.class, String.class }, - new Object[] { RETRY_ROLLBACKING_SESSION_MANAGER_NAME, null }); - } - else if (StoreMode.REDIS.equals(storeMode)) { - ROOT_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.REDIS.getName()); - ASYNC_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, - StoreMode.REDIS.getName(), new Object[] { ASYNC_COMMITTING_SESSION_MANAGER_NAME }); - RETRY_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, - StoreMode.REDIS.getName(), new Object[] { RETRY_COMMITTING_SESSION_MANAGER_NAME }); - RETRY_ROLLBACKING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, - StoreMode.REDIS.getName(), new Object[] { RETRY_ROLLBACKING_SESSION_MANAGER_NAME }); - } - else { - // unknown store - throw new IllegalArgumentException("unknown store mode:" + mode); - } - reload(storeMode); - } + /** + * Reload. + * + * @param sessionMode the mode of store + */ + protected static void reload(SessionMode sessionMode) { - // region reload + if (ROOT_SESSION_MANAGER instanceof Reloadable) { + ((Reloadable) ROOT_SESSION_MANAGER).reload(); + } - /** - * Reload. - */ - protected static void reload(StoreMode storeMode) { - if (ROOT_SESSION_MANAGER instanceof Reloadable) { - ((Reloadable) ROOT_SESSION_MANAGER).reload(); - } + if (SessionMode.FILE.equals(sessionMode)) { + Collection allSessions = ROOT_SESSION_MANAGER.allSessions(); + if (CollectionUtils.isNotEmpty(allSessions)) { + for (GlobalSession globalSession : allSessions) { + GlobalStatus globalStatus = globalSession.getStatus(); + switch (globalStatus) { + case UnKnown: + case Committed: + case CommitFailed: + case Rollbacked: + case RollbackFailed: + case TimeoutRollbacked: + case TimeoutRollbackFailed: + case Finished: + removeInErrorState(globalSession); + break; + case AsyncCommitting: + queueToAsyncCommitting(globalSession); + break; + 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); - Collection allSessions = ROOT_SESSION_MANAGER.allSessions(); - if (CollectionUtils.isNotEmpty(allSessions)) { - List removeGlobalSessions = new ArrayList<>(); - Iterator iterator = allSessions.iterator(); - while (iterator.hasNext()) { - GlobalSession globalSession = iterator.next(); - GlobalStatus globalStatus = globalSession.getStatus(); - switch (globalStatus) { - case UnKnown: - case Committed: - case CommitFailed: - case Rollbacked: - case RollbackFailed: - case TimeoutRollbacked: - case TimeoutRollbackFailed: - case Finished: - removeGlobalSessions.add(globalSession); - break; - case AsyncCommitting: - if (storeMode == StoreMode.FILE) { - queueToAsyncCommitting(globalSession); - } - break; - default: { - if (storeMode == StoreMode.FILE) { - lockBranchSessions(globalSession.getSortedBranches()); + long now = System.currentTimeMillis(); + List errorStatusGlobalSessions = ROOT_SESSION_MANAGER.findGlobalSessions(searchCondition); + while (!CollectionUtils.isEmpty(errorStatusGlobalSessions)) { + for (GlobalSession errorStatusGlobalSession : errorStatusGlobalSessions) { + if (errorStatusGlobalSession.getBeginTime() >= now) { + // Exit when the global transaction begin after the instance started + return; + } - switch (globalStatus) { - case Committing: - case CommitRetrying: - queueToRetryCommit(globalSession); - break; - case Rollbacking: - case RollbackRetrying: - case TimeoutRollbacking: - case TimeoutRollbackRetrying: - queueToRetryRollback(globalSession); - break; - case Begin: - globalSession.setActive(true); - break; - default: - throw new ShouldNeverHappenException("NOT properly handled " + globalStatus); - } - } - break; - } - } - } - for (GlobalSession globalSession : removeGlobalSessions) { - removeInErrorState(globalSession); - } - } - } + removeInErrorState(errorStatusGlobalSession); + } - 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); - } - } + // Load the next part + errorStatusGlobalSessions = ROOT_SESSION_MANAGER.findGlobalSessions(searchCondition); + } + }); + } + } - private static void queueToAsyncCommitting(GlobalSession globalSession) { - try { - globalSession.addSessionLifecycleListener(getAsyncCommittingSessionManager()); - getAsyncCommittingSessionManager().addGlobalSession(globalSession); - } - catch (TransactionException e) { - throw new ShouldNeverHappenException(e); - } - } + 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 lockBranchSessions(ArrayList branchSessions) { - branchSessions.forEach(branchSession -> { - try { - branchSession.lock(); - } - catch (TransactionException e) { - throw new ShouldNeverHappenException(e); - } - }); - } + private static void queueToAsyncCommitting(GlobalSession globalSession) { + try { + globalSession.addSessionLifecycleListener(getAsyncCommittingSessionManager()); + getAsyncCommittingSessionManager().addGlobalSession(globalSession); + } 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 lockBranchSessions(List branchSessions) { + branchSessions.forEach(branchSession -> { + try { + branchSession.lock(); + } 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); - } - } + private static void queueToRetryCommit(GlobalSession globalSession) { + try { + globalSession.addSessionLifecycleListener(getRetryCommittingSessionManager()); + getRetryCommittingSessionManager().addGlobalSession(globalSession); + } catch (TransactionException e) { + throw new ShouldNeverHappenException(e); + } + } - // endregion + private static void queueToRetryRollback(GlobalSession globalSession) { + try { + globalSession.addSessionLifecycleListener(getRetryRollbackingSessionManager()); + getRetryRollbackingSessionManager().addGlobalSession(globalSession); + } catch (TransactionException e) { + throw new ShouldNeverHappenException(e); + } + } - // region get session manager + //endregion - /** - * 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; - } + //region get session manager - /** - * Gets async committing session manager. - * @return the async committing session manager - */ - public static SessionManager getAsyncCommittingSessionManager() { - if (ASYNC_COMMITTING_SESSION_MANAGER == null) { - throw new ShouldNeverHappenException("SessionManager is NOT init!"); - } - return ASYNC_COMMITTING_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 retry committing session manager. - * @return the retry committing session manager - */ - public static SessionManager getRetryCommittingSessionManager() { - if (RETRY_COMMITTING_SESSION_MANAGER == null) { - throw new ShouldNeverHappenException("SessionManager is NOT init!"); - } - return RETRY_COMMITTING_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 rollbacking session manager. - * @return the retry rollbacking session manager - */ - public static SessionManager getRetryRollbackingSessionManager() { - if (RETRY_ROLLBACKING_SESSION_MANAGER == null) { - throw new ShouldNeverHappenException("SessionManager is NOT init!"); - } - return RETRY_ROLLBACKING_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; + } - // endregion + /** + * 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; + } - /** - * Find global session. - * @param xid the xid - * @return the global session - */ - public static GlobalSession findGlobalSession(String xid) { - return findGlobalSession(xid, true); - } + //endregion - /** - * 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); - } + /** + * Find global session. + * + * @param xid the xid + * @return the global session + */ + public static GlobalSession findGlobalSession(String xid) { + return findGlobalSession(xid, true); + } - /** - * lock and execute - * @param globalSession the global session - * @param lockCallable the lock Callable - * @return the value - */ - public static T lockAndExecute(GlobalSession globalSession, GlobalSession.LockCallable lockCallable) - throws TransactionException { - return getRootSessionManager().lockAndExecute(globalSession, lockCallable); - } + /** + * 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); + } - /** - * retry rollbacking lock - * @return the boolean - */ - public static boolean retryRollbackingLock() { - return getRootSessionManager().scheduledLock(RETRY_ROLLBACKING); - } + /** + * lock and execute + * + * @param globalSession the global session + * @param lockCallable the lock Callable + * @return the value + */ + public static T lockAndExecute(GlobalSession globalSession, GlobalSession.LockCallable lockCallable) + throws TransactionException { + return getRootSessionManager().lockAndExecute(globalSession, lockCallable); + } - /** - * retry committing lock - * @return the boolean - */ - public static boolean retryCommittingLock() { - return getRootSessionManager().scheduledLock(RETRY_COMMITTING); - } + /** + * 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)); + } - /** - * async committing lock - * @return the boolean - */ - public static boolean asyncCommittingLock() { - return getRootSessionManager().scheduledLock(ASYNC_COMMITTING); - } + /** + * release lock + * + * @return the boolean + */ + public static boolean releaseDistributedLock(String lockKey) { + return DISTRIBUTED_LOCKER.releaseLock(new DistributedLockDO(lockKey, XID.getIpAddressAndPort(), DISTRIBUTED_LOCK_EXPIRE_TIME)); + } - /** - * tx timeout check lOck - * @return the boolean - */ - public static boolean txTimeoutCheckLock() { - return getRootSessionManager().scheduledLock(TX_TIMEOUT_CHECK); - } + /** + * 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.info("Exception running function with key = {}", key, e); + } finally { + if (lock) { + try { + SessionHolder.releaseDistributedLock(key); + } catch (Exception ex) { + LOGGER.warn("release distribute lock failure, message = {}", ex.getMessage(), ex); + } + } + } + return lock; + } - /** - * undolog delete lock - * @return the boolean - */ - public static boolean undoLogDeleteLock() { - return getRootSessionManager().scheduledLock(UNDOLOG_DELETE); - } - - /** - * un retry rollbacking lock - * @return the boolean - */ - public static boolean unRetryRollbackingLock() { - return getRootSessionManager().unScheduledLock(RETRY_ROLLBACKING); - } - - /** - * un retry committing lock - * @return the boolean - */ - public static boolean unRetryCommittingLock() { - return getRootSessionManager().unScheduledLock(RETRY_COMMITTING); - } - - /** - * un async committing lock - * @return the boolean - */ - public static boolean unAsyncCommittingLock() { - return getRootSessionManager().unScheduledLock(ASYNC_COMMITTING); - } - - /** - * un tx timeout check lOck - * @return the boolean - */ - public static boolean unTxTimeoutCheckLock() { - return getRootSessionManager().unScheduledLock(TX_TIMEOUT_CHECK); - } - - /** - * un undolog delete lock - * @return the boolean - */ - public static boolean unUndoLogDeleteLock() { - return getRootSessionManager().unScheduledLock(UNDOLOG_DELETE); - } - - public static void destroy() { - if (ROOT_SESSION_MANAGER != null) { - ROOT_SESSION_MANAGER.destroy(); - } - if (ASYNC_COMMITTING_SESSION_MANAGER != null) { - ASYNC_COMMITTING_SESSION_MANAGER.destroy(); - } - if (RETRY_COMMITTING_SESSION_MANAGER != null) { - RETRY_COMMITTING_SESSION_MANAGER.destroy(); - } - if (RETRY_ROLLBACKING_SESSION_MANAGER != null) { - RETRY_ROLLBACKING_SESSION_MANAGER.destroy(); - } - } + public static void destroy() { + if (ROOT_SESSION_MANAGER != null) { + ROOT_SESSION_MANAGER.destroy(); + } + } + @FunctionalInterface + public interface NoArgsFunc { + void call(); + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionLifecycle.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionLifecycle.java index 8c54200cf..05cd87a3a 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionLifecycle.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionLifecycle.java @@ -26,57 +26,64 @@ import io.seata.core.model.GlobalStatus; */ public interface SessionLifecycle { - /** - * Begin. - * @throws TransactionException the transaction exception - */ - void begin() throws TransactionException; + /** + * Begin. + * + * @throws TransactionException the transaction exception + */ + void begin() throws TransactionException; - /** - * Change status. - * @param status the status - * @throws TransactionException the transaction exception - */ - void changeStatus(GlobalStatus status) 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; + /** + * 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; + /** + * Add branch. + * + * @param branchSession the branch session + * @throws TransactionException the transaction exception + */ + void addBranch(BranchSession branchSession) throws TransactionException; - /** - * Remove branch. - * @param branchSession the branch session - * @throws TransactionException the transaction exception - */ - void removeBranch(BranchSession branchSession) throws TransactionException; + /** + * Remove branch. + * + * @param branchSession the branch session + * @throws TransactionException the transaction exception + */ + void removeBranch(BranchSession branchSession) throws TransactionException; - /** - * Is active boolean. - * @return the boolean - */ - boolean isActive(); + /** + * 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; + /** + * Close. + * + * @throws TransactionException the transaction exception + */ + void close() throws TransactionException; + /** + * end. + * + * @throws TransactionException the transaction exception + */ + void end() throws TransactionException; } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionLifecycleListener.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionLifecycleListener.java index dd8f03bed..693d5b695 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionLifecycleListener.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionLifecycleListener.java @@ -26,59 +26,73 @@ import io.seata.core.model.GlobalStatus; */ public interface SessionLifecycleListener { - /** - * On begin. - * @param globalSession the global session - * @throws TransactionException the transaction exception - */ - void onBegin(GlobalSession globalSession) throws TransactionException; + /** + * 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 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 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 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 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 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 onEnd(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; } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionManager.java index a95184d18..ac357eb99 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionManager.java @@ -15,13 +15,14 @@ */ package io.seata.server.session; -import java.util.Collection; -import java.util.List; import io.seata.core.exception.TransactionException; import io.seata.core.model.BranchStatus; import io.seata.core.model.GlobalStatus; import io.seata.core.rpc.Disposable; +import java.util.Collection; +import java.util.List; + /** * The interface Session manager. * @@ -29,105 +30,98 @@ import io.seata.core.rpc.Disposable; */ public interface SessionManager extends SessionLifecycleListener, Disposable { - /** - * Add global session. - * @param session the session - * @throws TransactionException the transaction exception - */ - void addGlobalSession(GlobalSession session) throws TransactionException; + /** + * 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 + * @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); + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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; + /** + * 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 allSessions(); + /** + * All sessions collection. + * + * @return the collection + */ + Collection allSessions(); - /** - * Find global sessions list. - * @param condition the condition - * @return the list - */ - List findGlobalSessions(SessionCondition condition); - - /** - * lock and execute - * @param globalSession the global session - * @param lockCallable the lock Callable - * @return the value - */ - T lockAndExecute(GlobalSession globalSession, GlobalSession.LockCallable lockCallable) - throws TransactionException; - - /** - * scheduled lock - * @param key the lock key - * @return the boolean - */ - default boolean scheduledLock(String key) { - return true; - } - - /** - * un scheduled lock - * @param key the lock key - * @return the boolean - */ - default boolean unScheduledLock(String key) { - return true; - } + /** + * Find global sessions list. + * + * @param condition the condition + * @return the list + */ + List 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 lockAndExecute(GlobalSession globalSession, GlobalSession.LockCallable lockCallable) + throws TransactionException; } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionStatusValidator.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionStatusValidator.java new file mode 100644 index 000000000..20323a7e7 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/session/SessionStatusValidator.java @@ -0,0 +1,87 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.session; + +import io.seata.core.model.GlobalStatus; + +/** + * The type change status validator. + * + * @author Bughue + */ +public class SessionStatusValidator { + + /** + * is timeout global status + * + * @param status the global session + */ + public static boolean isTimeoutGlobalStatus(GlobalStatus status) { + return status == GlobalStatus.TimeoutRollbacked + || status == GlobalStatus.TimeoutRollbackFailed + || status == GlobalStatus.TimeoutRollbacking + || status == GlobalStatus.TimeoutRollbackRetrying; + } + + /** + * is rollback global status + * + * @param status the global session + */ + public static boolean isRollbackGlobalStatus(GlobalStatus status) { + return status == GlobalStatus.Rollbacking + || status == GlobalStatus.RollbackRetrying + || status == GlobalStatus.Rollbacked + || status == GlobalStatus.RollbackFailed + || status == GlobalStatus.RollbackRetryTimeout; + } + + /** + * is commit global status + * + * @param status the global session + */ + public static boolean isCommitGlobalStatus(GlobalStatus status) { + return status == GlobalStatus.Committing + || status == GlobalStatus.AsyncCommitting + || status == GlobalStatus.CommitRetrying + || status == GlobalStatus.Committed + || status == GlobalStatus.CommitFailed + || status == GlobalStatus.CommitRetryTimeout; + } + + /** + * check the relation of before status and after status + * + * @param before the global session + * @param after the global session + */ + public static boolean validateUpdateStatus(GlobalStatus before, GlobalStatus after) { + if (isTimeoutGlobalStatus(before) && isCommitGlobalStatus(after)) { + return false; + } + if (isCommitGlobalStatus(before) && isTimeoutGlobalStatus(after)) { + return false; + } + if (isRollbackGlobalStatus(before) && isCommitGlobalStatus(after)) { + return false; + } + if (isCommitGlobalStatus(before) && isRollbackGlobalStatus(after)) { + return false; + } + return true; + } +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/SessionConverter.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/SessionConverter.java index f66c812d4..b6b2f957a 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/SessionConverter.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/SessionConverter.java @@ -15,92 +15,193 @@ */ 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) { - if (globalTransactionDO == null) { - return null; - } - GlobalSession session = new GlobalSession(globalTransactionDO.getApplicationId(), - globalTransactionDO.getTransactionServiceGroup(), globalTransactionDO.getTransactionName(), - globalTransactionDO.getTimeout()); - 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, 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 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 GlobalSession convertGlobalSession(GlobalTransactionDO globalTransactionDO) { + return convertGlobalSession(globalTransactionDO, false); + } - 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; + 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; + } - 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 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; - 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; - } + 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 result, List globalSessions) { + if (CollectionUtils.isNotEmpty(globalSessions)) { + for (GlobalSession globalSession : globalSessions) { + GlobalSessionVO globalSessionVO = new GlobalSessionVO(); + BeanUtils.copyProperties(globalSession,globalSessionVO); + globalSessionVO.setStatus(globalSession.getStatus().getCode()); + globalSessionVO.setTimeout(Long.valueOf(globalSession.getTimeout())); + globalSessionVO.setBranchSessionVOs(converToBranchSession(globalSession.getBranchSessions())); + result.add(globalSessionVO); + } + } + } + + public static Set converToBranchSession(List branchSessions) { + Set branchSessionVOs = new HashSet<>(branchSessions.size()); + if (CollectionUtils.isNotEmpty(branchSessions)) { + for (BranchSession branchSession : branchSessions) { + BranchSessionVO branchSessionVONew = new BranchSessionVO(); + BeanUtils.copyProperties(branchSession,branchSessionVONew); + + branchSessionVONew.setBranchType(branchSession.getBranchType().name()); + branchSessionVONew.setStatus(branchSession.getStatus().getCode()); + branchSessionVOs.add(branchSessionVONew); + } + } + return branchSessionVOs; + } + + /** + * convert GlobalSession to GlobalSessionVO + * + * @param filteredSessions the GlobalSession list + * @return the GlobalSessionVO list + */ + public static List convertGlobalSession(List filteredSessions) { + + if (CollectionUtils.isEmpty(filteredSessions)) { + return Collections.emptyList(); + } + + final ArrayList result = new ArrayList<>(filteredSessions.size()); + + for (GlobalSession session : filteredSessions) { + result.add(new GlobalSessionVO( + session.getXid(), + session.getTransactionId(), + session.getStatus().getCode(), + session.getApplicationId(), + session.getTransactionServiceGroup(), + session.getTransactionName(), + (long) session.getTimeout(), + session.getBeginTime(), + session.getApplicationData(), + convertBranchSession(session.getBranchSessions()) + )); + } + return result; + } + + /** + * convert BranchSession to BranchSessionVO + * + * @param branchSessions the BranchSession list + * @return the BranchSessionVO list + */ + public static Set convertBranchSession(List branchSessions) { + + if (CollectionUtils.isEmpty(branchSessions)) { + return Collections.emptySet(); + } + + final Set result = new HashSet<>(branchSessions.size()); + + for (BranchSession session : branchSessions) { + result.add(new BranchSessionVO( + session.getXid(), + session.getTransactionId(), + session.getBranchId(), + session.getResourceGroupId(), + session.getResourceId(), + session.getBranchType().name(), + session.getStatus().getCode(), + session.getClientId(), + session.getApplicationData() + )); + } + return result; + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/lock/DataBaseDistributedLocker.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/lock/DataBaseDistributedLocker.java new file mode 100644 index 000000000..1b68600da --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/lock/DataBaseDistributedLocker.java @@ -0,0 +1,251 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.storage.db.lock; + + +import io.seata.common.exception.ShouldNeverHappenException; +import io.seata.common.loader.EnhancedServiceLoader; +import io.seata.common.loader.LoadLevel; +import io.seata.common.loader.Scope; +import io.seata.common.util.IOUtil; +import io.seata.common.util.StringUtils; +import io.seata.config.*; +import io.seata.core.constants.ConfigurationKeys; +import io.seata.core.constants.ServerTableColumnsName; +import io.seata.core.store.DistributedLockDO; +import io.seata.core.store.DistributedLocker; +import io.seata.core.store.db.DataSourceProvider; +import io.seata.core.store.db.sql.distributed.lock.DistributedLockSqlFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Objects; + +import static io.seata.core.constants.ConfigurationKeys.DISTRIBUTED_LOCK_DB_TABLE; + +/** + * @author chd + */ +@LoadLevel(name = "db", scope = Scope.SINGLETON) +public class DataBaseDistributedLocker implements DistributedLocker { + private static final Logger LOGGER = LoggerFactory.getLogger(DataBaseDistributedLocker.class); + + private final String dbType; + + private final String datasourceType; + + private volatile String distributedLockTable; + + private DataSource distributedLockDataSource; + + /** + * whether the distribute lock demotion + * using for 1.5.0 only and will remove in 1.6.0 + */ + @Deprecated + private volatile boolean demotion; + + /** + * Instantiates a new Log store data base dao. + */ + public DataBaseDistributedLocker() { + Configuration configuration = ConfigurationFactory.getInstance(); + + distributedLockTable = configuration.getConfig(DISTRIBUTED_LOCK_DB_TABLE); + dbType = configuration.getConfig(ConfigurationKeys.STORE_DB_TYPE); + datasourceType = configuration.getConfig(ConfigurationKeys.STORE_DB_DATASOURCE_TYPE); + + if (StringUtils.isBlank(distributedLockTable)) { + demotion = true; + ConfigurationCache.addConfigListener(DISTRIBUTED_LOCK_DB_TABLE, new ConfigurationChangeListener() { + @Override + public void onChangeEvent(ConfigurationChangeEvent event) { + String newValue = event.getNewValue(); + if (StringUtils.isNotBlank(newValue)) { + distributedLockTable = newValue; + init(); + demotion = false; + ConfigurationCache.removeConfigListener(DISTRIBUTED_LOCK_DB_TABLE, this); + } + } + }); + + LOGGER.error("The distribute lock table is not config, please create the target table and config it"); + return; + } + + init(); + } + + + @Override + public boolean acquireLock(DistributedLockDO distributedLockDO) { + if (demotion) { + return true; + } + + Connection connection = null; + boolean originalAutoCommit = false; + try { + connection = distributedLockDataSource.getConnection(); + originalAutoCommit = connection.getAutoCommit(); + connection.setAutoCommit(false); + + DistributedLockDO distributedLockDOFromDB = getDistributedLockDO(connection, distributedLockDO.getLockKey()); + if (null == distributedLockDOFromDB) { + boolean ret = insertDistribute(connection, distributedLockDO); + connection.commit(); + return ret; + } + + if (distributedLockDOFromDB.getExpireTime() >= System.currentTimeMillis()) { + LOGGER.debug("the distribute lock for key :{} is holding by :{}, acquire lock failure.", + distributedLockDO.getLockKey(), distributedLockDOFromDB.getLockValue()); + connection.commit(); + return false; + } + + boolean ret = updateDistributedLock(connection, distributedLockDO); + connection.commit(); + + return ret; + } catch (SQLException ex) { + LOGGER.error("execute acquire lock failure, key is: {}", distributedLockDO.getLockKey(), ex); + try { + if (connection != null) { + connection.rollback(); + } + } catch (SQLException e) { + LOGGER.warn("rollback fail because of {}", e.getMessage(), e); + } + return false; + } finally { + try { + if (originalAutoCommit) { + connection.setAutoCommit(true); + } + IOUtil.close(connection); + } catch (SQLException ignore) { } + } + } + + @Override + public boolean releaseLock(DistributedLockDO distributedLockDO) { + if (demotion) { + return true; + } + + Connection connection = null; + boolean originalAutoCommit = false; + try { + connection = distributedLockDataSource.getConnection(); + originalAutoCommit = connection.getAutoCommit(); + connection.setAutoCommit(false); + + DistributedLockDO distributedLockDOFromDB = getDistributedLockDO(connection, distributedLockDO.getLockKey()); + if (null == distributedLockDOFromDB) { + throw new ShouldNeverHappenException("distributedLockDO would not be null when release distribute lock"); + } + + if (distributedLockDOFromDB.getExpireTime() >= System.currentTimeMillis() + && !Objects.equals(distributedLockDOFromDB.getLockValue(), distributedLockDO.getLockValue())) { + LOGGER.debug("the distribute lock for key :{} is holding by :{}, skip the release lock.", + distributedLockDO.getLockKey(), distributedLockDOFromDB.getLockValue()); + connection.commit(); + return true; + } + + distributedLockDO.setLockValue(StringUtils.SPACE); + distributedLockDO.setExpireTime(0L); + boolean ret = updateDistributedLock(connection, distributedLockDO); + + connection.commit(); + return ret; + } catch (SQLException ex) { + LOGGER.error("execute release lock failure, key is: {}", distributedLockDO.getLockKey(), ex); + + try { + if (connection != null) { + connection.rollback(); + } + } catch (SQLException e) { + LOGGER.warn("rollback fail because of {}", e.getMessage(), e); + } + return false; + } finally { + try { + if (originalAutoCommit) { + connection.setAutoCommit(true); + } + IOUtil.close(connection); + } catch (SQLException ignore) { } + } + } + + protected DistributedLockDO getDistributedLockDO(Connection connection, String key) throws SQLException { + try (PreparedStatement pst = connection.prepareStatement(DistributedLockSqlFactory.getDistributedLogStoreSql(dbType) + .getSelectDistributeForUpdateSql(distributedLockTable))) { + + pst.setString(1, key); + ResultSet resultSet = pst.executeQuery(); + + if (resultSet.next()) { + DistributedLockDO distributedLock = new DistributedLockDO(); + distributedLock.setExpireTime(resultSet.getLong(ServerTableColumnsName.DISTRIBUTED_LOCK_EXPIRE)); + distributedLock.setLockValue(resultSet.getString(ServerTableColumnsName.DISTRIBUTED_LOCK_VALUE)); + distributedLock.setLockKey(key); + return distributedLock; + } + return null; + } + } + + protected boolean insertDistribute(Connection connection, DistributedLockDO distributedLockDO) throws SQLException { + try (PreparedStatement insertPst = connection.prepareStatement(DistributedLockSqlFactory.getDistributedLogStoreSql(dbType) + .getInsertSql(distributedLockTable))) { + insertPst.setString(1, distributedLockDO.getLockKey()); + insertPst.setString(2, distributedLockDO.getLockValue()); + if (distributedLockDO.getExpireTime() > 0) { + distributedLockDO.setExpireTime(distributedLockDO.getExpireTime() + System.currentTimeMillis()); + } + insertPst.setLong(3, distributedLockDO.getExpireTime()); + return insertPst.executeUpdate() > 0; + } + } + + protected boolean updateDistributedLock(Connection connection, DistributedLockDO distributedLockDO) throws SQLException { + try (PreparedStatement updatePst = connection.prepareStatement(DistributedLockSqlFactory.getDistributedLogStoreSql(dbType) + .getUpdateSql(distributedLockTable))) { + updatePst.setString(1, distributedLockDO.getLockValue()); + if (distributedLockDO.getExpireTime() > 0) { + distributedLockDO.setExpireTime(distributedLockDO.getExpireTime() + System.currentTimeMillis()); + } + updatePst.setLong(2, distributedLockDO.getExpireTime()); + updatePst.setString(3, distributedLockDO.getLockKey()); + return updatePst.executeUpdate() > 0; + } + } + + private void init() { + this.distributedLockDataSource = EnhancedServiceLoader.load(DataSourceProvider.class, datasourceType).provide(); + } + +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/lock/DataBaseLockManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/lock/DataBaseLockManager.java index ea2f88f09..f73df2f9d 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/lock/DataBaseLockManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/lock/DataBaseLockManager.java @@ -15,14 +15,9 @@ */ package io.seata.server.storage.db.lock; -import java.util.List; -import java.util.stream.Collectors; -import javax.sql.DataSource; - import io.seata.common.executor.Initialize; import io.seata.common.loader.EnhancedServiceLoader; import io.seata.common.loader.LoadLevel; -import io.seata.common.util.CollectionUtils; import io.seata.config.ConfigurationFactory; import io.seata.core.constants.ConfigurationKeys; import io.seata.core.exception.TransactionException; @@ -32,6 +27,8 @@ import io.seata.server.lock.AbstractLockManager; import io.seata.server.session.BranchSession; import io.seata.server.session.GlobalSession; +import javax.sql.DataSource; + /** * The type db lock manager. * @@ -40,51 +37,42 @@ import io.seata.server.session.GlobalSession; @LoadLevel(name = "db") public class DataBaseLockManager extends AbstractLockManager implements Initialize { - /** - * The locker. - */ - private Locker locker; + /** + * 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 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 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 Locker getLocker(BranchSession branchSession) { + return locker; + } - @Override - public boolean releaseGlobalSessionLock(GlobalSession globalSession) throws TransactionException { - List branchSessions = globalSession.getBranchSessions(); - if (CollectionUtils.isEmpty(branchSessions)) { - return true; - } - List branchIds = branchSessions.stream().map(BranchSession::getBranchId).collect(Collectors.toList()); - try { - return getLocker().releaseLock(globalSession.getXid(), branchIds); - } - catch (Exception t) { - LOGGER.error("unLock globalSession error, xid:{} branchIds:{}", globalSession.getXid(), - CollectionUtils.toString(branchIds), 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; + } + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/lock/DataBaseLocker.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/lock/DataBaseLocker.java index b0816d78d..8355bc039 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/lock/DataBaseLocker.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/lock/DataBaseLocker.java @@ -15,16 +15,17 @@ */ package io.seata.server.storage.db.lock; -import java.util.List; -import javax.sql.DataSource; - import io.seata.common.exception.DataAccessException; import io.seata.common.exception.StoreException; import io.seata.common.util.CollectionUtils; import io.seata.core.lock.AbstractLocker; import io.seata.core.lock.RowLock; +import io.seata.core.model.LockStatus; import io.seata.core.store.LockStore; +import javax.sql.DataSource; +import java.util.List; + /** * The type Data base locker. * @@ -32,115 +33,112 @@ import io.seata.core.store.LockStore; */ public class DataBaseLocker extends AbstractLocker { - private LockStore lockStore; + private LockStore lockStore; - /** - * Instantiates a new Data base locker. - */ - public DataBaseLocker() { - } + /** + * 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); - } + /** + * 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 locks) { - if (CollectionUtils.isEmpty(locks)) { - // no lock - return true; - } - try { - return lockStore.acquireLock(convertToLockDO(locks)); - } - catch (StoreException e) { - throw e; - } - catch (Exception t) { - LOGGER.error("AcquireLock error, locks:{}", CollectionUtils.toString(locks), t); - return false; - } - } + @Override + public boolean acquireLock(List locks) { + return acquireLock(locks, true, false); + } - @Override - public boolean releaseLock(List 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 acquireLock(List 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(String xid, Long branchId) { - try { - return lockStore.unLock(xid, 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(List 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, List branchIds) { - if (CollectionUtils.isEmpty(branchIds)) { - // no lock - return true; - } - try { - return lockStore.unLock(xid, branchIds); - } - catch (StoreException e) { - throw e; - } - catch (Exception t) { - LOGGER.error("unLock by branchIds error, xid {}, branchIds:{}", xid, CollectionUtils.toString(branchIds), - 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 isLockable(List 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 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; + } + } - /** - * Sets lock store. - * @param lockStore the lock store - */ - public void setLockStore(LockStore lockStore) { - this.lockStore = lockStore; - } + @Override + public boolean isLockable(List 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; + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/lock/LockStoreDataBaseDAO.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/lock/LockStoreDataBaseDAO.java index bbe9fd29e..21f5fa25b 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/lock/LockStoreDataBaseDAO.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/lock/LockStoreDataBaseDAO.java @@ -15,18 +15,6 @@ */ package io.seata.server.storage.db.lock; -import javax.sql.DataSource; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.StringJoiner; -import java.util.stream.Collectors; - import io.seata.common.exception.DataAccessException; import io.seata.common.exception.StoreException; import io.seata.common.util.CollectionUtils; @@ -37,13 +25,24 @@ import io.seata.config.Configuration; import io.seata.config.ConfigurationFactory; import io.seata.core.constants.ConfigurationKeys; import io.seata.core.constants.ServerTableColumnsName; +import io.seata.core.exception.BranchTransactionException; +import io.seata.core.model.LockStatus; import io.seata.core.store.LockDO; import io.seata.core.store.LockStore; import io.seata.core.store.db.sql.lock.LockStoreSqlFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.sql.DataSource; +import java.sql.*; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + import static io.seata.common.DefaultValues.DEFAULT_LOCK_DB_TABLE; +import static io.seata.core.exception.TransactionExceptionCode.LockKeyConflictFailFast; /** * The type Data base lock store. @@ -52,386 +51,387 @@ import static io.seata.common.DefaultValues.DEFAULT_LOCK_DB_TABLE; */ public class LockStoreDataBaseDAO implements LockStore { - private static final Logger LOGGER = LoggerFactory.getLogger(LockStoreDataBaseDAO.class); + private static final Logger LOGGER = LoggerFactory.getLogger(LockStoreDataBaseDAO.class); - /** - * The constant CONFIG. - */ - protected static final Configuration CONFIG = ConfigurationFactory.getInstance(); + /** + * The constant CONFIG. + */ + protected static final Configuration CONFIG = ConfigurationFactory.getInstance(); - /** - * The Lock store data source. - */ - protected DataSource lockStoreDataSource; + /** + * The Lock store data source. + */ + protected DataSource lockStoreDataSource; - /** - * The Lock table. - */ - protected String lockTable; + /** + * The Lock table. + */ + protected String lockTable; - /** - * The Db type. - */ - protected String dbType; + /** + * 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."); - } - } + /** + * 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(LockDO lockDO) { + return acquireLock(Collections.singletonList(lockDO)); + } - @Override - public boolean acquireLock(List lockDOs) { - Connection conn = null; - PreparedStatement ps = null; - ResultSet rs = null; - Set 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); - } - // check lock - StringJoiner sj = new StringJoiner(","); - for (int i = 0; i < lockDOs.size(); i++) { - sj.add("?"); - } - boolean canLock = true; - // query - String checkLockSQL = LockStoreSqlFactory.getLogStoreSql(dbType).getCheckLockableSql(lockTable, - sj.toString()); - ps = conn.prepareStatement(checkLockSQL); - for (int i = 0; i < lockDOs.size(); i++) { - ps.setString(i + 1, lockDOs.get(i).getRowKey()); - } - rs = ps.executeQuery(); - String currentXID = lockDOs.get(0).getXid(); - while (rs.next()) { - String dbXID = rs.getString(ServerTableColumnsName.LOCK_TABLE_XID); - if (!StringUtils.equals(dbXID, currentXID)) { - if (LOGGER.isInfoEnabled()) { - String dbPk = rs.getString(ServerTableColumnsName.LOCK_TABLE_PK); - String dbTableName = rs.getString(ServerTableColumnsName.LOCK_TABLE_TABLE_NAME); - Long dbBranchId = rs.getLong(ServerTableColumnsName.LOCK_TABLE_BRANCH_ID); - LOGGER.info("Global lock on [{}:{}] is holding by xid {} branchId {}", dbTableName, dbPk, dbXID, - dbBranchId); - } - canLock &= false; - break; - } - dbExistedRowKeys.add(rs.getString(ServerTableColumnsName.LOCK_TABLE_ROW_KEY)); - } + @Override + public boolean acquireLock(List lockDOs) { + return acquireLock(lockDOs, true, false); + } - if (!canLock) { - conn.rollback(); - return false; - } - List unrepeatedLockDOs = null; - if (CollectionUtils.isNotEmpty(dbExistedRowKeys)) { - unrepeatedLockDOs = lockDOs.stream().filter(lockDO -> !dbExistedRowKeys.contains(lockDO.getRowKey())) - .collect(Collectors.toList()); - } - else { - unrepeatedLockDOs = lockDOs; - } - if (CollectionUtils.isEmpty(unrepeatedLockDOs)) { - conn.rollback(); - return true; - } - // lock - if (unrepeatedLockDOs.size() == 1) { - LockDO lockDO = unrepeatedLockDOs.get(0); - 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 acquireLock(List lockDOs, boolean autoCommit, boolean skipCheckLock) { + Connection conn = null; + PreparedStatement ps = null; + ResultSet rs = null; + Set 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 unrepeatedLockDOs = lockDOs; - @Override - public boolean unLock(LockDO lockDO) { - return unLock(Collections.singletonList(lockDO)); - } + //check lock + if (!skipCheckLock) { - @Override - public boolean unLock(List lockDOs) { - Connection conn = null; - PreparedStatement ps = null; - try { - conn = lockStoreDataSource.getConnection(); - conn.setAutoCommit(true); + 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; + } - StringJoiner sj = new StringJoiner(","); - for (int i = 0; i < lockDOs.size(); i++) { - sj.add("?"); - } - // batch release lock - String batchDeleteSQL = LockStoreSqlFactory.getLogStoreSql(dbType).getBatchDeleteLockSql(lockTable, - sj.toString()); - 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; - } + 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; + } + } - @Override - public boolean unLock(String xid, Long branchId) { - Connection conn = null; - PreparedStatement ps = null; - try { - conn = lockStoreDataSource.getConnection(); - conn.setAutoCommit(true); - // batch release lock by branch - String batchDeleteSQL = LockStoreSqlFactory.getLogStoreSql(dbType).getBatchDeleteLockSqlByBranch(lockTable); - ps = conn.prepareStatement(batchDeleteSQL); - ps.setString(1, xid); - ps.setLong(2, branchId); - ps.executeUpdate(); - } - catch (SQLException e) { - throw new StoreException(e); - } - finally { - IOUtil.close(ps, conn); - } - 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(String xid, List branchIds) { - Connection conn = null; - PreparedStatement ps = null; - try { - conn = lockStoreDataSource.getConnection(); - conn.setAutoCommit(true); - StringJoiner sj = new StringJoiner(","); - branchIds.forEach(branchId -> sj.add("?")); - // batch release lock by branch list - String batchDeleteSQL = LockStoreSqlFactory.getLogStoreSql(dbType).getBatchDeleteLockSqlByBranchs(lockTable, - sj.toString()); - ps = conn.prepareStatement(batchDeleteSQL); - ps.setString(1, xid); - for (int i = 0; i < branchIds.size(); i++) { - ps.setLong(i + 2, branchIds.get(i)); - } - ps.executeUpdate(); - } - catch (SQLException e) { - throw new StoreException(e); - } - finally { - IOUtil.close(ps, conn); - } - return true; - } + @Override + public boolean unLock(LockDO lockDO) { + return unLock(Collections.singletonList(lockDO)); + } - @Override - public boolean isLockable(List 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 boolean unLock(List lockDOs) { + Connection conn = null; + PreparedStatement ps = null; + try { + conn = lockStoreDataSource.getConnection(); + conn.setAutoCommit(true); - /** - * 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()); - return ps.executeUpdate() > 0; - } - catch (SQLException e) { - throw new StoreException(e); - } - finally { - IOUtil.close(ps); - } - } + //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; + } - /** - * Do acquire lock boolean. - * @param conn the conn - * @param lockDOs the lock do list - * @return the boolean - */ - protected boolean doAcquireLocks(Connection conn, List lockDOs) { - 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.addBatch(); - } - return ps.executeBatch().length == lockDOs.size(); - } - catch (SQLException e) { - LOGGER.error("Global lock batch acquire error: {}", e.getMessage(), e); - // return false,let the caller go to conn.rollabck() - return false; - } - finally { - IOUtil.close(ps); - } - } + @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; + } - /** - * Check lock boolean. - * @param conn the conn - * @param lockDOs the lock do - * @return the boolean - */ - protected boolean checkLockable(Connection conn, List lockDOs) { - PreparedStatement ps = null; - ResultSet rs = null; - try { - StringJoiner sj = new StringJoiner(","); - for (int i = 0; i < lockDOs.size(); i++) { - sj.add("?"); - } + @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; + } - // query - String checkLockSQL = LockStoreSqlFactory.getLogStoreSql(dbType).getCheckLockableSql(lockTable, - sj.toString()); - ps = conn.prepareStatement(checkLockSQL); - for (int i = 0; i < lockDOs.size(); i++) { - ps.setString(i + 1, lockDOs.get(i).getRowKey()); - } - rs = ps.executeQuery(); - 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); - } - } + @Override + public boolean isLockable(List 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); + } + } - /** - * Sets lock table. - * @param lockTable the lock table - */ - public void setLockTable(String lockTable) { - this.lockTable = lockTable; - } + @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); + } + } - /** - * Sets db type. - * @param dbType the db type - */ - public void setDbType(String dbType) { - this.dbType = dbType; - } + /** + * 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); + } + } - /** - * Sets log store data source. - * @param lockStoreDataSource the log store data source - */ - public void setLogStoreDataSource(DataSource lockStoreDataSource) { - this.lockStoreDataSource = lockStoreDataSource; - } + /** + * Do acquire lock boolean. + * + * @param conn the conn + * @param lockDOs the lock do list + * @return the boolean + */ + protected boolean doAcquireLocks(Connection conn, List 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 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; + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/session/DataBaseSessionManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/session/DataBaseSessionManager.java index 95c62077b..f93e3a9e9 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/session/DataBaseSessionManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/session/DataBaseSessionManager.java @@ -15,185 +15,176 @@ */ package io.seata.server.storage.db.session; -import java.util.Collection; -import java.util.List; - import io.seata.common.exception.StoreException; import io.seata.common.executor.Initialize; import io.seata.common.loader.LoadLevel; +import io.seata.common.loader.Scope; import io.seata.common.util.StringUtils; import io.seata.core.exception.TransactionException; import io.seata.core.model.BranchStatus; import io.seata.core.model.GlobalStatus; -import io.seata.server.session.AbstractSessionManager; -import io.seata.server.session.BranchSession; -import io.seata.server.session.GlobalSession; -import io.seata.server.session.SessionCondition; -import io.seata.server.session.SessionHolder; +import io.seata.server.session.*; import io.seata.server.storage.db.store.DataBaseTransactionStoreManager; import io.seata.server.store.TransactionStoreManager.LogOperation; -import io.seata.common.loader.Scope; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Collection; +import java.util.List; + /** * The Data base session manager. * * @author zhangsen */ @LoadLevel(name = "db", scope = Scope.PROTOTYPE) -public class DataBaseSessionManager extends AbstractSessionManager implements Initialize { +public class DataBaseSessionManager extends AbstractSessionManager + implements Initialize { - /** - * The constant LOGGER. - */ - protected static final Logger LOGGER = LoggerFactory.getLogger(DataBaseSessionManager.class); + /** + * The constant LOGGER. + */ + protected static final Logger LOGGER = LoggerFactory.getLogger(DataBaseSessionManager.class); - /** - * The Task name. - */ - protected String taskName; + /** + * The Task name. + */ + protected String taskName; - /** - * Instantiates a new Data base session manager. - */ - public DataBaseSessionManager() { - super(); - } + /** + * 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; - } + /** + * 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 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 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."); - } - } + @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 - */ - @Override - public void removeGlobalSession(GlobalSession session) throws TransactionException { - boolean ret = transactionStoreManager.writeSession(LogOperation.GLOBAL_REMOVE, session); - if (!ret) { - throw new StoreException("removeGlobalSession 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 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 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 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) { + return this.findGlobalSession(xid, true); + } - @Override - public GlobalSession findGlobalSession(String xid, boolean withBranchSessions) { - return transactionStoreManager.readSession(xid, withBranchSessions); - } + @Override + public GlobalSession findGlobalSession(String xid, boolean withBranchSessions) { + return transactionStoreManager.readSession(xid, withBranchSessions); + } - @Override - public Collection 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(new GlobalStatus[] { GlobalStatus.CommitRetrying, GlobalStatus.Committing })); - } - else if (SessionHolder.RETRY_ROLLBACKING_SESSION_MANAGER_NAME.equalsIgnoreCase(taskName)) { - return findGlobalSessions(new SessionCondition(new GlobalStatus[] { GlobalStatus.RollbackRetrying, - GlobalStatus.Rollbacking, GlobalStatus.TimeoutRollbacking, GlobalStatus.TimeoutRollbackRetrying })); - } - else { - // all data - return findGlobalSessions(new SessionCondition(new GlobalStatus[] { GlobalStatus.UnKnown, - GlobalStatus.Begin, GlobalStatus.Committing, GlobalStatus.CommitRetrying, GlobalStatus.Rollbacking, - GlobalStatus.RollbackRetrying, GlobalStatus.TimeoutRollbacking, - GlobalStatus.TimeoutRollbackRetrying, GlobalStatus.AsyncCommitting })); - } - } + @Override + public Collection 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 findGlobalSessions(SessionCondition condition) { - // nothing need to do - return transactionStoreManager.readSession(condition); - } - - @Override - public T lockAndExecute(GlobalSession globalSession, GlobalSession.LockCallable lockCallable) - throws TransactionException { - return lockCallable.call(); - } + @Override + public List findGlobalSessions(SessionCondition condition) { + // nothing need to do + return transactionStoreManager.readSession(condition); + } + @Override + public T lockAndExecute(GlobalSession globalSession, GlobalSession.LockCallable lockCallable) + throws TransactionException { + return lockCallable.call(); + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/store/DataBaseTransactionStoreManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/store/DataBaseTransactionStoreManager.java index bb7b49bd2..e6eb83e69 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/store/DataBaseTransactionStoreManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/store/DataBaseTransactionStoreManager.java @@ -15,13 +15,6 @@ */ package io.seata.server.storage.db.store; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import javax.sql.DataSource; - import io.seata.common.exception.StoreException; import io.seata.common.loader.EnhancedServiceLoader; import io.seata.common.util.CollectionUtils; @@ -36,10 +29,16 @@ import io.seata.core.store.LogStore; import io.seata.core.store.db.DataSourceProvider; import io.seata.server.session.GlobalSession; import io.seata.server.session.SessionCondition; +import io.seata.server.storage.SessionConverter; import io.seata.server.store.AbstractTransactionStoreManager; import io.seata.server.store.SessionStorable; import io.seata.server.store.TransactionStoreManager; -import io.seata.server.storage.SessionConverter; + +import javax.sql.DataSource; +import java.util.*; +import java.util.stream.Collectors; + +import static io.seata.common.DefaultValues.DEFAULT_QUERY_LIMIT; /** * The type Database transaction store manager. @@ -47,202 +46,211 @@ import io.seata.server.storage.SessionConverter; * @author zhangsen */ public class DataBaseTransactionStoreManager extends AbstractTransactionStoreManager - implements TransactionStoreManager { + implements TransactionStoreManager { - private static volatile DataBaseTransactionStoreManager instance; + private static volatile DataBaseTransactionStoreManager instance; - /** - * The constant CONFIG. - */ - protected static final Configuration CONFIG = ConfigurationFactory.getInstance(); + /** + * The constant CONFIG. + */ + protected static final Configuration CONFIG = ConfigurationFactory.getInstance(); - /** - * The constant DEFAULT_LOG_QUERY_LIMIT. - */ - protected static final int DEFAULT_LOG_QUERY_LIMIT = 100; + /** + * The Log store. + */ + protected LogStore logStore; - /** - * The Log store. - */ - protected LogStore logStore; + /** + * The Log query limit. + */ + protected int logQueryLimit; - /** - * 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; + } - /** - * 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); + } - /** - * Instantiates a new Database transaction store manager. - */ - private DataBaseTransactionStoreManager() { - logQueryLimit = CONFIG.getInt(ConfigurationKeys.STORE_DB_LOG_QUERY_LIMIT, DEFAULT_LOG_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()); + } + } - @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 branchTransactionDOs = logStore.queryBranchTransactionDO( + globalTransactionDO.getXid()); + return getGlobalSession(globalTransactionDO, branchTransactionDOs); + } - /** - * 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 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 - * @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 branchTransactionDOs = null; + //reduce rpc with db when branchRegister and getGlobalStatus + if (withBranchSessions) { + branchTransactionDOs = logStore.queryBranchTransactionDO(globalTransactionDO.getXid()); + } + return getGlobalSession(globalTransactionDO, branchTransactionDOs); + } - /** - * 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 branchTransactionDOs = null; - // reduce rpc with db when branchRegister and getGlobalStatus - if (withBranchSessions) { - branchTransactionDOs = logStore.queryBranchTransactionDO(globalTransactionDO.getXid()); - } - return getGlobalSession(globalTransactionDO, branchTransactionDOs); - } + @Override + public List readSortByTimeoutBeginSessions(boolean withBranchSessions) { + return readSession(new GlobalStatus[] {GlobalStatus.Begin}, withBranchSessions); + } - /** - * Read session list. - * @param statuses the statuses - * @return the list - */ - public List readSession(GlobalStatus[] statuses) { - int[] states = new int[statuses.length]; - for (int i = 0; i < statuses.length; i++) { - states[i] = statuses[i].getCode(); - } - // global transaction - List globalTransactionDOs = logStore.queryGlobalTransactionDO(states, logQueryLimit); - if (CollectionUtils.isEmpty(globalTransactionDOs)) { - return null; - } - List xids = globalTransactionDOs.stream().map(GlobalTransactionDO::getXid).collect(Collectors.toList()); - List branchTransactionDOs = logStore.queryBranchTransactionDO(xids); - Map> branchTransactionDOsMap = branchTransactionDOs.stream() - .collect(Collectors.groupingBy(BranchTransactionDO::getXid, LinkedHashMap::new, Collectors.toList())); - return globalTransactionDOs.stream().map(globalTransactionDO -> getGlobalSession(globalTransactionDO, - branchTransactionDOsMap.get(globalTransactionDO.getXid()))).collect(Collectors.toList()); - } + /** + * Read session list. + * + * @param statuses the statuses + * @return the list + */ + @Override + public List readSession(GlobalStatus[] statuses, boolean withBranchSessions) { + int[] states = new int[statuses.length]; + for (int i = 0; i < statuses.length; i++) { + states[i] = statuses[i].getCode(); + } + //global transaction + List globalTransactionDOs = logStore.queryGlobalTransactionDO(states, logQueryLimit); + Map> branchTransactionDOsMap = Collections.emptyMap(); + if (CollectionUtils.isNotEmpty(globalTransactionDOs)) { + List xids = + globalTransactionDOs.stream().map(GlobalTransactionDO::getXid).collect(Collectors.toList()); + if (withBranchSessions) { + List branchTransactionDOs = logStore.queryBranchTransactionDO(xids); + branchTransactionDOsMap = branchTransactionDOs.stream().collect( + Collectors.groupingBy(BranchTransactionDO::getXid, LinkedHashMap::new, Collectors.toList())); + } + } + Map> finalBranchTransactionDOsMap = branchTransactionDOsMap; + return globalTransactionDOs.stream() + .map(globalTransactionDO -> getGlobalSession(globalTransactionDO, + finalBranchTransactionDOsMap.get(globalTransactionDO.getXid()), withBranchSessions)) + .collect(Collectors.toList()); + } - @Override - public List readSession(SessionCondition sessionCondition) { - if (StringUtils.isNotBlank(sessionCondition.getXid())) { - GlobalSession globalSession = readSession(sessionCondition.getXid()); - if (globalSession != null) { - List globalSessions = new ArrayList<>(); - globalSessions.add(globalSession); - return globalSessions; - } - } - else if (sessionCondition.getTransactionId() != null) { - GlobalSession globalSession = readSession(sessionCondition.getTransactionId()); - if (globalSession != null) { - List globalSessions = new ArrayList<>(); - globalSessions.add(globalSession); - return globalSessions; - } - } - else if (CollectionUtils.isNotEmpty(sessionCondition.getStatuses())) { - return readSession(sessionCondition.getStatuses()); - } - return null; - } + @Override + public List readSession(SessionCondition sessionCondition) { + if (StringUtils.isNotBlank(sessionCondition.getXid())) { + GlobalSession globalSession = readSession(sessionCondition.getXid()); + if (globalSession != null) { + List globalSessions = new ArrayList<>(); + globalSessions.add(globalSession); + return globalSessions; + } + } else if (sessionCondition.getTransactionId() != null) { + GlobalSession globalSession = readSession(sessionCondition.getTransactionId()); + if (globalSession != null) { + List 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 branchTransactionDOs) { - GlobalSession globalSession = SessionConverter.convertGlobalSession(globalTransactionDO); - // branch transactions - if (CollectionUtils.isNotEmpty(branchTransactionDOs)) { - for (BranchTransactionDO branchTransactionDO : branchTransactionDOs) { - globalSession.add(SessionConverter.convertBranchSession(branchTransactionDO)); - } - } - return globalSession; - } + private GlobalSession getGlobalSession(GlobalTransactionDO globalTransactionDO, + List branchTransactionDOs) { + return getGlobalSession(globalTransactionDO, branchTransactionDOs, true); + } - /** - * Sets log store. - * @param logStore the log store - */ - public void setLogStore(LogStore logStore) { - this.logStore = logStore; - } + private GlobalSession getGlobalSession(GlobalTransactionDO globalTransactionDO, + List branchTransactionDOs, boolean withBranchSessions) { + GlobalSession globalSession = SessionConverter.convertGlobalSession(globalTransactionDO, !withBranchSessions); + // branch transactions + if (CollectionUtils.isNotEmpty(branchTransactionDOs)) { + for (BranchTransactionDO branchTransactionDO : branchTransactionDOs) { + globalSession.add(SessionConverter.convertBranchSession(branchTransactionDO)); + } + } + return globalSession; + } - /** - * Sets log query limit. - * @param logQueryLimit the log query limit - */ - public void setLogQueryLimit(int logQueryLimit) { - this.logQueryLimit = logQueryLimit; - } + /** + * 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; + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/store/LogStoreDataBaseDAO.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/store/LogStoreDataBaseDAO.java index bc613e982..94baf8e39 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/store/LogStoreDataBaseDAO.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/db/store/LogStoreDataBaseDAO.java @@ -15,15 +15,6 @@ */ package io.seata.server.storage.db.store; -import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; -import javax.sql.DataSource; - import io.seata.common.exception.DataAccessException; import io.seata.common.exception.StoreException; import io.seata.common.util.IOUtil; @@ -39,6 +30,11 @@ import io.seata.core.store.db.sql.log.LogStoreSqlsFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.sql.DataSource; +import java.sql.*; +import java.util.ArrayList; +import java.util.List; + import static io.seata.common.DefaultValues.DEFAULT_STORE_DB_BRANCH_TABLE; import static io.seata.common.DefaultValues.DEFAULT_STORE_DB_GLOBAL_TABLE; @@ -49,576 +45,557 @@ import static io.seata.common.DefaultValues.DEFAULT_STORE_DB_GLOBAL_TABLE; */ public class LogStoreDataBaseDAO implements LogStore { - private static final Logger LOGGER = LoggerFactory.getLogger(LogStoreDataBaseDAO.class); + 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 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 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 constant CONFIG. - */ - protected static final Configuration CONFIG = ConfigurationFactory.getInstance(); + /** + * The Log store data source. + */ + protected DataSource logStoreDataSource = null; - /** - * The Log store data source. - */ - protected DataSource logStoreDataSource = null; + /** + * The Global table. + */ + protected String globalTable; - /** - * The Global table. - */ - protected String globalTable; + /** + * The Branch table. + */ + protected String branchTable; - /** - * The Branch table. - */ - protected String branchTable; + private String dbType; - private String dbType; + private int transactionNameColumnSize = TRANSACTION_NAME_DEFAULT_SIZE; - 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(); + } - /** - * 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(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 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 queryGlobalTransactionDO(int[] statuses, int limit) { + List ret = new ArrayList<>(); + Connection conn = null; + PreparedStatement ps = null; + ResultSet rs = null; + try { + conn = logStoreDataSource.getConnection(); + conn.setAutoCommit(true); - @Override - public List queryGlobalTransactionDO(int[] statuses, int limit) { - List 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 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); + } + } - 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 insertGlobalTransactionDO(GlobalTransactionDO globalTransactionDO) { - String sql = LogStoreSqlsFactory.getLogStoreSqls(dbType).getInsertGlobalTransactionSQL(globalTable); - Connection conn = null; - PreparedStatement ps = null; - try { - conn = logStoreDataSource.getConnection(); - conn.setAutoCommit(true); - ps = conn.prepareStatement(sql); - ps.setString(1, globalTransactionDO.getXid()); - ps.setLong(2, globalTransactionDO.getTransactionId()); - ps.setInt(3, globalTransactionDO.getStatus()); - ps.setString(4, globalTransactionDO.getApplicationId()); - ps.setString(5, globalTransactionDO.getTransactionServiceGroup()); - String transactionName = globalTransactionDO.getTransactionName(); - transactionName = transactionName.length() > transactionNameColumnSize - ? transactionName.substring(0, transactionNameColumnSize) : transactionName; - ps.setString(6, transactionName); - ps.setInt(7, globalTransactionDO.getTimeout()); - ps.setLong(8, globalTransactionDO.getBeginTime()); - ps.setString(9, globalTransactionDO.getApplicationData()); - 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 updateGlobalTransactionDO(GlobalTransactionDO globalTransactionDO) { - String sql = LogStoreSqlsFactory.getLogStoreSqls(dbType).getUpdateGlobalTransactionStatusSQL(globalTable); - Connection conn = null; - PreparedStatement ps = null; - try { - conn = logStoreDataSource.getConnection(); - conn.setAutoCommit(true); - ps = conn.prepareStatement(sql); - ps.setInt(1, globalTransactionDO.getStatus()); - ps.setString(2, 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 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 queryBranchTransactionDO(String xid) { + List 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); - @Override - public List queryBranchTransactionDO(String xid) { - List 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); - 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); + } + } - 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 queryBranchTransactionDO(List xids) { + int length = xids.size(); + List 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 List queryBranchTransactionDO(List xids) { - int length = xids.size(); - List 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 insertBranchTransactionDO(BranchTransactionDO branchTransactionDO) { - String sql = LogStoreSqlsFactory.getLogStoreSqls(dbType).getInsertBranchTransactionSQL(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.getTransactionId()); - ps.setLong(3, branchTransactionDO.getBranchId()); - ps.setString(4, branchTransactionDO.getResourceGroupId()); - ps.setString(5, branchTransactionDO.getResourceId()); - ps.setString(6, branchTransactionDO.getBranchType()); - ps.setInt(7, branchTransactionDO.getStatus()); - ps.setString(8, branchTransactionDO.getClientId()); - ps.setString(9, branchTransactionDO.getApplicationData()); - 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 updateBranchTransactionDO(BranchTransactionDO branchTransactionDO) { - String sql = LogStoreSqlsFactory.getLogStoreSqls(dbType).getUpdateBranchTransactionStatusSQL(branchTable); - Connection conn = null; - PreparedStatement ps = null; - try { - conn = logStoreDataSource.getConnection(); - conn.setAutoCommit(true); - ps = conn.prepareStatement(sql); - ps.setInt(1, branchTransactionDO.getStatus()); - ps.setString(2, branchTransactionDO.getXid()); - ps.setLong(3, branchTransactionDO.getBranchId()); - 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 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); + } - @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 maxBranchId > maxTransId ? 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); - private long getCurrentMaxSessionId(String sql, long high, long low) { - long max = 0; - Connection conn = null; - PreparedStatement ps = null; - ResultSet rs = null; - try { - conn = logStoreDataSource.getConnection(); - conn.setAutoCommit(true); - ps = conn.prepareStatement(sql); - ps.setLong(1, high); - ps.setLong(2, 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; + } - 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 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; + } - 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(); + } - /** - * 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; + } - /** - * 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(); + } + } - 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 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 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 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; + } - /** - * Sets db type. - * @param dbType the db type - */ - public void setDbType(String dbType) { - this.dbType = dbType; - } + public int getTransactionNameColumnSize() { + return transactionNameColumnSize; + } - public int getTransactionNameColumnSize() { - return transactionNameColumnSize; - } + /** + * column info + */ + private static class ColumnInfo { + private String columnName; + private String typeName; + private int columnSize; + private String remarks; - /** - * column info - */ - private static class ColumnInfo { + public String getColumnName() { + return columnName; + } - private String columnName; + public void setColumnName(String columnName) { + this.columnName = columnName; + } - private String typeName; + public String getTypeName() { + return typeName; + } - private int columnSize; + public void setTypeName(String typeName) { + this.typeName = typeName; + } - private String remarks; + public int getColumnSize() { + return columnSize; + } - public String getColumnName() { - return columnName; - } + public void setColumnSize(int columnSize) { + this.columnSize = columnSize; + } - 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; - } - - } + public String getRemarks() { + return remarks; + } + public void setRemarks(String remarks) { + this.remarks = remarks; + } + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/FlushDiskMode.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/FlushDiskMode.java index cab554b16..9fdb50ee2 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/FlushDiskMode.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/FlushDiskMode.java @@ -19,27 +19,25 @@ package io.seata.server.storage.file; * @author lizhao */ public enum FlushDiskMode { + /** + * sync flush disk + */ + SYNC_MODEL("sync"), + /** + * async flush disk + */ + ASYNC_MODEL("async"); - /** - * sync flush disk - */ - SYNC_MODEL("sync"), - /** - * async flush disk - */ - ASYNC_MODEL("async"); + private String modeStr; - 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; - } + FlushDiskMode(String modeStr) { + this.modeStr = modeStr; + } + public static FlushDiskMode findDiskMode(String modeStr) { + if (SYNC_MODEL.modeStr.equals(modeStr)) { + return SYNC_MODEL; + } + return ASYNC_MODEL; + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/ReloadableStore.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/ReloadableStore.java index 90e822bdc..be45305cf 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/ReloadableStore.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/ReloadableStore.java @@ -24,19 +24,22 @@ import java.util.List; */ public interface ReloadableStore { - /** - * Read write store. - * @param readSize the read size - * @param isHistory the is history - * @return the list - */ - List readWriteStore(int readSize, boolean isHistory); + /** + * Read write store. + * + * @param readSize the read size + * @param isHistory the is history + * @return the list + */ + List readWriteStore(int readSize, boolean isHistory); + + /** + * Has remaining boolean. + * + * @param isHistory the is history + * @return the boolean + */ + boolean hasRemaining(boolean isHistory); - /** - * Has remaining boolean. - * @param isHistory the is history - * @return the boolean - */ - boolean hasRemaining(boolean isHistory); } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/TransactionWriteStore.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/TransactionWriteStore.java index 315d21dac..d5e4d3e29 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/TransactionWriteStore.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/TransactionWriteStore.java @@ -15,114 +15,115 @@ */ package io.seata.server.storage.file; -import java.nio.ByteBuffer; - import io.seata.common.exception.ShouldNeverHappenException; import io.seata.server.session.BranchSession; import io.seata.server.session.GlobalSession; import io.seata.server.store.SessionStorable; import io.seata.server.store.TransactionStoreManager.LogOperation; +import java.nio.ByteBuffer; + /** * The type Transaction write store. * * @author slievrly */ public class TransactionWriteStore implements SessionStorable { + private SessionStorable sessionRequest; + private LogOperation operate; - private SessionStorable sessionRequest; + /** + * 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; + } - private LogOperation operate; + /** + * Instantiates a new Transaction write store. + */ + public TransactionWriteStore() {} - /** - * 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; - } + /** + * Gets session request. + * + * @return the session request + */ + public SessionStorable getSessionRequest() { + return sessionRequest; + } - /** - * Instantiates a new Transaction write store. - */ - public TransactionWriteStore() { - } + /** + * Sets session request. + * + * @param sessionRequest the session request + */ + public void setSessionRequest(SessionStorable sessionRequest) { + this.sessionRequest = sessionRequest; + } - /** - * Gets session request. - * @return the session request - */ - public SessionStorable getSessionRequest() { - return sessionRequest; - } + /** + * Gets operate. + * + * @return the operate + */ + public LogOperation getOperate() { + return operate; + } - /** - * Sets session request. - * @param sessionRequest the session request - */ - public void setSessionRequest(SessionStorable sessionRequest) { - this.sessionRequest = sessionRequest; - } + /** + * Sets operate. + * + * @param operate the operate + */ + public void setOperate(LogOperation operate) { + this.operate = operate; + } - /** - * Gets operate. - * @return the operate - */ - public LogOperation getOperate() { - return 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; + } - /** - * 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; - } + @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; + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/lock/FileLockManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/lock/FileLockManager.java index 3899d2bcc..72e897e73 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/lock/FileLockManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/lock/FileLockManager.java @@ -15,8 +15,6 @@ */ package io.seata.server.storage.file.lock; -import java.util.ArrayList; - import io.seata.common.loader.LoadLevel; import io.seata.core.exception.TransactionException; import io.seata.core.lock.Locker; @@ -25,6 +23,8 @@ import io.seata.server.session.BranchSession; import io.seata.server.session.GlobalSession; import org.slf4j.MDC; +import java.util.List; + import static io.seata.core.context.RootContext.MDC_KEY_BRANCH_ID; /** @@ -35,27 +35,26 @@ import static io.seata.core.context.RootContext.MDC_KEY_BRANCH_ID; @LoadLevel(name = "file") public class FileLockManager extends AbstractLockManager { - @Override - public Locker getLocker(BranchSession branchSession) { - return new FileLocker(branchSession); - } + @Override + public Locker getLocker(BranchSession branchSession) { + return new FileLocker(branchSession); + } - @Override - public boolean releaseGlobalSessionLock(GlobalSession globalSession) throws TransactionException { - ArrayList 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; - } + @Override + public boolean releaseGlobalSessionLock(GlobalSession globalSession) throws TransactionException { + List 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; + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/lock/FileLocker.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/lock/FileLocker.java index c348446e6..fe111022d 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/lock/FileLocker.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/lock/FileLocker.java @@ -15,19 +15,23 @@ */ package io.seata.server.storage.file.lock; +import io.seata.common.exception.FrameworkException; +import io.seata.common.exception.StoreException; +import io.seata.common.util.CollectionUtils; +import io.seata.core.exception.BranchTransactionException; +import io.seata.core.exception.TransactionException; +import io.seata.core.lock.AbstractLocker; +import io.seata.core.lock.RowLock; +import io.seata.core.model.LockStatus; +import io.seata.server.session.BranchSession; + import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import io.netty.util.internal.ConcurrentSet; -import io.seata.common.exception.FrameworkException; -import io.seata.common.util.CollectionUtils; -import io.seata.core.exception.TransactionException; -import io.seata.core.lock.AbstractLocker; -import io.seata.core.lock.RowLock; -import io.seata.server.session.BranchSession; +import static io.seata.core.exception.TransactionExceptionCode.LockKeyConflictFailFast; /** * The type Memory locker. @@ -36,165 +40,181 @@ import io.seata.server.session.BranchSession; */ public class FileLocker extends AbstractLocker { - private static final int BUCKET_PER_TABLE = 128; + private static final int BUCKET_PER_TABLE = 128; - private static final ConcurrentMap>> LOCK_MAP = new ConcurrentHashMap<>(); + private static final ConcurrentMap>> + LOCK_MAP = new ConcurrentHashMap<>(); - /** - * The Branch session. - */ - protected BranchSession branchSession = null; + /** + * The Branch session. + */ + protected BranchSession branchSession; - /** - * Instantiates a new Memory locker. - * @param branchSession the branch session - */ - public FileLocker(BranchSession branchSession) { - this.branchSession = branchSession; - } + /** + * Instantiates a new Memory locker. + * + * @param branchSession the branch session + */ + public FileLocker(BranchSession branchSession) { + this.branchSession = branchSession; + } - @Override - public boolean acquireLock(List rowLocks) { - if (CollectionUtils.isEmpty(rowLocks)) { - // no lock - return true; - } - String resourceId = branchSession.getResourceId(); - long transactionId = branchSession.getTransactionId(); + @Override + public boolean acquireLock(List rowLocks) { + return acquireLock(rowLocks, true, false); + } - ConcurrentMap> bucketHolder = branchSession.getLockHolder(); - ConcurrentMap> dbLockMap = CollectionUtils - .computeIfAbsent(LOCK_MAP, resourceId, key -> new ConcurrentHashMap<>()); + @Override + public boolean acquireLock(List rowLocks, boolean autoCommit, boolean skipCheckLock) { + if (CollectionUtils.isEmpty(rowLocks)) { + // no lock + return true; + } + String resourceId = branchSession.getResourceId(); + long transactionId = branchSession.getTransactionId(); - for (RowLock lock : rowLocks) { - String tableName = lock.getTableName(); - String pk = lock.getPk(); - ConcurrentMap tableLockMap = CollectionUtils.computeIfAbsent(dbLockMap, tableName, - key -> new ConcurrentHashMap<>()); + ConcurrentMap> bucketHolder = branchSession.getLockHolder(); + ConcurrentMap> dbLockMap = CollectionUtils.computeIfAbsent( + LOCK_MAP, resourceId, key -> new ConcurrentHashMap<>()); + boolean failFast = false; + boolean canLock = true; + for (RowLock lock : rowLocks) { + String tableName = lock.getTableName(); + String pk = lock.getPk(); + ConcurrentMap tableLockMap = CollectionUtils.computeIfAbsent(dbLockMap, tableName, + key -> new ConcurrentHashMap<>()); - int bucketId = pk.hashCode() % BUCKET_PER_TABLE; - BucketLockMap bucketLockMap = CollectionUtils.computeIfAbsent(tableLockMap, bucketId, - key -> new BucketLockMap()); - Long previousLockTransactionId = bucketLockMap.get().putIfAbsent(pk, transactionId); - if (previousLockTransactionId == null) { - // No existing lock, and now locked by myself - Set keysInHolder = CollectionUtils.computeIfAbsent(bucketHolder, bucketLockMap, - key -> new ConcurrentSet<>()); - keysInHolder.add(pk); - } - else if (previousLockTransactionId == transactionId) { - // Locked by me before - continue; - } - else { - LOGGER.info("Global lock on [" + tableName + ":" + pk + "] is holding by " + previousLockTransactionId); - try { - // Release all acquired locks. - branchSession.unlock(); - } - catch (TransactionException e) { - throw new FrameworkException(e); - } - return false; - } - } - return true; - } + 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 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) { - if (CollectionUtils.isEmpty(rowLock)) { - // no lock - return true; - } - ConcurrentMap> lockHolder = branchSession.getLockHolder(); - if (CollectionUtils.isEmpty(lockHolder)) { - return true; - } - for (Map.Entry> entry : lockHolder.entrySet()) { - BucketLockMap bucket = entry.getKey(); - Set keys = entry.getValue(); - for (String key : keys) { - // remove lock only if it locked by myself - bucket.get().remove(key, branchSession.getTransactionId()); - } - } - lockHolder.clear(); - return true; - } + @Override + public boolean releaseLock(List rowLock) { + if (CollectionUtils.isEmpty(rowLock)) { + //no lock + return true; + } + ConcurrentMap> lockHolder = branchSession.getLockHolder(); + if (CollectionUtils.isEmpty(lockHolder)) { + return true; + } + for (Map.Entry> entry : lockHolder.entrySet()) { + BucketLockMap bucket = entry.getKey(); + Set 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 rowLocks) { - if (CollectionUtils.isEmpty(rowLocks)) { - // no lock - return true; - } - Long transactionId = rowLocks.get(0).getTransactionId(); - String resourceId = rowLocks.get(0).getResourceId(); - ConcurrentMap> dbLockMap = LOCK_MAP.get(resourceId); - if (dbLockMap == null) { - return true; - } - for (RowLock rowLock : rowLocks) { - String tableName = rowLock.getTableName(); - String pk = rowLock.getPk(); + @Override + public boolean isLockable(List rowLocks) { + if (CollectionUtils.isEmpty(rowLocks)) { + //no lock + return true; + } + Long transactionId = rowLocks.get(0).getTransactionId(); + String resourceId = rowLocks.get(0).getResourceId(); + ConcurrentMap> dbLockMap = LOCK_MAP.get(resourceId); + if (dbLockMap == null) { + return true; + } + for (RowLock rowLock : rowLocks) { + String tableName = rowLock.getTableName(); + String pk = rowLock.getPk(); - ConcurrentMap tableLockMap = dbLockMap.get(tableName); - if (tableLockMap == null) { - continue; - } - int bucketId = pk.hashCode() % BUCKET_PER_TABLE; - BucketLockMap bucketLockMap = tableLockMap.get(bucketId); - if (bucketLockMap == null) { - continue; - } - Long lockingTransactionId = bucketLockMap.get().get(pk); - 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; - } + ConcurrentMap 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 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 { + @Override + public void updateLockStatus(String xid, LockStatus lockStatus) { + } - private final ConcurrentHashMap bucketLockMap = new ConcurrentHashMap<>(); + @Override + public void cleanAllLocks() { + LOCK_MAP.clear(); + } - ConcurrentHashMap get() { - return bucketLockMap; - } + /** + * 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 bucketLockMap + = new ConcurrentHashMap<>(); - @Override - public int hashCode() { - return super.hashCode(); - } + ConcurrentHashMap get() { + return bucketLockMap; + } - @Override - public boolean equals(Object o) { - return super.equals(o); - } - - } + @Override + public int hashCode() { + return super.hashCode(); + } + @Override + public boolean equals(Object o) { + return super.equals(o); + } + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/session/FileSessionManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/session/FileSessionManager.java index e19dd488a..504ec495f 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/session/FileSessionManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/session/FileSessionManager.java @@ -15,30 +15,16 @@ */ package io.seata.server.storage.file.session; -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - import io.seata.common.exception.ShouldNeverHappenException; import io.seata.common.loader.LoadLevel; import io.seata.common.loader.Scope; +import io.seata.common.util.CollectionUtils; import io.seata.common.util.StringUtils; import io.seata.config.ConfigurationFactory; import io.seata.core.constants.ConfigurationKeys; import io.seata.core.exception.TransactionException; import io.seata.core.model.GlobalStatus; -import io.seata.server.session.AbstractSessionManager; -import io.seata.server.session.BranchSession; -import io.seata.server.session.GlobalSession; -import io.seata.server.session.Reloadable; -import io.seata.server.session.SessionCondition; +import io.seata.server.session.*; import io.seata.server.storage.file.ReloadableStore; import io.seata.server.storage.file.TransactionWriteStore; import io.seata.server.storage.file.store.FileTransactionStoreManager; @@ -46,6 +32,14 @@ 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. * @@ -54,285 +48,322 @@ import io.seata.server.store.TransactionStoreManager; @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, 100); + 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 sessionMap = new ConcurrentHashMap<>(); - /** - * The Session map. - */ - private Map 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; + } + }; + } + } - /** - * 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 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 void addGlobalSession(GlobalSession session) throws TransactionException { - super.addGlobalSession(session); - sessionMap.put(session.getXid(), session); - } + @Override + public GlobalSession findGlobalSession(String xid) { + return sessionMap.get(xid); + } - @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 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 void removeGlobalSession(GlobalSession session) throws TransactionException { - super.removeGlobalSession(session); - sessionMap.remove(session.getXid()); - } + @Override + public Collection allSessions() { + return sessionMap.values(); + } - @Override - public Collection allSessions() { - return sessionMap.values(); - } + @Override + public List findGlobalSessions(SessionCondition condition) { + List found = new ArrayList<>(); - @Override - public List findGlobalSessions(SessionCondition condition) { - List found = new ArrayList<>(); - for (GlobalSession globalSession : sessionMap.values()) { - if (System.currentTimeMillis() - globalSession.getBeginTime() > condition.getOverTimeAliveMills()) { - found.add(globalSession); - } - } - return found; - } + List 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; + } + } - @Override - public T lockAndExecute(GlobalSession globalSession, GlobalSession.LockCallable lockCallable) - throws TransactionException { - globalSession.lock(); - try { - return lockCallable.call(); - } - finally { - globalSession.unlock(); - } - } + 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; + } + } - private void restoreSessions() { - final Set removedGlobalBuffer = new HashSet<>(); - final Map> unhandledBranchBuffer = new HashMap<>(); + 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; + } + } - restoreSessions(true, removedGlobalBuffer, unhandledBranchBuffer); - restoreSessions(false, removedGlobalBuffer, unhandledBranchBuffer); + if (null != globalStatuses) { + if (!globalStatuses.contains(globalSession.getStatus())) { + continue; + } + } - if (!unhandledBranchBuffer.isEmpty()) { - unhandledBranchBuffer.values().forEach(unhandledBranchSessions -> { - unhandledBranchSessions.values().forEach(branchSession -> { - String xid = branchSession.getXid(); - if (removedGlobalBuffer.contains(xid)) { - return; - } + // All test pass, add to resp + found.add(globalSession); + } + return found; + } - 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()); - } - } - }); - }); - } - } + @Override + public T lockAndExecute(GlobalSession globalSession, GlobalSession.LockCallable lockCallable) + throws TransactionException { + globalSession.lock(); + try { + return lockCallable.call(); + } finally { + globalSession.unlock(); + } + } - 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 Finished: - return false; - default: - return true; - } - } + private void restoreSessions() { + final Set removedGlobalBuffer = new HashSet<>(); + final Map> unhandledBranchBuffer = new HashMap<>(); - private void restoreSessions(boolean isHistory, Set removedGlobalBuffer, - Map> unhandledBranchBuffer) { - if (!(transactionStoreManager instanceof ReloadableStore)) { - return; - } - while (((ReloadableStore) transactionStoreManager).hasRemaining(isHistory)) { - List stores = ((ReloadableStore) transactionStoreManager).readWriteStore(READ_SIZE, - isHistory); - restore(stores, removedGlobalBuffer, unhandledBranchBuffer); - } - } + restoreSessions(true, removedGlobalBuffer, unhandledBranchBuffer); + restoreSessions(false, removedGlobalBuffer, unhandledBranchBuffer); - private void restore(List stores, Set removedGlobalBuffer, - Map> 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); - } - } + 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()); + } + } + }); + }); + } + } - @Override - public void destroy() { - transactionStoreManager.shutdown(); - } + 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 Finished: + return false; + default: + return true; + } + } + + private void restoreSessions(boolean isHistory, Set removedGlobalBuffer, Map> unhandledBranchBuffer) { + if (!(transactionStoreManager instanceof ReloadableStore)) { + return; + } + while (((ReloadableStore)transactionStoreManager).hasRemaining(isHistory)) { + List stores = ((ReloadableStore)transactionStoreManager).readWriteStore(READ_SIZE, + isHistory); + restore(stores, removedGlobalBuffer, unhandledBranchBuffer); + } + } + + private void restore(List stores, Set removedGlobalBuffer, + Map> 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(); + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/store/FileTransactionStoreManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/store/FileTransactionStoreManager.java index ce4707333..6b3c1c777 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/store/FileTransactionStoreManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/file/store/FileTransactionStoreManager.java @@ -15,6 +15,24 @@ */ package io.seata.server.storage.file.store; +import io.seata.common.exception.StoreException; +import io.seata.common.thread.NamedThreadFactory; +import io.seata.common.util.CollectionUtils; +import io.seata.server.session.BranchSession; +import io.seata.server.session.GlobalSession; +import io.seata.server.session.SessionCondition; +import io.seata.server.session.SessionManager; +import io.seata.server.storage.file.FlushDiskMode; +import io.seata.server.storage.file.ReloadableStore; +import io.seata.server.storage.file.TransactionWriteStore; +import io.seata.server.store.AbstractTransactionStoreManager; +import io.seata.server.store.SessionStorable; +import io.seata.server.store.StoreConfig; +import io.seata.server.store.TransactionStoreManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; + import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; @@ -24,32 +42,10 @@ import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.ReentrantLock; -import io.seata.common.exception.StoreException; -import io.seata.common.thread.NamedThreadFactory; -import io.seata.common.util.CollectionUtils; -import io.seata.server.session.BranchSession; -import io.seata.server.session.GlobalSession; -import io.seata.server.session.SessionCondition; -import io.seata.server.session.SessionManager; -import io.seata.server.store.AbstractTransactionStoreManager; -import io.seata.server.storage.file.FlushDiskMode; -import io.seata.server.storage.file.ReloadableStore; -import io.seata.server.store.SessionStorable; -import io.seata.server.store.StoreConfig; -import io.seata.server.store.TransactionStoreManager; -import io.seata.server.storage.file.TransactionWriteStore; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.slf4j.MDC; - import static io.seata.core.context.RootContext.MDC_KEY_BRANCH_ID; /** @@ -58,629 +54,598 @@ import static io.seata.core.context.RootContext.MDC_KEY_BRANCH_ID; * @author slievrly */ public class FileTransactionStoreManager extends AbstractTransactionStoreManager - implements TransactionStoreManager, ReloadableStore { + implements TransactionStoreManager, ReloadableStore { + private static final Logger LOGGER = LoggerFactory.getLogger(FileTransactionStoreManager.class); - private static final Logger LOGGER = LoggerFactory.getLogger(FileTransactionStoreManager.class); + private static final int MAX_THREAD_WRITE = 1; - private static final int MAX_THREAD_WRITE = 1; + private ExecutorService fileWriteExecutor; - private ExecutorService fileWriteExecutor; + private volatile boolean stopping = false; - private volatile boolean stopping = false; + private static final int MAX_SHUTDOWN_RETRY = 3; - private static final int MAX_SHUTDOWN_RETRY = 3; + private static final int SHUTDOWN_CHECK_INTERVAL = 1 * 1000; - private static final int SHUTDOWN_CHECK_INTERNAL = 1 * 1000; + private static final int MAX_WRITE_RETRY = 5; - private static final int MAX_WRITE_RETRY = 5; + private static final String HIS_DATA_FILENAME_POSTFIX = ".1"; - private static final String HIS_DATA_FILENAME_POSTFIX = ".1"; + private static final AtomicLong FILE_TRX_NUM = new AtomicLong(0); - private static final AtomicLong FILE_TRX_NUM = new AtomicLong(0); + private static final AtomicLong FILE_FLUSH_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 MARK_SIZE = 4; + private static final int MAX_WAIT_TIME_MILLS = 2 * 1000; - 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_TIME_MILLS = 2 * 1000; + private static final int MAX_FLUSH_NUM = 10; - private static final int MAX_FLUSH_NUM = 10; + private static final int PER_FILE_BLOCK_SIZE = 65535 * 8; - private static final int PER_FILE_BLOCK_SIZE = 65535 * 8; + private static final long MAX_TRX_TIMEOUT_MILLS = 30 * 60 * 1000; - private static final long MAX_TRX_TIMEOUT_MILLS = 30 * 60 * 1000; + private static volatile long trxStartTimeMills = System.currentTimeMillis(); - private static volatile long trxStartTimeMills = System.currentTimeMillis(); + private File currDataFile; - private File currDataFile; + private RandomAccessFile currRaf; - private RandomAccessFile currRaf; + private FileChannel currFileChannel; - private FileChannel currFileChannel; + private long recoverCurrOffset = 0; - private long recoverCurrOffset = 0; + private long recoverHisOffset = 0; - private long recoverHisOffset = 0; + private SessionManager sessionManager; - private SessionManager sessionManager; + private String currFullFileName; - private String currFullFileName; + private String hisFullFileName; - private String hisFullFileName; + private WriteDataFileRunnable writeDataFileRunnable; - private WriteDataFileRunnable writeDataFileRunnable; + private ReentrantLock writeSessionLock = new ReentrantLock(); - private ReentrantLock writeSessionLock = new ReentrantLock(); + private volatile long lastModifiedTime; - 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(), - 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) { - writeSessionLock.lock(); - long curFileTrxNum; - 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 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 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 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_INTERNAL); - } - catch (InterruptedException ignore) { - } - } - if (retry >= MAX_SHUTDOWN_RETRY) { - fileWriteExecutor.shutdownNow(); - } - } - try { - currFileChannel.force(true); - } - catch (IOException e) { - LOGGER.error("fileChannel force error{}", e.getMessage(), e); - } - closeFile(currRaf); - } - - @Override - public List 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 = null; - RandomAccessFile raf = null; - long currentOffset = 0; - 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 parseDataFile(File file, int readSize, long currentOffset, boolean isHistory) { - List 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 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); - } - } - - } + 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(), + 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 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 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 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 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 parseDataFile(File file, int readSize, long currentOffset, boolean isHistory) { + List 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 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); + } + } + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/JedisPooledFactory.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/JedisPooledFactory.java index f7cab8a87..031dbe905 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/JedisPooledFactory.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/JedisPooledFactory.java @@ -15,130 +15,111 @@ */ package io.seata.server.storage.redis; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.seata.common.util.ConfigTools; import io.seata.common.exception.RedisException; +import io.seata.common.util.ConfigTools; import io.seata.common.util.StringUtils; import io.seata.config.Configuration; import io.seata.config.ConfigurationFactory; import io.seata.core.constants.ConfigurationKeys; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import redis.clients.jedis.*; -import redis.clients.jedis.Jedis; -import redis.clients.jedis.JedisPool; -import redis.clients.jedis.JedisPoolAbstract; -import redis.clients.jedis.JedisPoolConfig; -import redis.clients.jedis.JedisSentinelPool; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import static io.seata.common.DefaultValues.*; /** * @author funkye */ public class JedisPooledFactory { + /** + * The constant LOGGER. + */ + protected static final Logger LOGGER = LoggerFactory.getLogger(JedisPooledFactory.class); - /** - * The constant LOGGER. - */ - protected static final Logger LOGGER = LoggerFactory.getLogger(JedisPooledFactory.class); + private static volatile JedisPoolAbstract jedisPool = null; - private static volatile JedisPoolAbstract jedisPool = null; + private static final String HOST = "127.0.0.1"; - 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 PORT = 6379; + private static final int SENTINEL_HOST_NUMBER = 3; - private static final int MINCONN = 1; + private static final Configuration CONFIGURATION = ConfigurationFactory.getInstance(); - private static final int MAXCONN = 10; + /** + * 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 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; + } - private static final int MAXTOTAL = 100; - - 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) { - if (jedisPools != null && jedisPools.length > 0) { - jedisPool = 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, MINCONN)); - poolConfig.setMaxIdle(CONFIGURATION.getInt(ConfigurationKeys.STORE_REDIS_MAX_CONN, MAXCONN)); - poolConfig.setMaxTotal(CONFIGURATION.getInt(ConfigurationKeys.STORE_REDIS_MAX_TOTAL, MAXTOTAL)); - 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 sentinels = new HashSet<>(SENTINEL_HOST_NUMBER); - String[] sentinelHosts = CONFIGURATION - .getConfig(ConfigurationKeys.STORE_REDIS_SENTINEL_HOST).split(","); - Arrays.asList(sentinelHosts).forEach(sentinelHost -> sentinels.add(sentinelHost)); - jedisPool = new JedisSentinelPool(masterName, sentinels, poolConfig, 60000, password, - 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; - jedisPool = 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"); - } - } - } - } - return jedisPool; - } - - /** - * get an instance of Jedis (connection) from the connection pool - * @return jedis - */ - public static Jedis getJedisInstance() { - return getJedisPoolInstance().getResource(); - } + /** + * get an instance of Jedis (connection) from the connection pool + * + * @return jedis + */ + public static Jedis getJedisInstance() { + return getJedisPoolInstance().getResource(); + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/lock/RedisDistributedLocker.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/lock/RedisDistributedLocker.java new file mode 100644 index 000000000..ecd60cacd --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/lock/RedisDistributedLocker.java @@ -0,0 +1,85 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.storage.redis.lock; + +import io.seata.common.loader.LoadLevel; +import io.seata.common.loader.Scope; +import io.seata.core.store.DistributedLockDO; +import io.seata.core.store.DistributedLocker; +import io.seata.server.storage.redis.JedisPooledFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.Transaction; +import redis.clients.jedis.params.SetParams; + +/** + * Redis distributed lock + * @author zhongxiang.wang + */ +@LoadLevel(name = "redis", scope = Scope.SINGLETON) +public class RedisDistributedLocker implements DistributedLocker { + + protected static final Logger LOGGER = LoggerFactory.getLogger(RedisDistributedLocker.class); + private static final String SUCCESS = "OK"; + + /** + * Acquire the distributed lock + * + * @param distributedLockDO the distributed lock info + * @return the distributed lock info + */ + @Override + public boolean acquireLock(DistributedLockDO distributedLockDO) { + try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { + //Don't need retry,if can't acquire the lock,let the other get the lock + String result = jedis.set(distributedLockDO.getLockKey(), distributedLockDO.getLockValue(), SetParams.setParams().nx().px(distributedLockDO.getExpireTime())); + return SUCCESS.equalsIgnoreCase(result); + } catch (Exception ex) { + LOGGER.error("The {} acquired the {} distributed lock failed.", distributedLockDO.getLockValue(), distributedLockDO.getLockKey(), ex); + return false; + } + } + + + /** + * Release the distributed lock + * + * @param distributedLockDO the distributed lock info + * @return the boolean + */ + @Override + public boolean releaseLock(DistributedLockDO distributedLockDO) { + String lockKey = distributedLockDO.getLockKey(); + String lockValue = distributedLockDO.getLockValue(); + try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { + jedis.watch(lockKey); + //Check the value to prevent release the other's lock + if (lockValue.equals(jedis.get(lockKey))) { + Transaction multi = jedis.multi(); + multi.del(lockKey); + multi.exec(); + return true; + } + //The lock hold by others,If other one get the lock,we release lock success too as for current lockKey + jedis.unwatch(); + return true; + } catch (Exception ex) { + LOGGER.error("The {} release the {} distributed lock failed.", lockValue, lockKey, ex); + return false; + } + } +} diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/lock/RedisLockManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/lock/RedisLockManager.java index 784463c8a..067fd50d6 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/lock/RedisLockManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/lock/RedisLockManager.java @@ -15,12 +15,8 @@ */ package io.seata.server.storage.redis.lock; -import java.util.List; -import java.util.stream.Collectors; - import io.seata.common.executor.Initialize; import io.seata.common.loader.LoadLevel; -import io.seata.common.util.CollectionUtils; import io.seata.core.exception.TransactionException; import io.seata.core.lock.Locker; import io.seata.server.lock.AbstractLockManager; @@ -33,47 +29,38 @@ import io.seata.server.session.GlobalSession; @LoadLevel(name = "redis") public class RedisLockManager extends AbstractLockManager implements Initialize { - /** - * The locker. - */ - private Locker locker; + /** + * The locker. + */ + private Locker locker; - @Override - public void init() { - locker = new RedisLocker(); - } + @Override + public void init() { + locker = new RedisLocker(); + } - @Override - public Locker getLocker(BranchSession branchSession) { - return locker; - } + @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 { - List branchSessions = globalSession.getBranchSessions(); - if (CollectionUtils.isEmpty(branchSessions)) { - return true; - } - List branchIds = branchSessions.stream().map(BranchSession::getBranchId).collect(Collectors.toList()); - try { - return getLocker().releaseLock(globalSession.getXid(), branchIds); - } - catch (Exception t) { - LOGGER.error("unLock globalSession error, xid:{} branchIds:{}", globalSession.getXid(), - CollectionUtils.toString(branchIds), t); - return false; - } - } + @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; + } + } } \ No newline at end of file diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/lock/RedisLocker.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/lock/RedisLocker.java index 86343f39b..361b16243 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/lock/RedisLocker.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/lock/RedisLocker.java @@ -15,28 +15,31 @@ */ package io.seata.server.storage.redis.lock; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.StringJoiner; -import java.util.stream.Collectors; import com.google.common.collect.Lists; +import io.seata.common.exception.StoreException; +import io.seata.common.io.FileLoader; import io.seata.common.util.CollectionUtils; import io.seata.common.util.LambdaUtils; import io.seata.common.util.StringUtils; +import io.seata.core.exception.BranchTransactionException; import io.seata.core.lock.AbstractLocker; import io.seata.core.lock.RowLock; +import io.seata.core.model.LockStatus; import io.seata.core.store.LockDO; import io.seata.server.storage.redis.JedisPooledFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import redis.clients.jedis.Jedis; import redis.clients.jedis.Pipeline; -import static io.seata.common.Constants.ROW_LOCK_KEY_SPLIT_CHAR; +import java.io.*; +import java.util.*; +import java.util.stream.Collectors; +import static io.seata.common.Constants.ROW_LOCK_KEY_SPLIT_CHAR; +import static io.seata.core.constants.RedisKeyConstants.DEFAULT_REDIS_SEATA_GLOBAL_LOCK_PREFIX; +import static io.seata.core.constants.RedisKeyConstants.DEFAULT_REDIS_SEATA_ROW_LOCK_PREFIX; +import static io.seata.core.exception.TransactionExceptionCode.LockKeyConflictFailFast; /** * The redis lock store operation * @@ -45,204 +48,338 @@ import static io.seata.common.Constants.ROW_LOCK_KEY_SPLIT_CHAR; */ public class RedisLocker extends AbstractLocker { - private static final Integer SUCCEED = 1; + private static final Logger LOGGER = LoggerFactory.getLogger(RedisLocker.class); - private static final Integer FAILED = 0; + private static final Integer SUCCEED = 1; - private static final String DEFAULT_REDIS_SEATA_ROW_LOCK_PREFIX = "SEATA_ROW_LOCK_"; + private static final Integer FAILED = 0; - private static final String DEFAULT_REDIS_SEATA_GLOBAL_LOCK_PREFIX = "SEATA_GLOBAL_LOCK"; + private static final String XID = "xid"; - private static final String XID = "xid"; + private static final String TRANSACTION_ID = "transactionId"; - private static final String TRANSACTION_ID = "transactionId"; + private static final String BRANCH_ID = "branchId"; - private static final String BRANCH_ID = "branchId"; + private static final String RESOURCE_ID = "resourceId"; - private static final String RESOURCE_ID = "resourceId"; + private static final String TABLE_NAME = "tableName"; - private static final String TABLE_NAME = "tableName"; + private static final String PK = "pk"; - private static final String PK = "pk"; + private static final String STATUS = "status"; - private static final String ROW_KEY = "rowKey"; + private static final String ROW_KEY = "rowKey"; - /** - * Instantiates a new Redis locker. - */ - public RedisLocker() { - } + private static final String REDIS_LUA_FILE_NAME = "lua/redislocker/redislock.lua"; - @Override - public boolean acquireLock(List rowLocks) { - if (CollectionUtils.isEmpty(rowLocks)) { - return true; - } - String needLockXid = rowLocks.get(0).getXid(); - Long branchId = rowLocks.get(0).getBranchId(); + private static String ACQUIRE_LOCK_SHA; - try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { - List needLockDOS = convertToLockDO(rowLocks); - if (needLockDOS.size() > 1) { - needLockDOS = needLockDOS.stream().filter(LambdaUtils.distinctByKey(LockDO::getRowKey)) - .collect(Collectors.toList()); - } - List needLockKeys = new ArrayList<>(); - needLockDOS.forEach(lockDO -> needLockKeys.add(buildLockKey(lockDO.getRowKey()))); + private static final String WHITE_SPACE = " "; - Pipeline pipeline1 = jedis.pipelined(); - needLockKeys.stream().forEachOrdered(needLockKey -> pipeline1.hget(needLockKey, XID)); - List existedLockInfos = (List) (List) pipeline1.syncAndReturnAll(); - Map needAddLock = new HashMap<>(needLockKeys.size(), 1); + private static final String ANNOTATION_LUA = "--"; - for (int i = 0; i < needLockKeys.size(); i++) { - String existedLockXid = existedLockInfos.get(i); - 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 - return false; - } - } - } + /** + * 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"); + } + } + } - if (needAddLock.isEmpty()) { - return true; - } - Pipeline pipeline = jedis.pipelined(); - List readyKeys = new ArrayList<>(); - needAddLock.forEach((key, value) -> { - pipeline.hsetnx(key, XID, value.getXid()); - pipeline.hsetnx(key, TRANSACTION_ID, value.getTransactionId().toString()); - pipeline.hsetnx(key, BRANCH_ID, value.getBranchId().toString()); - pipeline.hset(key, ROW_KEY, value.getRowKey()); - pipeline.hset(key, RESOURCE_ID, value.getResourceId()); - pipeline.hset(key, TABLE_NAME, value.getTableName()); - pipeline.hset(key, PK, value.getPk()); - readyKeys.add(key); - }); - List results = (List) (List) pipeline.syncAndReturnAll(); - List> partitions = Lists.partition(results, 7); + @Override + public boolean acquireLock(List rowLocks) { + return acquireLock(rowLocks, true, false); + } - ArrayList success = new ArrayList<>(partitions.size()); - Integer status = SUCCEED; - for (int i = 0; i < partitions.size(); i++) { - if (Objects.equals(partitions.get(i).get(0), FAILED)) { - status = FAILED; - } - else { - success.add(readyKeys.get(i)); - } - } + @Override + public boolean acquireLock(List rowLocks, boolean autoCommit, boolean skipCheckLock) { + if (CollectionUtils.isEmpty(rowLocks)) { + return true; + } + try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { + if (ACQUIRE_LOCK_SHA != null && autoCommit) { + return acquireLockByLua(jedis, rowLocks); + } else { + return acquireLockByPipeline(jedis, rowLocks, autoCommit, skipCheckLock); + } + } + } - // 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 acquireLockByPipeline(Jedis jedis, List rowLocks, boolean autoCommit, boolean skipCheckLock) { + String needLockXid = rowLocks.get(0).getXid(); + Long branchId = rowLocks.get(0).getBranchId(); + List needLockDOS = convertToLockDO(rowLocks); + if (needLockDOS.size() > 1) { + needLockDOS = + needLockDOS.stream().filter(LambdaUtils.distinctByKey(LockDO::getRowKey)).collect(Collectors.toList()); + } + List needLockKeys = new ArrayList<>(); + needLockDOS.forEach(lockDO -> needLockKeys.add(buildLockKey(lockDO.getRowKey()))); + Map needAddLock = new HashMap<>(needLockKeys.size(), 1); - @Override - public boolean releaseLock(List rowLocks) { - if (CollectionUtils.isEmpty(rowLocks)) { - return true; - } - String currentXid = rowLocks.get(0).getXid(); - Long branchId = rowLocks.get(0).getBranchId(); - List needReleaseLocks = convertToLockDO(rowLocks); - String[] needReleaseKeys = new String[needReleaseLocks.size()]; - for (int i = 0; i < needReleaseLocks.size(); i++) { - needReleaseKeys[i] = buildLockKey(needReleaseLocks.get(i).getRowKey()); - } + if (!skipCheckLock) { + Pipeline pipeline1 = jedis.pipelined(); + needLockKeys.stream().forEachOrdered(needLockKey -> { + pipeline1.hget(needLockKey, XID); + if (!autoCommit) { + pipeline1.hget(needLockKey, STATUS); + } + }); + List> existedLockInfos = + Lists.partition((List)(List)pipeline1.syncAndReturnAll(), autoCommit ? 1 : 2); - try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { - Pipeline pipelined = jedis.pipelined(); - pipelined.del(needReleaseKeys); - pipelined.hdel(buildXidLockKey(currentXid), branchId.toString()); - pipelined.sync(); - return true; - } - } + // 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)); + } + } - @Override - public boolean releaseLock(String xid, List branchIds) { - if (CollectionUtils.isEmpty(branchIds)) { - return true; - } - try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { - String xidLockKey = buildXidLockKey(xid); - String[] branchIdsArray = new String[branchIds.size()]; - for (int i = 0; i < branchIds.size(); i++) { - branchIdsArray[i] = branchIds.get(i).toString(); - } - List rowKeys = jedis.hmget(xidLockKey, branchIdsArray); + // 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 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; + } + } - if (CollectionUtils.isNotEmpty(rowKeys)) { - Pipeline pipelined = jedis.pipelined(); - pipelined.hdel(xidLockKey, branchIdsArray); - 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; - } - } + Pipeline pipeline = jedis.pipelined(); + List readyKeys = new ArrayList<>(needAddLock.keySet()); + needAddLock.forEach((key, value) -> { + pipeline.hsetnx(key, XID, value.getXid()); + pipeline.hsetnx(key, TRANSACTION_ID, value.getTransactionId().toString()); + pipeline.hsetnx(key, BRANCH_ID, value.getBranchId().toString()); + pipeline.hset(key, ROW_KEY, value.getRowKey()); + pipeline.hset(key, RESOURCE_ID, value.getResourceId()); + pipeline.hset(key, TABLE_NAME, value.getTableName()); + pipeline.hset(key, PK, value.getPk()); + }); + List results = (List) (List) pipeline.syncAndReturnAll(); + List> partitions = Lists.partition(results, 7); - @Override - public boolean releaseLock(String xid, Long branchId) { - List branchIds = new ArrayList<>(); - branchIds.add(branchId); - return releaseLock(xid, branchIds); - } + ArrayList success = new ArrayList<>(partitions.size()); + Integer status = SUCCEED; + for (int i = 0; i < partitions.size(); i++) { + if (Objects.equals(partitions.get(i).get(0), FAILED)) { + status = FAILED; + } else { + success.add(readyKeys.get(i)); + } + } - @Override - public boolean isLockable(List rowLocks) { - if (CollectionUtils.isEmpty(rowLocks)) { - return true; - } - try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { - List locks = convertToLockDO(rowLocks); - Set lockKeys = new HashSet<>(); - for (LockDO rowlock : locks) { - lockKeys.add(buildLockKey(rowlock.getRowKey())); - } + // 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; + } - String xid = rowLocks.get(0).getXid(); - Pipeline pipeline = jedis.pipelined(); - lockKeys.forEach(key -> pipeline.hget(key, XID)); - List existedXids = (List) (List) pipeline.syncAndReturnAll(); - return existedXids.stream().allMatch(existedXid -> existedXid == null || xid.equals(existedXid)); - } - } + private boolean acquireLockByLua(Jedis jedis, List rowLocks) { + String needLockXid = rowLocks.get(0).getXid(); + Long branchId = rowLocks.get(0).getBranchId(); + List needLockDOs = rowLocks.stream() + .map(this::convertToLockDO) + .filter(LambdaUtils.distinctByKey(LockDO::getRowKey)) + .collect(Collectors.toList()); + ArrayList keys = new ArrayList<>(); + ArrayList args = new ArrayList<>(); + int size = needLockDOs.size(); + args.add(String.valueOf(size)); + // args index 2 placeholder + args.add(null); + args.add(needLockXid); + for (LockDO lockDO : needLockDOs) { + keys.add(buildLockKey(lockDO.getRowKey())); + args.add(lockDO.getTransactionId().toString()); + args.add(lockDO.getBranchId().toString()); + args.add(lockDO.getResourceId()); + args.add(lockDO.getTableName()); + args.add(lockDO.getRowKey()); + args.add(lockDO.getPk()); + } + String xidLockKey = buildXidLockKey(needLockXid); + StringJoiner lockKeysString = new StringJoiner(ROW_LOCK_KEY_SPLIT_CHAR); + needLockDOs.stream().map(lockDO -> buildLockKey(lockDO.getRowKey())).forEach(lockKeysString::add); + keys.add(xidLockKey); + keys.add(branchId.toString()); + args.add(lockKeysString.toString()); + // reset args index 2 + args.set(1, String.valueOf(args.size())); + String xIdOwnLock = (String) jedis.evalsha(ACQUIRE_LOCK_SHA, keys, args); + if (xIdOwnLock.equals(needLockXid)) { + return true; + } else { + logGlobalLockConflictInfo(needLockXid, keys.get(0), xIdOwnLock); + return false; + } + } - private String buildXidLockKey(String xid) { - return DEFAULT_REDIS_SEATA_GLOBAL_LOCK_PREFIX + xid; - } + 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); + } - private String buildLockKey(String rowKey) { - return DEFAULT_REDIS_SEATA_ROW_LOCK_PREFIX + rowKey; - } + @Override + public boolean releaseLock(List rowLocks) { + if (CollectionUtils.isEmpty(rowLocks)) { + return true; + } + String currentXid = rowLocks.get(0).getXid(); + Long branchId = rowLocks.get(0).getBranchId(); + List 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 rowLocks) { + if (CollectionUtils.isEmpty(rowLocks)) { + return true; + } + try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { + List locks = convertToLockDO(rowLocks); + Set lockKeys = new HashSet<>(); + for (LockDO rowlock : locks) { + lockKeys.add(buildLockKey(rowlock.getRowKey())); + } + + String xid = rowLocks.get(0).getXid(); + try (Pipeline pipeline = jedis.pipelined()) { + lockKeys.forEach(key -> pipeline.hget(key, XID)); + List existedXids = (List)(List)pipeline.syncAndReturnAll(); + return existedXids.stream().allMatch(existedXid -> existedXid == null || xid.equals(existedXid)); + } + } + } + + @Override + public void updateLockStatus(String xid, LockStatus lockStatus) { + try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { + String xidLockKey = buildXidLockKey(xid); + Map branchAndLockKeys = jedis.hgetAll(xidLockKey); + if (CollectionUtils.isNotEmpty(branchAndLockKeys)) { + try (Pipeline pipeline = jedis.pipelined()) { + branchAndLockKeys.values() + .forEach(k -> pipeline.hset(k, STATUS, String.valueOf(lockStatus.getCode()))); + pipeline.sync(); + } + } + } + } + + private boolean doReleaseLock(String xid, Long branchId) { + try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { + String xidLockKey = buildXidLockKey(xid); + final List rowKeys = new ArrayList<>(); + if (null == branchId) { + Map rowKeyMap = jedis.hgetAll(xidLockKey); + rowKeyMap.forEach((branch, rowKey) -> rowKeys.add(rowKey)); + } else { + rowKeys.add(jedis.hget(xidLockKey, branchId.toString())); + } + if (CollectionUtils.isNotEmpty(rowKeys)) { + Pipeline pipelined = jedis.pipelined(); + 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; + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/session/RedisSessionManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/session/RedisSessionManager.java index 7c326ff0b..50120200e 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/session/RedisSessionManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/session/RedisSessionManager.java @@ -15,8 +15,6 @@ */ package io.seata.server.storage.redis.session; -import java.util.Collection; -import java.util.List; import io.seata.common.exception.StoreException; import io.seata.common.executor.Initialize; import io.seata.common.loader.LoadLevel; @@ -25,172 +23,167 @@ import io.seata.common.util.StringUtils; import io.seata.core.exception.TransactionException; import io.seata.core.model.BranchStatus; import io.seata.core.model.GlobalStatus; -import io.seata.server.session.AbstractSessionManager; -import io.seata.server.session.BranchSession; -import io.seata.server.session.GlobalSession; -import io.seata.server.session.SessionCondition; -import io.seata.server.session.SessionHolder; +import io.seata.server.session.*; import io.seata.server.storage.redis.store.RedisTransactionStoreManager; import io.seata.server.store.TransactionStoreManager.LogOperation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Collection; +import java.util.List; + /** * @author funkye */ @LoadLevel(name = "redis", scope = Scope.PROTOTYPE) -public class RedisSessionManager extends AbstractSessionManager implements Initialize { +public class RedisSessionManager extends AbstractSessionManager + implements Initialize { + /** + * The constant LOGGER. + */ + protected static final Logger LOGGER = LoggerFactory.getLogger(RedisSessionManager.class); - /** - * The constant LOGGER. - */ - protected static final Logger LOGGER = LoggerFactory.getLogger(RedisSessionManager.class); + /** + * The Task name. + */ + protected String taskName; - /** - * The Task name. - */ - protected String taskName; + /** + * Instantiates a new Data base session manager. + */ + public RedisSessionManager() { + super(); + } - /** - * 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; + } - /** - * 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 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 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."); + } + } - @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."); + } + } - /** - * 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 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 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 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) { - return this.findGlobalSession(xid, true); - } + @Override + public GlobalSession findGlobalSession(String xid, boolean withBranchSessions) { + return transactionStoreManager.readSession(xid, withBranchSessions); + } - @Override - public GlobalSession findGlobalSession(String xid, boolean withBranchSessions) { - return transactionStoreManager.readSession(xid, withBranchSessions); - } + @Override + public Collection 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 Collection 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(new GlobalStatus[] { GlobalStatus.CommitRetrying, GlobalStatus.Committing })); - } - else if (SessionHolder.RETRY_ROLLBACKING_SESSION_MANAGER_NAME.equalsIgnoreCase(taskName)) { - return findGlobalSessions(new SessionCondition(new GlobalStatus[] { GlobalStatus.RollbackRetrying, - GlobalStatus.Rollbacking, GlobalStatus.TimeoutRollbacking, GlobalStatus.TimeoutRollbackRetrying })); - } - else { - // all data - return findGlobalSessions(new SessionCondition(new GlobalStatus[] { GlobalStatus.UnKnown, - GlobalStatus.Begin, GlobalStatus.Committing, GlobalStatus.CommitRetrying, GlobalStatus.Rollbacking, - GlobalStatus.RollbackRetrying, GlobalStatus.TimeoutRollbacking, - GlobalStatus.TimeoutRollbackRetrying, GlobalStatus.AsyncCommitting })); - } - } - - @Override - public List findGlobalSessions(SessionCondition condition) { - // nothing need to do - return transactionStoreManager.readSession(condition); - } - - @Override - public T lockAndExecute(GlobalSession globalSession, GlobalSession.LockCallable lockCallable) - throws TransactionException { - return lockCallable.call(); - } + @Override + public List findGlobalSessions(SessionCondition condition) { + // nothing need to do + return transactionStoreManager.readSession(condition); + } + @Override + public T lockAndExecute(GlobalSession globalSession, GlobalSession.LockCallable lockCallable) + throws TransactionException { + return lockCallable.call(); + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/store/RedisTransactionStoreManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/store/RedisTransactionStoreManager.java index 197955af4..573d120e1 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/store/RedisTransactionStoreManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/storage/redis/store/RedisTransactionStoreManager.java @@ -15,482 +15,821 @@ */ package io.seata.server.storage.redis.store; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import redis.clients.jedis.Jedis; -import redis.clients.jedis.Pipeline; -import redis.clients.jedis.Transaction; +import com.google.common.collect.ImmutableMap; +import io.seata.common.XID; +import io.seata.common.exception.RedisException; import io.seata.common.exception.StoreException; +import io.seata.common.util.BeanUtils; import io.seata.common.util.CollectionUtils; import io.seata.common.util.StringUtils; -import io.seata.common.exception.RedisException; -import io.seata.common.util.BeanUtils; -import io.seata.common.XID; +import io.seata.config.Configuration; +import io.seata.config.ConfigurationFactory; import io.seata.core.model.GlobalStatus; import io.seata.core.store.BranchTransactionDO; import io.seata.core.store.GlobalTransactionDO; +import io.seata.server.console.param.GlobalSessionParam; import io.seata.server.session.GlobalSession; import io.seata.server.session.SessionCondition; +import io.seata.server.session.SessionStatusValidator; import io.seata.server.storage.SessionConverter; import io.seata.server.storage.redis.JedisPooledFactory; import io.seata.server.store.AbstractTransactionStoreManager; import io.seata.server.store.SessionStorable; import io.seata.server.store.TransactionStoreManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.Pipeline; +import redis.clients.jedis.Transaction; -import static io.seata.core.constants.RedisKeyConstants.REDIS_KEY_BRANCH_GMT_MODIFIED; -import static io.seata.core.constants.RedisKeyConstants.REDIS_KEY_BRANCH_STATUS; -import static io.seata.core.constants.RedisKeyConstants.REDIS_KEY_BRANCH_XID; -import static io.seata.core.constants.RedisKeyConstants.REDIS_KEY_GLOBAL_GMT_MODIFIED; -import static io.seata.core.constants.RedisKeyConstants.REDIS_KEY_GLOBAL_STATUS; -import static io.seata.core.constants.RedisKeyConstants.REDIS_KEY_GLOBAL_XID; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static io.seata.common.ConfigurationKeys.STORE_REDIS_QUERY_LIMIT; +import static io.seata.common.DefaultValues.DEFAULT_QUERY_LIMIT; +import static io.seata.core.constants.RedisKeyConstants.*; /** * The redis transaction store manager * * @author funkye * @author wangzhongxiang + * @author doubleDimple */ public class RedisTransactionStoreManager extends AbstractTransactionStoreManager implements TransactionStoreManager { - private static final Logger LOGGER = LoggerFactory.getLogger(RedisTransactionStoreManager.class); + 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 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 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*/ + 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 prefix of the global transaction status*/ + private static final String REDIS_SEATA_STATUS_PREFIX = "SEATA_STATUS_"; - private static volatile RedisTransactionStoreManager instance; + /**the key of global transaction status for begin*/ + private static final String REDIS_SEATA_BEGIN_TRANSACTIONS_KEY = "SEATA_BEGIN_TRANSACTIONS"; - private static final String OK = "OK"; + private static volatile RedisTransactionStoreManager instance; - /** - * Get the instance. - */ - public static RedisTransactionStoreManager getInstance() { - if (instance == null) { - synchronized (RedisTransactionStoreManager.class) { - if (instance == null) { - instance = new RedisTransactionStoreManager(); - } - } - } - return instance; - } + private static final String OK = "OK"; - @Override - public boolean writeSession(LogOperation logOperation, SessionStorable session) { - if (LogOperation.GLOBAL_ADD.equals(logOperation)) { - return insertGlobalTransactionDO(SessionConverter.convertGlobalTransactionDO(session)); - } - else if (LogOperation.GLOBAL_UPDATE.equals(logOperation)) { - return updateGlobalTransactionDO(SessionConverter.convertGlobalTransactionDO(session)); - } - else if (LogOperation.GLOBAL_REMOVE.equals(logOperation)) { - return deleteGlobalTransactionDO(SessionConverter.convertGlobalTransactionDO(session)); - } - else if (LogOperation.BRANCH_ADD.equals(logOperation)) { - return insertBranchTransactionDO(SessionConverter.convertBranchTransactionDO(session)); - } - else if (LogOperation.BRANCH_UPDATE.equals(logOperation)) { - return updateBranchTransactionDO(SessionConverter.convertBranchTransactionDO(session)); - } - else if (LogOperation.BRANCH_REMOVE.equals(logOperation)) { - return deleteBranchTransactionDO(SessionConverter.convertBranchTransactionDO(session)); - } - else { - throw new StoreException("Unknown LogOperation:" + logOperation.name()); - } - } + /** + * The constant CONFIG. + */ + private static final Configuration CONFIG = ConfigurationFactory.getInstance(); - /** - * 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()) { - Date now = new Date(); - branchTransactionDO.setGmtCreate(now); - branchTransactionDO.setGmtModified(now); - Pipeline pipelined = jedis.pipelined(); - pipelined.hmset(branchKey, BeanUtils.objectToMap(branchTransactionDO)); - pipelined.rpush(branchListKey, branchKey); - pipelined.sync(); - return true; - } - catch (Exception ex) { - throw new RedisException(ex); - } - } + /** + * The Log query limit. + */ + private int logQueryLimit; - /** - * Delete the branch transaction - * @param branchTransactionDO - * @return - */ - private boolean deleteBranchTransactionDO(BranchTransactionDO branchTransactionDO) { - String branchKey = buildBranchKey(branchTransactionDO.getBranchId()); - try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { - Map branchTransactionDOMap = jedis.hgetAll(branchKey); - String xid = branchTransactionDOMap.get(REDIS_KEY_BRANCH_XID); - if (StringUtils.isEmpty(xid)) { - return true; - } - String branchListKey = buildBranchListKeyByXid(branchTransactionDO.getXid()); - Pipeline pipelined = jedis.pipelined(); - pipelined.lrem(branchListKey, 0, branchKey); - pipelined.del(branchKey); - pipelined.sync(); - return true; - } - catch (Exception ex) { - throw new RedisException(ex); - } - } + /** + * Get the instance. + */ + public static RedisTransactionStoreManager getInstance() { + if (instance == null) { + synchronized (RedisTransactionStoreManager.class) { + if (instance == null) { + instance = new RedisTransactionStoreManager(); + } + } + } + return instance; + } - /** - * 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 map = new HashMap<>(2, 1); - map.put(REDIS_KEY_BRANCH_STATUS, String.valueOf(branchTransactionDO.getStatus())); - map.put(REDIS_KEY_BRANCH_GMT_MODIFIED, String.valueOf((new Date()).getTime())); - jedis.hmset(branchKey, map); - return true; - } - catch (Exception ex) { - throw new RedisException(ex); - } - } + /** + * init map to constructor + */ + public RedisTransactionStoreManager() { + super(); + initGlobalMap(); + initBranchMap(); + logQueryLimit = CONFIG.getInt(STORE_REDIS_QUERY_LIMIT, DEFAULT_QUERY_LIMIT); + } - /** - * Insert the global transaction. - * @param globalTransactionDO - * @return - */ - private boolean insertGlobalTransactionDO(GlobalTransactionDO globalTransactionDO) { - String globalKey = buildGlobalKeyByTransactionId(globalTransactionDO.getTransactionId()); - try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { - Date now = new Date(); - globalTransactionDO.setGmtCreate(now); - globalTransactionDO.setGmtModified(now); - Pipeline pipelined = jedis.pipelined(); - pipelined.hmset(globalKey, BeanUtils.objectToMap(globalTransactionDO)); - pipelined.rpush(buildGlobalStatus(globalTransactionDO.getStatus()), globalTransactionDO.getXid()); - pipelined.sync(); - return true; - } - catch (Exception ex) { - throw new RedisException(ex); - } - } + /** + * Map for LogOperation Global Operation + */ + public static volatile ImmutableMap> globalMap; - /** - * 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()) { - Map globalTransactionDoMap = jedis.hgetAll(globalKey); - String xid = globalTransactionDoMap.get(REDIS_KEY_GLOBAL_XID); - if (StringUtils.isEmpty(xid)) { - LOGGER.warn("Global transaction is not exist,xid = {}.Maybe has been deleted by another tc server", - globalTransactionDO.getXid()); - return true; - } - Pipeline pipelined = jedis.pipelined(); - pipelined.lrem(buildGlobalStatus(globalTransactionDO.getStatus()), 0, globalTransactionDO.getXid()); - pipelined.del(globalKey); - pipelined.sync(); - return true; - } - catch (Exception ex) { - throw new RedisException(ex); - } - } + /** + * Map for LogOperation Branch Operation + */ + public static volatile ImmutableMap> branchMap; - /** - * 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 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; - } - String previousGmtModified = statusAndGmtModified.get(1); - Transaction multi = jedis.multi(); - Map 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); - List exec = multi.exec(); - 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 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); - } - } + /** + * init globalMap + * + */ + public void initGlobalMap() { + if (CollectionUtils.isEmpty(branchMap)) { + globalMap = ImmutableMap.>builder() + .put(LogOperation.GLOBAL_ADD, this::insertGlobalTransactionDO) + .put(LogOperation.GLOBAL_UPDATE, this::updateGlobalTransactionDO) + .put(LogOperation.GLOBAL_REMOVE, this::deleteGlobalTransactionDO) + .build(); + } + } - /** - * 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 map = jedis.hgetAll(globalKey); - if (CollectionUtils.isEmpty(map)) { - return null; - } - GlobalTransactionDO globalTransactionDO = (GlobalTransactionDO) BeanUtils.mapToObject(map, - GlobalTransactionDO.class); - List branchTransactionDOs = null; - if (withBranchSessions) { - branchTransactionDOs = this.readBranchSessionByXid(jedis, xid); - } - return getGlobalSession(globalTransactionDO, branchTransactionDOs); - } - } + /** + * init branchMap + * + */ + public void initBranchMap() { + if (CollectionUtils.isEmpty(branchMap)) { + branchMap = ImmutableMap.>builder() + .put(LogOperation.BRANCH_ADD, this::insertBranchTransactionDO) + .put(LogOperation.BRANCH_UPDATE, this::updateBranchTransactionDO) + .put(LogOperation.BRANCH_REMOVE, this::deleteBranchTransactionDO) + .build(); + } + } - /** - * 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 - */ - public List readSession(GlobalStatus[] statuses) { - List statusKeys = new ArrayList<>(); - for (int i = 0; i < statuses.length; i++) { - statusKeys.add(buildGlobalStatus(statuses[i].getCode())); - } - try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { - Pipeline pipelined = jedis.pipelined(); - statusKeys.stream().forEach(statusKey -> pipelined.lrange(statusKey, 0, -1)); - List> list = (List>) (List) pipelined.syncAndReturnAll(); - List xids = new ArrayList<>(); - if (CollectionUtils.isNotEmpty(list)) { - xids = list.stream().flatMap(ll -> ll.stream()).collect(Collectors.toList()); - } - List globalSessions = new ArrayList<>(); - xids.parallelStream().forEach(xid -> { - GlobalSession globalSession = this.readSession(xid, true); - if (globalSession != null) { - globalSessions.add(globalSession); - } - }); - return globalSessions; - } - } + @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()); + } + } - /** - * read the global session list by different condition - * @param sessionCondition the session condition - * @return the global sessions - */ - @Override - public List readSession(SessionCondition sessionCondition) { - List globalSessions = new ArrayList<>(); - if (StringUtils.isNotEmpty(sessionCondition.getXid())) { - GlobalSession globalSession = this.readSession(sessionCondition.getXid(), true); - if (globalSession != null) { - globalSessions.add(globalSession); - } - return globalSessions; - } - else if (sessionCondition.getTransactionId() != null) { - GlobalSession globalSession = this - .readSessionByTransactionId(sessionCondition.getTransactionId().toString(), true); - if (globalSession != null) { - globalSessions.add(globalSession); - } - return globalSessions; - } - else if (CollectionUtils.isNotEmpty(sessionCondition.getStatuses())) { - return readSession(sessionCondition.getStatuses()); - } - else if (sessionCondition.getStatus() != null) { - return readSession(new GlobalStatus[] { sessionCondition.getStatus() }); - } - return null; - } + /** + * 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); + } + } - /** - * assemble the global session and branch session - * @param globalTransactionDO the global transactionDo - * @param branchTransactionDOs the branch transactionDos - * @return the global session with branch session - */ - private GlobalSession getGlobalSession(GlobalTransactionDO globalTransactionDO, - List branchTransactionDOs) { - GlobalSession globalSession = SessionConverter.convertGlobalSession(globalTransactionDO); - if (CollectionUtils.isNotEmpty(branchTransactionDOs)) { - for (BranchTransactionDO branchTransactionDO : branchTransactionDOs) { - globalSession.add(SessionConverter.convertBranchSession(branchTransactionDO)); - } - } - return globalSession; - } + /** + * 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); + } + } - /** - * 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 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 branchTransactionDOs = new ArrayList<>(); - if (withBranchSessions) { - branchTransactionDOs = this.readBranchSessionByXid(jedis, xid); - } - return getGlobalSession(globalTransactionDO, branchTransactionDOs); - } - } + /** + * 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 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); + } + } - /** - * Read the branch session list by xid - * @param jedis - * @param xid the xid - * @return the branch transactionDo list - */ - private List readBranchSessionByXid(Jedis jedis, String xid) { - List branchTransactionDOs = new ArrayList<>(); - String branchListKey = buildBranchListKeyByXid(xid); - List branchKeys = jedis.lrange(branchListKey, 0, -1); - Pipeline pipeline = jedis.pipelined(); - if (CollectionUtils.isNotEmpty(branchKeys)) { - branchKeys.stream().forEachOrdered(branchKey -> pipeline.hgetAll(branchKey)); - List branchInfos = pipeline.syncAndReturnAll(); - for (Object branchInfo : branchInfos) { - if (branchInfo != null) { - Map branchInfoMap = (Map) branchInfo; - Optional branchTransactionDO = Optional.ofNullable( - (BranchTransactionDO) BeanUtils.mapToObject(branchInfoMap, BranchTransactionDO.class)); - branchTransactionDO.ifPresent(branchTransactionDOs::add); - } - } - } - if (CollectionUtils.isNotEmpty(branchTransactionDOs)) { - branchTransactionDOs = branchTransactionDOs.stream() - .sorted(Comparator.comparing(BranchTransactionDO::getGmtCreate)).collect(Collectors.toList()); - } - return branchTransactionDOs; - } + /** + * 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); + } + } - private String buildBranchListKeyByXid(String xid) { - return REDIS_SEATA_BRANCHES_PREFIX + xid; - } + /** + * 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); + } + } - private String buildGlobalKeyByTransactionId(Object transactionId) { - return REDIS_SEATA_GLOBAL_PREFIX + transactionId; - } + /** + * 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 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() + "]"); + } - private String buildBranchKey(Long branchId) { - return REDIS_SEATA_BRANCH_PREFIX + branchId; - } + String previousGmtModified = statusAndGmtModified.get(1); + Transaction multi = jedis.multi(); + Map 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 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 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); + } + } - private String buildGlobalStatus(Integer status) { - return REDIS_SEATA_STATUS_PREFIX + status; - } + /** + * 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 map = jedis.hgetAll(globalKey); + if (CollectionUtils.isEmpty(map)) { + return null; + } + GlobalTransactionDO globalTransactionDO = (GlobalTransactionDO)BeanUtils.mapToObject(map, GlobalTransactionDO.class); + List 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 readSession(GlobalStatus[] statuses, boolean withBranchSessions) { + List globalSessions = Collections.synchronizedList(new ArrayList<>()); + List statusKeys = convertStatusKeys(statuses); + Map targetMap = calculateStatuskeysHasData(statusKeys); + if (targetMap.size() == 0 || logQueryLimit <= 0) { + return globalSessions; + } + int perStatusLimit = resetLogQueryLimit(targetMap); + final long countGlobalSessions = targetMap.values().stream().collect(Collectors.summarizingInt(Integer::intValue)).getSum(); + // queryCount + final long queryCount = Math.min(logQueryLimit, countGlobalSessions); + List> list = new ArrayList<>(); + dogetXidsForTargetMapRecursive(targetMap, 0L, perStatusLimit - 1, queryCount, list); + if (CollectionUtils.isNotEmpty(list)) { + List xids = list.stream().flatMap(Collection::stream).collect(Collectors.toList()); + xids.parallelStream().forEach(xid -> { + GlobalSession globalSession = this.readSession(xid, withBranchSessions); + if (globalSession != null) { + globalSessions.add(globalSession); + } + }); + } + return globalSessions; + } + + @Override + public List readSortByTimeoutBeginSessions(boolean withBranchSessions) { + List list = Collections.emptyList(); + List statusKeys = convertStatusKeys(GlobalStatus.Begin); + Map targetMap = calculateStatuskeysHasData(statusKeys); + if (targetMap.size() == 0 || logQueryLimit <= 0) { + return list; + } + final long countGlobalSessions = targetMap.values().stream().collect(Collectors.summarizingInt(Integer::intValue)).getSum(); + // queryCount + final long queryCount = Math.min(logQueryLimit, countGlobalSessions); + try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { + Set values = + jedis.zrangeByScore(REDIS_SEATA_BEGIN_TRANSACTIONS_KEY, 0, System.currentTimeMillis(), 0, + (int) queryCount); + List> rep; + try (Pipeline pipeline = jedis.pipelined()) { + for (String value : values) { + pipeline.hgetAll(value); + } + rep = (List>) (List) pipeline.syncAndReturnAll(); + } + list = rep.stream().map(map -> { + GlobalTransactionDO globalTransactionDO = (GlobalTransactionDO) BeanUtils.mapToObject(map, + GlobalTransactionDO.class); + if (globalTransactionDO != null) { + String xid = globalTransactionDO.getXid(); + List branchTransactionDOs = new ArrayList<>(); + if (withBranchSessions) { + branchTransactionDOs = this.readBranchSessionByXid(jedis, xid); + } + return getGlobalSession(globalTransactionDO, branchTransactionDOs, withBranchSessions); + } + return null; + }).filter(Objects::nonNull).collect(Collectors.toList()); + } + return list; + } + + /** + * get everyone keys limit + * + * @param targetMap + * @return + */ + private int resetLogQueryLimit(Map targetMap) { + int resetLimitQuery = logQueryLimit; + if (targetMap.size() > 1) { + int size = targetMap.size(); + resetLimitQuery = (logQueryLimit / size) == 0 ? 1 : (logQueryLimit / size); + } + return resetLimitQuery; + } + + /** + * read the global session list by different condition + * @param sessionCondition the session condition + * @return the global sessions + */ + @Override + public List readSession(SessionCondition sessionCondition) { + List 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 + */ + public List readSessionStatusByPage(GlobalSessionParam param) { + List globalSessions = new ArrayList<>(); + + int pageNum = param.getPageNum(); + int pageSize = param.getPageSize(); + int start = Math.max((pageNum - 1) * pageSize, 0); + int end = pageNum * pageSize - 1; + + if (param.getStatus() != null) { + String statusKey = buildGlobalStatus(GlobalStatus.get(param.getStatus()).getCode()); + try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { + final List xids = jedis.lrange(statusKey, start, end); + xids.forEach(xid -> { + GlobalSession globalSession = this.readSession(xid, param.isWithBranch()); + if (globalSession != null) { + globalSessions.add(globalSession); + } + }); + } + } + return globalSessions; + } + + /** + * assemble the global session and branch session + * @param globalTransactionDO the global transactionDo + * @param branchTransactionDOs the branch transactionDos + * @param withBranchSessions if read branch sessions + * @return the global session with branch session + */ + private GlobalSession getGlobalSession(GlobalTransactionDO globalTransactionDO, + List branchTransactionDOs, 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 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 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 readBranchSessionByXid(Jedis jedis, String xid) { + List branchTransactionDOs = new ArrayList<>(); + String branchListKey = buildBranchListKeyByXid(xid); + List branchKeys = lRange(jedis, branchListKey); + if (CollectionUtils.isNotEmpty(branchKeys)) { + try (Pipeline pipeline = jedis.pipelined()) { + branchKeys.stream().forEach(branchKey -> pipeline.hgetAll(branchKey)); + List branchInfos = pipeline.syncAndReturnAll(); + for (Object branchInfo : branchInfos) { + if (branchInfo != null) { + Map branchInfoMap = (Map)branchInfo; + Optional branchTransactionDO = Optional.ofNullable( + (BranchTransactionDO)BeanUtils.mapToObject(branchInfoMap, BranchTransactionDO.class)); + branchTransactionDO.ifPresent(branchTransactionDOs::add); + } + } + } + } + if (CollectionUtils.isNotEmpty(branchTransactionDOs)) { + Collections.sort(branchTransactionDOs); + } + return branchTransactionDOs; + } + + private List lRange(Jedis jedis, String key) { + List keys = new ArrayList<>(); + List values; + int limit = 20; + int start = 0; + int stop = limit; + for (;;) { + values = jedis.lrange(key, start, stop); + keys.addAll(values); + if (CollectionUtils.isEmpty(values) || values.size() < limit) { + break; + } + start = keys.size(); + stop = start + limit; + } + return keys; + } + + public List findBranchSessionByXid(String xid) { + try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { + return readBranchSessionByXid(jedis, xid); + } + } + + /** + * query globalSession by page + * + * @param pageNum + * @param pageSize + * @param withBranchSessions + * @return List + */ + public List findGlobalSessionByPage(int pageNum, int pageSize, boolean withBranchSessions) { + List globalSessions = new ArrayList<>(); + int start = Math.max((pageNum - 1) * pageSize, 0); + int end = pageNum * pageSize - 1; + + List statusKeys = convertStatusKeys(GlobalStatus.values()); + Map stringLongMap = calculateStatuskeysHasData(statusKeys); + + List> list = dogetXidsForTargetMap(stringLongMap, start, end, pageSize); + + if (CollectionUtils.isNotEmpty(list)) { + List xids = list.stream().flatMap(Collection::stream).collect(Collectors.toList()); + xids.forEach(xid -> { + if (globalSessions.size() < pageSize) { + GlobalSession globalSession = this.readSession(xid, withBranchSessions); + if (globalSession != null) { + globalSessions.add(globalSession); + } + } + }); + } + return globalSessions; + } + + /** + * query and sort existing values + * + * @param statusKeys + * @return + */ + private Map calculateStatuskeysHasData(List statusKeys) { + Map resultMap = new LinkedHashMap<>(); + Map keysMap = new HashMap<>(statusKeys.size()); + try (Jedis jedis = JedisPooledFactory.getJedisInstance(); Pipeline pipelined = jedis.pipelined()) { + statusKeys.forEach(key -> pipelined.llen(key)); + List counts = (List) pipelined.syncAndReturnAll(); + for (int i = 0; i < counts.size(); i++) { + if (counts.get(i) > 0) { + keysMap.put(statusKeys.get(i), counts.get(i).intValue()); + } + } + } + + //sort + List> list = new ArrayList<>(keysMap.entrySet()); + list.sort((o1, o2) -> o2.getValue() - o1.getValue()); + list.forEach(e -> resultMap.put(e.getKey(), e.getValue())); + + return resultMap; + } + + /** + * count GlobalSession total by status + * + * @param values + * @return Long + */ + public Long countByGlobalSessions(GlobalStatus[] values) { + List statusKeys = new ArrayList<>(); + Long total = 0L; + for (GlobalStatus status : values) { + statusKeys.add(buildGlobalStatus(status.getCode())); + } + try (Jedis jedis = JedisPooledFactory.getJedisInstance(); Pipeline pipelined = jedis.pipelined()) { + statusKeys.stream().forEach(statusKey -> pipelined.llen(statusKey)); + List list = (List)(List)pipelined.syncAndReturnAll(); + if (list.size() > 0) { + total = list.stream().mapToLong(value -> value).sum(); + } + return total; + } + } + + private List convertStatusKeys(GlobalStatus... statuses) { + List statusKeys = new ArrayList<>(); + for (int i = 0; i < statuses.length; i++) { + statusKeys.add(buildGlobalStatus(statuses[i].getCode())); + } + return statusKeys; + } + + private void dogetXidsForTargetMapRecursive(Map targetMap, long start, long end, + long queryCount, List> listList) { + + long total = listList.stream().mapToLong(List::size).sum(); + + if (total >= queryCount) { + return; + } + // when start greater than offset(totalCount) + if (start >= queryCount) { + return; + } + + if (targetMap.size() == 0) { + return; + } + + try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { + Iterator> iterator = targetMap.entrySet().iterator(); + while (iterator.hasNext()) { + String key = iterator.next().getKey(); + final long sum = listList.stream().mapToLong(List::size).sum(); + final long diffCount = queryCount - sum; + if (diffCount <= 0) { + return; + } + List list; + if (end - start >= diffCount) { + long endNew = start + diffCount - 1; + list = jedis.lrange(key, start, endNew); + } else { + list = jedis.lrange(key, start, end); + } + + if (list.size() > 0) { + listList.add(list); + } else { + iterator.remove(); + } + } + } + long startNew = end + 1; + long endNew = startNew + end - start; + dogetXidsForTargetMapRecursive(targetMap, startNew, endNew, queryCount, listList); + } + + private List> dogetXidsForTargetMap(Map targetMap, int start, int end, + int totalCount) { + List> listList = new ArrayList<>(); + try (Jedis jedis = JedisPooledFactory.getJedisInstance()) { + for (String key : targetMap.keySet()) { + final List list = jedis.lrange(key, start, end); + final long sum = listList.stream().mapToLong(List::size).sum(); + if (list.size() > 0 && sum < totalCount) { + listList.add(list); + } else { + start = 0; + end = totalCount - 1; + } + } + } + return listList; + } + + private String buildBranchListKeyByXid(String xid) { + return REDIS_SEATA_BRANCHES_PREFIX + xid; + } + + 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; + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/AbstractTransactionStoreManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/AbstractTransactionStoreManager.java index 98c769408..3595a72c4 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/AbstractTransactionStoreManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/AbstractTransactionStoreManager.java @@ -15,9 +15,11 @@ */ package io.seata.server.store; +import io.seata.core.model.GlobalStatus; import io.seata.server.session.GlobalSession; import io.seata.server.session.SessionCondition; +import java.util.Collections; import java.util.List; /** @@ -27,23 +29,32 @@ import java.util.List; */ public abstract class AbstractTransactionStoreManager implements TransactionStoreManager { - @Override - public GlobalSession readSession(String xid) { - return null; - } + @Override + public GlobalSession readSession(String xid) { + return null; + } - @Override - public GlobalSession readSession(String xid, boolean withBranchSessions) { - return null; - } + @Override + public GlobalSession readSession(String xid, boolean withBranchSessions) { + return null; + } - @Override - public List readSession(SessionCondition sessionCondition) { - return null; - } + @Override + public List readSortByTimeoutBeginSessions(boolean withBranchSessions) { + return Collections.emptyList(); + } - @Override - public void shutdown() { - } + @Override + public List readSession(GlobalStatus[] statuses, boolean withBranchSessions) { + return Collections.emptyList(); + } + @Override + public List readSession(SessionCondition sessionCondition) { + return Collections.emptyList(); + } + + @Override + public void shutdown() { + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/DbcpDataSourceProvider.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/DbcpDataSourceProvider.java index 0d82bee3e..8880943bf 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/DbcpDataSourceProvider.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/DbcpDataSourceProvider.java @@ -23,7 +23,6 @@ import javax.sql.DataSource; /** * The dbcp datasource provider - * * @author zhangsen * @author ggndnn * @author will @@ -31,28 +30,27 @@ import javax.sql.DataSource; @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"); - return ds; - } + @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"); + return ds; + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/DruidDataSourceProvider.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/DruidDataSourceProvider.java index 9cb7a7229..b18a8bbd6 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/DruidDataSourceProvider.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/DruidDataSourceProvider.java @@ -23,7 +23,6 @@ import javax.sql.DataSource; /** * The druid datasource provider - * * @author zhangsen * @author ggndnn * @author will @@ -31,27 +30,28 @@ import javax.sql.DataSource; @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); - return ds; - } - + @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); + return ds; + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/HikariDataSourceProvider.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/HikariDataSourceProvider.java index 13f3d980a..8adb354f5 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/HikariDataSourceProvider.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/HikariDataSourceProvider.java @@ -25,38 +25,36 @@ 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); - return new HikariDataSource(config); - } + @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); + return new HikariDataSource(config); + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/SessionStorable.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/SessionStorable.java index 727c6f94c..cdcebeb08 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/SessionStorable.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/SessionStorable.java @@ -22,16 +22,17 @@ package io.seata.server.store; */ public interface SessionStorable { - /** - * Encode byte [ ]. - * @return the byte [ ] - */ - byte[] encode(); - - /** - * Decode. - * @param src the src - */ - void decode(byte[] src); + /** + * Encode byte [ ]. + * + * @return the byte [ ] + */ + byte[] encode(); + /** + * Decode. + * + * @param src the src + */ + void decode(byte[] src); } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/StoreConfig.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/StoreConfig.java index 11cfcfa4d..362c7992e 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/StoreConfig.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/StoreConfig.java @@ -15,10 +15,14 @@ */ package io.seata.server.store; +import io.seata.common.util.StringUtils; import io.seata.config.Configuration; import io.seata.config.ConfigurationFactory; +import io.seata.core.constants.ConfigurationKeys; +import io.seata.server.env.ContainerHelper; import io.seata.server.storage.file.FlushDiskMode; +import static io.seata.common.DefaultValues.SERVER_DEFAULT_STORE_MODE; import static io.seata.core.constants.ConfigurationKeys.STORE_FILE_PREFIX; /** @@ -26,37 +30,219 @@ import static io.seata.core.constants.ConfigurationKeys.STORE_FILE_PREFIX; */ public class StoreConfig { - private static final Configuration CONFIGURATION = ConfigurationFactory.getInstance(); + private static final Configuration CONFIGURATION = ConfigurationFactory.getInstance(); + private static StoreMode storeMode; + private static SessionMode sessionMode; + private static LockMode lockMode; - /** - * Default 16kb. - */ - private static final int DEFAULT_MAX_BRANCH_SESSION_SIZE = 1024 * 16; + /** + * 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 512b. - */ - private static final int DEFAULT_MAX_GLOBAL_SESSION_SIZE = 512; + /** + * Default 16kb. + */ + private static final int DEFAULT_MAX_BRANCH_SESSION_SIZE = 1024 * 16; - /** - * Default 16kb. - */ - private static final int DEFAULT_WRITE_BUFFER_SIZE = 1024 * 16; + /** + * Default 512b. + */ + private static final int DEFAULT_MAX_GLOBAL_SESSION_SIZE = 512; - public static int getMaxBranchSessionSize() { - return CONFIGURATION.getInt(STORE_FILE_PREFIX + "maxBranchSessionSize", DEFAULT_MAX_BRANCH_SESSION_SIZE); - } + /** + * Default 16kb. + */ + private static final int DEFAULT_WRITE_BUFFER_SIZE = 1024 * 16; - public static int getMaxGlobalSessionSize() { - return CONFIGURATION.getInt(STORE_FILE_PREFIX + "maxGlobalSessionSize", DEFAULT_MAX_GLOBAL_SESSION_SIZE); - } + public static int getMaxBranchSessionSize() { + return CONFIGURATION.getInt(STORE_FILE_PREFIX + "maxBranchSessionSize", DEFAULT_MAX_BRANCH_SESSION_SIZE); + } - public static int getFileWriteBufferCacheSize() { - return CONFIGURATION.getInt(STORE_FILE_PREFIX + "fileWriteBufferCacheSize", DEFAULT_WRITE_BUFFER_SIZE); - } + public static int getMaxGlobalSessionSize() { + return CONFIGURATION.getInt(STORE_FILE_PREFIX + "maxGlobalSessionSize", DEFAULT_MAX_GLOBAL_SESSION_SIZE); + } - public static FlushDiskMode getFlushDiskMode() { - return FlushDiskMode.findDiskMode(CONFIGURATION.getConfig(STORE_FILE_PREFIX + "flushDiskMode")); - } + 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); + } + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/TransactionStoreManager.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/TransactionStoreManager.java index 3410c0200..2fb6473bc 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/TransactionStoreManager.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/store/TransactionStoreManager.java @@ -15,6 +15,7 @@ */ package io.seata.server.store; +import io.seata.core.model.GlobalStatus; import io.seata.server.session.GlobalSession; import io.seata.server.session.SessionCondition; @@ -27,99 +28,121 @@ import java.util.List; */ public interface TransactionStoreManager { - /** - * Write session boolean. - * @param logOperation the log operation - * @param session the session - * @return the boolean - */ - boolean writeSession(LogOperation logOperation, SessionStorable session); + /** + * 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 global session global session. + * + * @param xid the xid + * @return the global session + */ + GlobalSession readSession(String xid); - /** - * Read session by status list. - * @param sessionCondition the session condition - * @return the list - */ - List readSession(SessionCondition sessionCondition); + /** + * Read session global session. + * + * @param xid the xid + * @param withBranchSessions the withBranchSessions + * @return the global session + */ + GlobalSession readSession(String xid, boolean withBranchSessions); - /** - * Shutdown. - */ - void shutdown(); + /** + * Read session global session by sort by timeout begin status. + * + * @param withBranchSessions the withBranchSessions + * @return the global session + */ + List readSortByTimeoutBeginSessions(boolean withBranchSessions); + /** + * Read session global session. + * + * @param statuses the statuses + * @param withBranchSessions the withBranchSessions + * @return the global session list + */ + List readSession(GlobalStatus[] statuses, boolean withBranchSessions); - /** - * The enum Log operation. - */ - enum LogOperation { + /** + * Read session by status list. + * + * @param sessionCondition the session condition + * @return the list + */ + List readSession(SessionCondition sessionCondition); - /** - * 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); + /** + * Shutdown. + */ + void shutdown(); - private byte code; - LogOperation(byte code) { - this.code = code; - } + /** + * The enum Log operation. + */ + enum LogOperation { - /** - * Gets code. - * @return the code - */ - public byte getCode() { - return this.code; - } + /** + * 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); - /** - * 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 + "]"); - } + 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 + "]"); + } + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/transaction/at/ATCore.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/transaction/at/ATCore.java index bc69a1ab2..b524bfee2 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/transaction/at/ATCore.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/transaction/at/ATCore.java @@ -15,6 +15,9 @@ */ package io.seata.server.transaction.at; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.seata.common.exception.StoreException; +import io.seata.common.util.StringUtils; import io.seata.core.exception.BranchTransactionException; import io.seata.core.exception.TransactionException; import io.seata.core.model.BranchType; @@ -23,6 +26,12 @@ import io.seata.server.coordinator.AbstractCore; import io.seata.server.session.BranchSession; import io.seata.server.session.GlobalSession; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import static io.seata.common.Constants.AUTO_COMMIT; +import static io.seata.common.Constants.SKIP_CHECK_LOCK; import static io.seata.core.exception.TransactionExceptionCode.LockKeyConflict; /** @@ -31,35 +40,64 @@ import static io.seata.core.exception.TransactionExceptionCode.LockKeyConflict; * @author ph3636 */ public class ATCore extends AbstractCore { + + private final ObjectMapper objectMapper = new ObjectMapper(); + + public ATCore(RemotingServer remotingServer) { + super(remotingServer); + } - public ATCore(RemotingServer remotingServer) { - super(remotingServer); - } + @Override + public BranchType getHandleBranchType() { + return BranchType.AT; + } - @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 data = objectMapper.readValue(applicationData, HashMap.class); + Object clientAutoCommit = data.get(AUTO_COMMIT); + if (clientAutoCommit != null && !(boolean)clientAutoCommit) { + autoCommit = (boolean)clientAutoCommit; + } + Object clientSkipCheckLock = data.get(SKIP_CHECK_LOCK); + if (clientSkipCheckLock instanceof Boolean) { + skipCheckLock = (boolean)clientSkipCheckLock; + } + } catch (IOException e) { + LOGGER.error("failed to get application data: {}", e.getMessage(), e); + } + } + try { + if (!branchSession.lock(autoCommit, skipCheckLock)) { + throw new BranchTransactionException(LockKeyConflict, + String.format("Global lock acquire failed xid = %s branchId = %s", globalSession.getXid(), + branchSession.getBranchId())); + } + } catch (StoreException e) { + if (e.getCause() instanceof BranchTransactionException) { + throw new BranchTransactionException(((BranchTransactionException)e.getCause()).getCode(), + String.format("Global lock acquire failed xid = %s branchId = %s", globalSession.getXid(), + branchSession.getBranchId())); + } + throw e; + } + } - @Override - protected void branchSessionLock(GlobalSession globalSession, BranchSession branchSession) - throws TransactionException { - if (!branchSession.lock()) { - throw new BranchTransactionException(LockKeyConflict, - String.format("Global lock acquire failed xid = %s branchId = %s", globalSession.getXid(), - branchSession.getBranchId())); - } - } + @Override + protected void branchSessionUnlock(BranchSession branchSession) throws TransactionException { + branchSession.unlock(); + } - @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); - } + @Override + public boolean lockQuery(BranchType branchType, String resourceId, String xid, String lockKeys) + throws TransactionException { + return lockManager.isLockable(xid, resourceId, lockKeys); + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/transaction/saga/SagaCore.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/transaction/saga/SagaCore.java index 6fa31b097..fb59c7951 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/transaction/saga/SagaCore.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/transaction/saga/SagaCore.java @@ -15,11 +15,6 @@ */ package io.seata.server.transaction.saga; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Map; -import java.util.concurrent.TimeoutException; - import io.netty.channel.Channel; import io.seata.common.util.CollectionUtils; import io.seata.core.exception.GlobalTransactionException; @@ -31,14 +26,18 @@ import io.seata.core.protocol.transaction.BranchCommitRequest; import io.seata.core.protocol.transaction.BranchCommitResponse; import io.seata.core.protocol.transaction.BranchRollbackRequest; import io.seata.core.protocol.transaction.BranchRollbackResponse; -import io.seata.core.rpc.netty.ChannelManager; import io.seata.core.rpc.RemotingServer; +import io.seata.core.rpc.netty.ChannelManager; import io.seata.server.coordinator.AbstractCore; import io.seata.server.session.BranchSession; import io.seata.server.session.GlobalSession; import io.seata.server.session.SessionHelper; import io.seata.server.session.SessionHolder; +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.TimeoutException; + /** * The type saga core. * @@ -46,209 +45,186 @@ import io.seata.server.session.SessionHolder; */ public class SagaCore extends AbstractCore { - public SagaCore(RemotingServer remotingServer) { - super(remotingServer); - } + public SagaCore(RemotingServer remotingServer) { + super(remotingServer); + } - @Override - public BranchType getHandleBranchType() { - return BranchType.SAGA; - } + @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 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 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 branchCommitSend(BranchCommitRequest request, GlobalSession globalSession, + BranchSession branchSession) throws IOException, TimeoutException { + Map 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 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 BranchStatus branchRollbackSend(BranchRollbackRequest request, GlobalSession globalSession, + BranchSession branchSession) throws IOException, TimeoutException { + Map 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())); + @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: - removeAllBranches(globalSession); - LOGGER.info("Successfully committed SAGA global[" + globalSession.getXid() + "]"); - break; - case PhaseTwo_Rollbacked: - LOGGER.info("Successfully rollbacked SAGA global[" + globalSession.getXid() + "]"); - removeAllBranches(globalSession); - SessionHelper.endRollbacked(globalSession); - 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()); - removeAllBranches(globalSession); - globalSession.changeStatus(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); - LOGGER.error("Finally, failed to commit SAGA global[{}]", globalSession.getXid()); - return false; - } - default: - if (!retrying) { - globalSession.queueToRetryCommit(); - return false; - } - else { - LOGGER.error("Failed to commit SAGA global[{}], will retry later.", globalSession.getXid()); - return false; - } - } - } - catch (Exception ex) { - LOGGER.error("Failed to commit global[" + globalSession.getXid() + "]", ex); + 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; - } + 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())); + @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: - removeAllBranches(globalSession); - LOGGER.info("Successfully rollbacked SAGA global[{}]", globalSession.getXid()); - break; - case PhaseTwo_RollbackFailed_Unretryable: - SessionHelper.endRollbackFailed(globalSession); - 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; - } + 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)) { - removeAllBranches(globalSession); - SessionHelper.endCommitted(globalSession); - LOGGER.info("Global[{}] committed", globalSession.getXid()); - } - else if (GlobalStatus.Rollbacked.equals(globalStatus) || GlobalStatus.Finished.equals(globalStatus)) { - removeAllBranches(globalSession); - SessionHelper.endRollbacked(globalSession); - LOGGER.info("Global[{}] rollbacked", globalSession.getXid()); - } - else { - globalSession.changeStatus(globalStatus); - LOGGER.info("Global[{}] reporting is successfully done. status[{}]", globalSession.getXid(), - globalSession.getStatus()); + @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()); - } - } - } - - /** - * remove all branches - * @param globalSession the globalSession - * @throws TransactionException the TransactionException - */ - private void removeAllBranches(GlobalSession globalSession) throws TransactionException { - ArrayList branchSessions = globalSession.getSortedBranches(); - for (BranchSession branchSession : branchSessions) { - globalSession.removeBranch(branchSession); - } - } - - /** - * get saga ResourceId - * @param globalSession the globalSession - * @return sagaResourceId - */ - private String getSagaResourceId(GlobalSession globalSession) { - return globalSession.getApplicationId() + "#" + globalSession.getTransactionServiceGroup(); - } + 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(); + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/transaction/tcc/TccCore.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/transaction/tcc/TccCore.java index 6302c18d4..e0171aa57 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/transaction/tcc/TccCore.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/transaction/tcc/TccCore.java @@ -26,13 +26,12 @@ import io.seata.server.coordinator.AbstractCore; */ public class TccCore extends AbstractCore { - public TccCore(RemotingServer remotingServer) { - super(remotingServer); - } - - @Override - public BranchType getHandleBranchType() { - return BranchType.TCC; - } + public TccCore(RemotingServer remotingServer) { + super(remotingServer); + } + @Override + public BranchType getHandleBranchType() { + return BranchType.TCC; + } } diff --git a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/transaction/xa/XACore.java b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/transaction/xa/XACore.java index 0ed194000..de4620d42 100644 --- a/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/transaction/xa/XACore.java +++ b/pigx-visual/pigx-seata-server/src/main/java/io/seata/server/transaction/xa/XACore.java @@ -28,22 +28,21 @@ import io.seata.server.coordinator.AbstractCore; */ public class XACore extends AbstractCore { - public XACore(RemotingServer remotingServer) { - super(remotingServer); - } + public XACore(RemotingServer remotingServer) { + super(remotingServer); + } - @Override - public BranchType getHandleBranchType() { - return BranchType.XA; - } + @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) { - - } - } + @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) { + } + } } diff --git a/pigx-visual/pigx-seata-server/src/main/resources/META-INF/services/io.seata.core.store.DistributedLocker b/pigx-visual/pigx-seata-server/src/main/resources/META-INF/services/io.seata.core.store.DistributedLocker new file mode 100644 index 000000000..874e8b91c --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/resources/META-INF/services/io.seata.core.store.DistributedLocker @@ -0,0 +1,2 @@ +io.seata.server.storage.redis.lock.RedisDistributedLocker +io.seata.server.storage.db.lock.DataBaseDistributedLocker \ No newline at end of file diff --git a/pigx-visual/pigx-seata-server/src/main/resources/META-INF/spring-configuration-metadata.json b/pigx-visual/pigx-seata-server/src/main/resources/META-INF/spring-configuration-metadata.json new file mode 100644 index 000000000..054491775 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/resources/META-INF/spring-configuration-metadata.json @@ -0,0 +1,22 @@ +{ + "groups": [], + "properties": [ + { + "name": "logging.extend.kafka-appender.bootstrap-servers", + "type": "java.lang.String", + "defaultValue": "localhost:9092" + }, + { + "name": "logging.extend.kafka-appender.topic", + "type": "java.lang.String", + "defaultValue": "logback_to_logstash" + }, + { + "name": "logging.extend.logstash-appender.destination", + "type": "java.lang.String", + "defaultValue": "localhost:4560" + } + ], + "hints": [ + ] +} \ No newline at end of file diff --git a/pigx-visual/pigx-seata-server/src/main/resources/META-INF/spring.factories b/pigx-visual/pigx-seata-server/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000..915bf1b78 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.context.ApplicationListener=\ +io.seata.server.ServerApplicationListener \ No newline at end of file diff --git a/pigx-visual/pigx-seata-server/src/main/resources/README-zh.md b/pigx-visual/pigx-seata-server/src/main/resources/README-zh.md new file mode 100644 index 000000000..05d1620e8 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/resources/README-zh.md @@ -0,0 +1,32 @@ +# 脚本说明 + +## [client](https://github.com/seata/seata/tree/develop/script/client) + +> 存放用于客户端的配置和SQL + +- at: AT模式下的 `undo_log` 建表语句 +- conf: 客户端的配置文件 +- saga: SAGA 模式下所需表的建表语句 +- spring: SpringBoot 应用支持的配置文件 + +## [server](https://github.com/seata/seata/tree/develop/script/server) + +> 存放server侧所需SQL和部署脚本 + +- db: server 侧的保存模式为 `db` 时所需表的建表语句 +- docker-compose: server 侧通过 docker-compose 部署的脚本 +- helm: server 侧通过 Helm 部署的脚本 +- kubernetes: server 侧通过 Kubernetes 部署的脚本 + +## [config-center](https://github.com/seata/seata/tree/develop/script/config-center) + +> 用于存放各种配置中心的初始化脚本,执行时都会读取 `config.txt`配置文件,并写入配置中心 + +- nacos: 用于向 Nacos 中添加配置 +- zk: 用于向 Zookeeper 中添加配置,脚本依赖 Zookeeper 的相关脚本,需要手动下载;ZooKeeper相关的配置可以写在 `zk-params.txt` 中,也可以在执行的时候输入 +- apollo: 向 Apollo 中添加配置,Apollo 的地址端口等可以写在 `apollo-params.txt`,也可以在执行的时候输入 +- etcd3: 用于向 Etcd3 中添加配置 +- consul: 用于向 consul 中添加配置 + +## 打包 +./mvnw -Prelease-seata -Dmaven.test.skip=true clean install -U diff --git a/pigx-visual/pigx-seata-server/src/main/resources/README.md b/pigx-visual/pigx-seata-server/src/main/resources/README.md new file mode 100644 index 000000000..ddabd559c --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/resources/README.md @@ -0,0 +1,33 @@ +# Script Description + +## [client](https://github.com/seata/seata/tree/develop/script/client) + +> Store configuration and SQL for client side + +- at: Script of create table `undo_log` for AT mode. +- conf: Configuration which client need. +- saga: Script of create table in SAGA mode +- spring: Configuration for Spring Boot + +## [server](https://github.com/seata/seata/tree/develop/script/server) + +> Store SQL and deploy script for server side + +- db: Create table script for server when store mode is `db` +- docker-compose: Script for deploy server by docker-compose +- helm: Script for deploy server by Helm +- kubernetes: Script for deploy server by Kubernetes + +## [config-center](https://github.com/seata/seata/tree/develop/script/config-center) + +> Store initialize script for configuration center, will use `config.txt` as configuration when initial + +- nacos: Initialize script for Nacos +- zk: Initialize script for ZooKeeper, the script need related script in Zookeeper, you need download yourself. You can modify `zk-params.txt` to change the ZooKeeper server configuration, or input when execute also +- apollo: Initialize script for Apollo. You can modify `apollo-params.txt` to change the Apollo server configuration, or input when execute also +- etcd3: Initialize script for Etcd3 +- consul: Initialize script for consul + +## build packege +./mvnw -Prelease-seata -Dmaven.test.skip=true clean install -U + diff --git a/pigx-visual/pigx-seata-server/src/main/resources/application.yml b/pigx-visual/pigx-seata-server/src/main/resources/application.yml new file mode 100644 index 000000000..16822c55e --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/resources/application.yml @@ -0,0 +1,54 @@ +# Copyright 1999-2019 Seata.io Group. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +server: + port: 7091 + +spring: + application: + name: seata-server + +logging: + config: classpath:logback-spring.xml + file: + path: ${user.home}/logs/seata + extend: + logstash-appender: + destination: 127.0.0.1:4560 + kafka-appender: + bootstrap-servers: 127.0.0.1:9092 + topic: logback_to_logstash + +console: + user: + username: seata + password: seata + +seata: + config: + # support: nacos, consul, apollo, zk, etcd3 + type: file + registry: + # support: nacos, eureka, redis, zk, consul, etcd3, sofa + type: file + store: + # support: file 、 db 、 redis + mode: file +# server: +# service-port: 8091 #If not configured, the default is '${server.port} + 1000' + security: + secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017 + tokenValidityInMilliseconds: 1800000 + ignore: + urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/api/v1/auth/login \ No newline at end of file diff --git a/pigx-visual/pigx-seata-server/src/main/resources/banner.txt b/pigx-visual/pigx-seata-server/src/main/resources/banner.txt new file mode 100644 index 000000000..e561e763b --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/resources/banner.txt @@ -0,0 +1,7 @@ +███████╗███████╗ █████╗ ████████╗ █████╗ +██╔════╝██╔════╝██╔══██╗╚══██╔══╝██╔══██╗ +███████╗█████╗ ███████║ ██║ ███████║ +╚════██║██╔══╝ ██╔══██║ ██║ ██╔══██║ +███████║███████╗██║ ██║ ██║ ██║ ██║ +╚══════╝╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ + diff --git a/pigx-visual/pigx-seata-server/src/main/resources/file.conf b/pigx-visual/pigx-seata-server/src/main/resources/file.conf deleted file mode 100644 index bf50039f3..000000000 --- a/pigx-visual/pigx-seata-server/src/main/resources/file.conf +++ /dev/null @@ -1,65 +0,0 @@ -## transaction log store, only used in seata-server -store { - ## store mode: file、db、redis - mode = "file" - ## rsa decryption public key - publicKey = "" - ## file store property - file { - ## store location dir - dir = "sessionStore" - # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions - maxBranchSessionSize = 16384 - # globe session size , if exceeded throws exceptions - maxGlobalSessionSize = 512 - # file buffer size , if exceeded allocate new buffer - fileWriteBufferCacheSize = 16384 - # when recover batch read size - sessionReloadReadSize = 100 - # async, sync - flushDiskMode = async - } - - ## database store property - db { - ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc. - datasource = "druid" - ## mysql/oracle/postgresql/h2/oceanbase etc. - dbType = "mysql" - driverClassName = "com.mysql.jdbc.Driver" - ## if using mysql to store the data, recommend add rewriteBatchedStatements=true in jdbc connection param - url = "jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true" - user = "mysql" - password = "mysql" - minConn = 5 - maxConn = 100 - globalTable = "global_table" - branchTable = "branch_table" - lockTable = "lock_table" - queryLimit = 100 - maxWait = 5000 - } - - ## redis store property - redis { - ## redis mode: single、sentinel - mode = "single" - ## single mode property - single { - host = "127.0.0.1" - port = "6379" - } - ## sentinel mode property - sentinel { - masterName = "" - ## such as "10.28.235.65:26379,10.28.235.65:26380,10.28.235.65:26381" - sentinelHosts = "" - } - password = "" - database = "0" - minConn = 1 - maxConn = 10 - maxTotal = 100 - queryLimit = 100 - } -} diff --git a/pigx-visual/pigx-seata-server/src/main/resources/logback.xml b/pigx-visual/pigx-seata-server/src/main/resources/logback-spring.xml similarity index 67% rename from pigx-visual/pigx-seata-server/src/main/resources/logback.xml rename to pigx-visual/pigx-seata-server/src/main/resources/logback-spring.xml index 02399e5dd..7d7c2cf21 100644 --- a/pigx-visual/pigx-seata-server/src/main/resources/logback.xml +++ b/pigx-visual/pigx-seata-server/src/main/resources/logback-spring.xml @@ -19,13 +19,16 @@ - - - - + + + + + + - + + diff --git a/pigx-visual/pigx-seata-server/src/main/resources/logback/console-appender.xml b/pigx-visual/pigx-seata-server/src/main/resources/logback/console-appender.xml index 682b1969a..e18e06823 100644 --- a/pigx-visual/pigx-seata-server/src/main/resources/logback/console-appender.xml +++ b/pigx-visual/pigx-seata-server/src/main/resources/logback/console-appender.xml @@ -1,7 +1,24 @@ + + - + diff --git a/pigx-visual/pigx-seata-server/src/main/resources/logback/file-appender.xml b/pigx-visual/pigx-seata-server/src/main/resources/logback/file-appender.xml index cb54dd4e6..bfd2d0326 100644 --- a/pigx-visual/pigx-seata-server/src/main/resources/logback/file-appender.xml +++ b/pigx-visual/pigx-seata-server/src/main/resources/logback/file-appender.xml @@ -1,15 +1,33 @@ + + - - + + - ${LOG_HOME}/${APPLICATION_NAME:-}.${PORT}.all.log + ${LOG_FILE_PATH}/${APPLICATION_NAME:-seata-server}.${RPC_PORT}.all.log true - ${LOG_HOME}/history/${APPLICATION_NAME:-}.${PORT}.all.%d{yyyy-MM-dd}.%i.log.gz + ${LOG_FILE_PATH}/history/${APPLICATION_NAME:-seata-server}.${RPC_PORT}.all.%d{yyyy-MM-dd}.%i.log.gz 2GB 7 7GB @@ -28,10 +46,10 @@ ACCEPT DENY - ${LOG_HOME}/${APPLICATION_NAME:-}.${PORT}.warn.log + ${LOG_FILE_PATH}/${APPLICATION_NAME:-seata-server}.${RPC_PORT}.warn.log true - ${LOG_HOME}/history/${APPLICATION_NAME:-}.${PORT}.warn.%d{yyyy-MM-dd}.%i.log.gz + ${LOG_FILE_PATH}/history/${APPLICATION_NAME:-seata-server}.${RPC_PORT}.warn.%d{yyyy-MM-dd}.%i.log.gz 2GB 7 7GB @@ -50,10 +68,10 @@ ACCEPT DENY - ${LOG_HOME}/${APPLICATION_NAME:-}.${PORT}.error.log + ${LOG_FILE_PATH}/${APPLICATION_NAME:-seata-server}.${RPC_PORT}.error.log true - ${LOG_HOME}/history/${APPLICATION_NAME:-}.${PORT}.error.%d{yyyy-MM-dd}.%i.log.gz + ${LOG_FILE_PATH}/history/${APPLICATION_NAME:-seata-server}.${RPC_PORT}.error.%d{yyyy-MM-dd}.%i.log.gz 2GB 7 7GB diff --git a/pigx-visual/pigx-seata-server/src/main/resources/logback/kafka-appender.xml b/pigx-visual/pigx-seata-server/src/main/resources/logback/kafka-appender.xml index 2b30a7598..a13000a23 100644 --- a/pigx-visual/pigx-seata-server/src/main/resources/logback/kafka-appender.xml +++ b/pigx-visual/pigx-seata-server/src/main/resources/logback/kafka-appender.xml @@ -1,17 +1,35 @@ + + - - + + - + { "@timestamp": "%d{yyyy-MM-dd HH:mm:ss.SSS}", "level":"%p", - "app_name":"${APPLICATION_NAME:-}", - "PORT": ${PORT:-0}, + "app_name":"${APPLICATION_NAME:-seata-server}", + "PORT": ${RPC_PORT:-0}, "thread_name": "%t", "logger_name": "%logger", "X-TX-XID": "%X{X-TX-XID:-}", diff --git a/pigx-visual/pigx-seata-server/src/main/resources/logback/logstash-appender.xml b/pigx-visual/pigx-seata-server/src/main/resources/logback/logstash-appender.xml index 18b9c25c0..2c2f97215 100644 --- a/pigx-visual/pigx-seata-server/src/main/resources/logback/logstash-appender.xml +++ b/pigx-visual/pigx-seata-server/src/main/resources/logback/logstash-appender.xml @@ -1,7 +1,24 @@ + + - + @@ -12,13 +29,13 @@ { - "app_name": "${APPLICATION_NAME:-}" + "app_name": "${APPLICATION_NAME:-seata-server}" } - + net.logstash.logback.composite.LogstashVersionJsonProvider - + net.logstash.logback.composite.loggingevent.JsonMessageJsonProvider net.logstash.logback.composite.loggingevent.TagsJsonProvider net.logstash.logback.composite.loggingevent.LogstashMarkersJsonProvider diff --git a/pigx-visual/pigx-seata-server/src/main/resources/redislocker/redislock.lua b/pigx-visual/pigx-seata-server/src/main/resources/redislocker/redislock.lua new file mode 100644 index 000000000..b004408c9 --- /dev/null +++ b/pigx-visual/pigx-seata-server/src/main/resources/redislocker/redislock.lua @@ -0,0 +1,52 @@ +-- +-- User: tianyu.li +-- Date: 2021/1/19 +-- +-- init data +local array = {}; local result; local keySize = ARGV[1]; local argSize = ARGV[2]; +-- Loop through all keys to see if they can be used , when a key is not available, exit +for i= 1, keySize do + -- search lock xid + result = redis.call('HGET',KEYS[i],'xid'); + -- if lock xid is nil + if (not result) + -- set 'no' mean There is need to store lock information + then array[i]='no' + else + if (result ~= ARGV[3]) + then + -- return fail + return result + else + -- set 'yes' mean There is not need to store lock information + array[i]= 'yes' + end + end +end +-- Loop through array +for i =1, keySize do + -- if is no ,The lock information is stored + if(array[i] == 'no') + then + -- set xid + redis.call('HSET',KEYS[i],'xid',ARGV[3]); + -- set transactionId + redis.call('HSET',KEYS[i],'transactionId',ARGV[(i-1)*6+4]); + -- set branchId + redis.call('HSET',KEYS[i],'branchId',ARGV[(i-1)*6+5]); + -- set resourceId + redis.call('HSET',KEYS[i],'resourceId',ARGV[(i-1)*6+6]); + -- set tableName + redis.call('HSET',KEYS[i],'tableName',ARGV[(i-1)*6+7]); + -- set rowKey + redis.call('HSET',KEYS[i],'rowKey',ARGV[(i-1)*6+8]); + -- set pk + redis.call('HSET',KEYS[i],'pk',ARGV[(i-1)*6+9]); + -- exit if + end +-- exit for +end +-- set SEATA_GLOBAL_LOCK +redis.call('HSET',KEYS[(keySize+1)],KEYS[(keySize+2)],ARGV[(argSize+0)]); +-- return success +return ARGV[3] diff --git a/pigx-visual/pigx-seata-server/src/main/resources/registry.conf b/pigx-visual/pigx-seata-server/src/main/resources/registry.conf deleted file mode 100644 index 8fb49b27a..000000000 --- a/pigx-visual/pigx-seata-server/src/main/resources/registry.conf +++ /dev/null @@ -1,96 +0,0 @@ -registry { - # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa - type = "file" - - nacos { - application = "seata-server" - serverAddr = "127.0.0.1:8848" - group = "SEATA_GROUP" - namespace = "" - cluster = "default" - username = "" - password = "" - } - eureka { - serviceUrl = "http://localhost:8761/eureka" - application = "default" - weight = "1" - } - redis { - serverAddr = "localhost:6379" - db = 0 - password = "" - cluster = "default" - timeout = 0 - } - zk { - cluster = "default" - serverAddr = "127.0.0.1:2181" - sessionTimeout = 6000 - connectTimeout = 2000 - username = "" - password = "" - } - consul { - cluster = "default" - serverAddr = "127.0.0.1:8500" - aclToken = "" - } - etcd3 { - cluster = "default" - serverAddr = "http://localhost:2379" - } - sofa { - serverAddr = "127.0.0.1:9603" - application = "default" - region = "DEFAULT_ZONE" - datacenter = "DefaultDataCenter" - cluster = "default" - group = "SEATA_GROUP" - addressWaitTime = "3000" - } - file { - name = "file.conf" - } -} - -config { - # file、nacos 、apollo、zk、consul、etcd3 - type = "file" - - nacos { - serverAddr = "127.0.0.1:8848" - namespace = "" - group = "SEATA_GROUP" - username = "" - password = "" - dataId = "seataServer.properties" - } - consul { - serverAddr = "127.0.0.1:8500" - aclToken = "" - } - apollo { - appId = "seata-server" - ## apolloConfigService will cover apolloMeta - apolloMeta = "http://192.168.1.204:8801" - apolloConfigService = "http://192.168.1.204:8080" - namespace = "application" - apolloAccesskeySecret = "" - cluster = "seata" - } - zk { - serverAddr = "127.0.0.1:2181" - sessionTimeout = 6000 - connectTimeout = 2000 - username = "" - password = "" - nodePath = "/seata/seata.properties" - } - etcd3 { - serverAddr = "http://localhost:2379" - } - file { - name = "file.conf" - } -}