chore(project): 添加项目配置文件和忽略规则

- 添加 Babel 配置文件支持 ES6+ 语法转换
- 添加 ESLint 忽略规则和配置文件
- 添加 Git 忽略规则文件
- 添加 Travis CI 配置文件
- 添加 1.4.2 版本变更日志文件
- 添加 Helm 图表辅助模板文件
- 添加 Helm 忽略规则文件
This commit is contained in:
2026-03-27 17:36:48 +08:00
commit c2453d6434
1703 changed files with 277582 additions and 0 deletions

133
test/pom.xml Normal file
View File

@@ -0,0 +1,133 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 1999-2019 Seata.io Group.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>io.seata</groupId>
<artifactId>seata-parent</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>seata-test</artifactId>
<packaging>jar</packaging>
<name>seata-test ${project.version}</name>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>seata-tm</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>seata-server</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>seata-rm-datasource</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>seata-saga-engine-store</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>seata-spring</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<exclusions>
<exclusion>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
<exclusion>
<artifactId>log4j</artifactId>
<groupId>log4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>seata-serializer-seata</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,132 @@
/*
* 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.
*/
import io.seata.common.ApplicationKeeper;
import io.seata.rm.RMClient;
import io.seata.tm.TMClient;
import io.seata.tm.api.TransactionalExecutor;
import io.seata.tm.api.TransactionalTemplate;
import io.seata.tm.api.transaction.TransactionInfo;
import org.junit.jupiter.api.Assertions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* The type App test.
*
* @author sharajava
*/
public class AppTest {
private static final Logger LOGGER = LoggerFactory.getLogger(AppTest.class);
private static final String APPLICATION_ID = "my_test_app";
private static final String TX_SERVICE_GROUP = "my_test_tx_group";
private static final String TX_NAME = "my_tx_instance";
private static final int TX_TIME_OUT = 60000;
/**
* The entry point of application.
*
* @param args the input arguments
*/
public static void main(String[] args) throws Throwable {
TMClient.init(APPLICATION_ID, TX_SERVICE_GROUP);
RMClient.init(APPLICATION_ID, TX_SERVICE_GROUP);
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"basic-test-context.xml");
final JdbcTemplate jdbcTemplate = (JdbcTemplate) context
.getBean("jdbcTemplate");
jdbcTemplate.update("delete from undo_log");
jdbcTemplate.update("delete from user0");
jdbcTemplate.update("insert into user0 (id, name, gmt) values (1, 'user0', '2019-01-01')");
jdbcTemplate.update("delete from user1");
final MyBusinessException bizException = new MyBusinessException("mock bizException");
TransactionalTemplate transactionalTemplate = new TransactionalTemplate();
try {
transactionalTemplate.execute(new TransactionalExecutor() {
@Override
public Object execute() throws Throwable {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Exception Rollback Business Begin ...");
}
jdbcTemplate.update("update user0 set name = 'xxx' where id = ?", new Object[]{1});
jdbcTemplate.update("insert into user1 (id, name, gmt) values (1, 'user1', '2019-01-01')");
throw bizException;
}
@Override
public TransactionInfo getTransactionInfo() {
TransactionInfo txInfo = new TransactionInfo();
txInfo.setTimeOut(TX_TIME_OUT);
txInfo.setName(TX_NAME);
return txInfo;
}
});
} catch (TransactionalExecutor.ExecutionException e) {
TransactionalExecutor.Code code = e.getCode();
if (code == TransactionalExecutor.Code.RollbackDone) {
Throwable businessEx = e.getOriginalException();
if (businessEx instanceof MyBusinessException) {
Assertions.assertEquals(((MyBusinessException) businessEx).getBusinessErrorCode(),
bizException.businessErrorCode);
}
} else {
Assertions.assertFalse(false, "Not expected," + e.getMessage());
}
}
new ApplicationKeeper(context).keep();
}
private static class MyBusinessException extends Exception {
private String businessErrorCode;
/**
* Gets business error code.
*
* @return the business error code
*/
public String getBusinessErrorCode() {
return businessErrorCode;
}
/**
* Sets business error code.
*
* @param businessErrorCode the business error code
*/
public void setBusinessErrorCode(String businessErrorCode) {
this.businessErrorCode = businessErrorCode;
}
/**
* Instantiates a new My business exception.
*
* @param businessErrorCode the business error code
*/
public MyBusinessException(String businessErrorCode) {
this.businessErrorCode = businessErrorCode;
}
}
}

View File

@@ -0,0 +1,283 @@
/*
* 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.
*/
import io.seata.core.context.RootContext;
import io.seata.core.exception.TransactionException;
import io.seata.core.model.BranchStatus;
import io.seata.core.model.BranchType;
import io.seata.core.model.Resource;
import io.seata.rm.DefaultResourceManager;
import io.seata.rm.RMClient;
import io.seata.rm.datasource.DataSourceManager;
import io.seata.tm.TMClient;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.UncategorizedSQLException;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.Date;
/**
* The type Data source basic test.
*
* @author services
*/
@Disabled
public class LocalTransactionWithGlobalLockDataSourceBasicTest {
private static ClassPathXmlApplicationContext context;
private static JdbcTemplate jdbcTemplate;
private static JdbcTemplate directJdbcTemplate;
private static final String APPLICATION_ID = "my_test_app";
private static final String TX_SERVICE_GROUP = "my_test_tx_group";
/**
* Before.
*/
@BeforeAll
public static void before() {
// Mock DataSourceManager
initClient();
DefaultResourceManager.mockResourceManager(BranchType.AT, new MockDataSourceManager());
context = new ClassPathXmlApplicationContext(
"basic-test-context.xml");
jdbcTemplate = (JdbcTemplate) context
.getBean("jdbcTemplate");
directJdbcTemplate = (JdbcTemplate) context
.getBean("directJdbcTemplate");
directJdbcTemplate.execute("delete from user0");
directJdbcTemplate.execute("delete from user1");
}
/**
* Test insert.
*/
@Test
public void testInsert() {
RootContext.bindGlobalLockFlag();
jdbcTemplate.update("insert into user0 (id, name, gmt) values (?, ?, ?)",
new Object[]{2, "xxx", new Date()});
}
/**
* Test insert.
*/
@Test
public void testInsertWithLock() {
RootContext.bindGlobalLockFlag();
new AbstractLockConflictExecuteTemplate() {
@Override
public void doExecute() {
jdbcTemplate.update("insert into user0 (id, name, gmt) values (?, ?, ?)",
new Object[]{3, "xxx", new Date()});
}
}.execute();
}
/**
* Test update.
*/
@Test
public void testUpdate() {
RootContext.bindGlobalLockFlag();
jdbcTemplate.update("update user0 a set a.name = 'yyyy' where a.id = ?", new Object[]{1});
}
@Test
public void testUpdateWithLock() {
RootContext.bindGlobalLockFlag();
RootContext.bindGlobalLockFlag();
new AbstractLockConflictExecuteTemplate() {
@Override
public void doExecute() {
jdbcTemplate.update("update user0 a set a.name = 'yyyy' where a.id = ?", new Object[]{1});
}
}.execute();
}
/**
* Test update with alias 1.
*/
@Test
public void testUpdateWithAlias1() {
directJdbcTemplate.update("delete from User1 where Id = ?",
new Object[]{1});
directJdbcTemplate.update("insert into User1 (Id, Name, gMt) values (?, ?, ?)",
new Object[]{1, "xxx", new Date()});
RootContext.bindGlobalLockFlag();
jdbcTemplate.update("update User1 a set a.Name = 'yyy' where a.Name = ?", new Object[]{"xxx"});
}
@Test
public void testUpdateWithAlias1WithLockConflict() {
directJdbcTemplate.update("delete from User1 where Id = ?",
new Object[]{1});
directJdbcTemplate.update("insert into User1 (Id, Name, gMt) values (?, ?, ?)",
new Object[]{1, "xxx", new Date()});
RootContext.bindGlobalLockFlag();
new AbstractLockConflictExecuteTemplate() {
@Override
public void doExecute() {
jdbcTemplate.update("update User1 a set a.Name = 'yyy' where a.Name = ?", new Object[]{"xxx"});
}
};
}
/**
* Test delete.
*/
@Test
public void testDelete() {
RootContext.bindGlobalLockFlag();
jdbcTemplate.update("delete from user0 where id = ?", new Object[]{2});
}
/**
* Test delete.
*/
@Test
public void testDeleteForLockConflict() {
RootContext.bindGlobalLockFlag();
new AbstractLockConflictExecuteTemplate() {
@Override
public void doExecute() {
jdbcTemplate.update("delete from user0 where id = ?", new Object[]{2});
}
};
}
/**
* Test select for update.
*/
@Test
public void testSelectForUpdate() {
RootContext.bindGlobalLockFlag();
jdbcTemplate.queryForRowSet("select a.name from user0 a where a.id = ? for update", new Object[]{1});
}
/**
* Test select for update.
*/
@Test
public void testSelectForUpdateWithLockConflict() {
RootContext.bindGlobalLockFlag();
new AbstractLockConflictExecuteTemplate() {
@Override
public void doExecute() {
jdbcTemplate.queryForRowSet("select a.name from user0 a where a.id = ? for update", new Object[]{1});
}
};
}
public static class MockDataSourceManager extends DataSourceManager {
@Override
public Long branchRegister(BranchType branchType, String resourceId, String clientId, String xid, String applicationData, String lockKeys)
throws TransactionException {
throw new RuntimeException("this method should not be called!");
}
@Override
public void branchReport(BranchType branchType, String xid, long branchId, BranchStatus status, String applicationData) throws TransactionException {
throw new RuntimeException("this method should not be called!");
}
@Override
public boolean lockQuery(BranchType branchType, String resourceId, String xid, String lockKeys)
throws TransactionException {
return true;
}
@Override
public void registerResource(Resource resource) {
}
@Override
public void unregisterResource(Resource resource) {
}
@Override
public BranchStatus branchCommit(BranchType branchType, String xid, long branchId, String resourceId, String applicationData) throws TransactionException {
throw new RuntimeException("this method should not be called!");
}
@Override
public BranchStatus branchRollback(BranchType branchType, String xid, long branchId, String resourceId, String applicationData)
throws TransactionException {
throw new RuntimeException("this method should not be called!");
}
}
private static void initClient() {
TMClient.init(APPLICATION_ID, TX_SERVICE_GROUP);
RMClient.init(APPLICATION_ID, TX_SERVICE_GROUP);
}
private abstract static class AbstractLockConflictExecuteTemplate {
public void execute() {
synchronized (LocalTransactionWithGlobalLockDataSourceBasicTest.class) {
DefaultResourceManager.mockResourceManager(BranchType.AT, new MockDataSourceManager() {
@Override
public boolean lockQuery(BranchType branchType, String resourceId, String xid, String lockKeys)
throws TransactionException {
return false;
}
});
boolean exceptionOccour = false;
try {
doExecute();
} catch (UncategorizedSQLException e) {
exceptionOccour = true;
Assertions.assertTrue(e.getMessage().contains("LockConflict"), "not lock Conflict exception");
} finally {
DefaultResourceManager.mockResourceManager(BranchType.AT, new MockDataSourceManager());
}
Assertions.assertTrue(exceptionOccour, "Lock Exception not occur!");
}
}
public abstract void doExecute();
}
/**
* After.
*/
@AfterAll
public static void after() {
if (context != null) {
context.close();
}
}
}

View File

@@ -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.common;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.AbstractApplicationContext;
/**
* The type Application keeper.
*
* @author sharajava
*/
public class ApplicationKeeper {
private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationKeeper.class);
private final ReentrantLock LOCK = new ReentrantLock();
private final Condition STOP = LOCK.newCondition();
/**
* Instantiates a new Application keeper.
*
* @param applicationContext the application context
*/
public ApplicationKeeper(AbstractApplicationContext applicationContext) {
addShutdownHook(applicationContext);
}
private void addShutdownHook(final AbstractApplicationContext applicationContext) {
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
try {
applicationContext.close();
LOGGER.info("ApplicationContext " + applicationContext + " is closed.");
} catch (Exception e) {
LOGGER.error("Failed to close ApplicationContext", e);
}
try {
LOCK.lock();
STOP.signal();
} finally {
LOCK.unlock();
}
}
}));
}
/**
* Keep.
*/
public void keep() {
LOCK.lock();
try {
LOGGER.info("Application is keep running ... ");
STOP.await();
} catch (InterruptedException e) {
LOGGER.error("interrupted error ", e);
} finally {
LOCK.unlock();
}
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.common.loader;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.List;
/**
* test EnhancedServiceLoader
* @author zhangsen
*/
public class EnhancedServiceLoaderTest {
@Test
public void testLoadBeanByOrder(){
LoaderTestSPI loader = EnhancedServiceLoader.load(LoaderTestSPI.class, EnhancedServiceLoaderTest.class.getClassLoader());
System.out.println(loader.echo());
Assertions.assertEquals("impl_2", loader.echo());
}
@Test
public void testLoadAll(){
List<LoaderTestSPI> list = EnhancedServiceLoader.loadAll(LoaderTestSPI.class);
Assertions.assertEquals(2, list.size());
}
}

View File

@@ -0,0 +1,27 @@
/*
* 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.common.loader;
/**
* @author zhangsen
*/
@LoadLevel(name = "one", order = 1)
public class LoaderTestImpl1 implements LoaderTestSPI {
@Override
public String echo() {
return "impl_1";
}
}

View File

@@ -0,0 +1,27 @@
/*
* 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.common.loader;
/**
* @author zhangsen
*/
@LoadLevel(name = "two", order = 2)
public class LoaderTestImpl2 implements LoaderTestSPI {
@Override
public String echo() {
return "impl_2";
}
}

View File

@@ -0,0 +1,24 @@
/*
* 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.common.loader;
/**
* @author zhangsen
*/
public interface LoaderTestSPI {
String echo();
}

View File

@@ -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.core.rpc.netty;
import java.lang.reflect.Method;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import io.netty.channel.Channel;
import io.seata.core.protocol.ResultCode;
import io.seata.core.protocol.transaction.BranchRegisterRequest;
import io.seata.core.protocol.transaction.BranchRegisterResponse;
import io.seata.server.UUIDGenerator;
import io.seata.server.coordinator.DefaultCoordinator;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
/**
* @author slievrly
*/
@Disabled
public class TmNettyClientTest {
private static final ThreadPoolExecutor
workingThreads = new ThreadPoolExecutor(100, 500, 500, TimeUnit.SECONDS,
new LinkedBlockingQueue(20000), new ThreadPoolExecutor.CallerRunsPolicy());
/**
* Client rely on server's starting first
*
* @throws Exception
*/
@Test
public void testDoConnect() throws Exception {
//start services server first
workingThreads.submit(new Runnable() {
@Override
public void run() {
NettyRemotingServer nettyRemotingServer = new NettyRemotingServer(workingThreads);
nettyRemotingServer.setHandler(new DefaultCoordinator(nettyRemotingServer));
UUIDGenerator.init(1L);
nettyRemotingServer.init();
}
});
//then test client
Thread.sleep(3000);
String applicationId = "app 1";
String transactionServiceGroup = "group A";
TmNettyRemotingClient tmNettyRemotingClient = TmNettyRemotingClient.getInstance(applicationId, transactionServiceGroup);
tmNettyRemotingClient.init();
Method doConnectMethod = TmNettyRemotingClient.class.getDeclaredMethod("doConnect", String.class);
doConnectMethod.setAccessible(true);
String serverAddress = "0.0.0.0:8091";
Channel channel = (Channel) doConnectMethod.invoke(tmNettyRemotingClient, serverAddress);
Assertions.assertNotNull(channel);
}
/**
* Client rely on server's starting first
*
* @throws Exception
*/
@Test
public void testReconnect() throws Exception {
//start services server first
workingThreads.submit(new Runnable() {
@Override
public void run() {
NettyRemotingServer nettyRemotingServer = new NettyRemotingServer(workingThreads);
nettyRemotingServer.setHandler(new DefaultCoordinator(nettyRemotingServer));
UUIDGenerator.init(1L);
nettyRemotingServer.init();
}
});
//then test client
Thread.sleep(3000);
String applicationId = "app 1";
String transactionServiceGroup = "my_test_tx_group";
TmNettyRemotingClient tmNettyRemotingClient = TmNettyRemotingClient.getInstance(applicationId, transactionServiceGroup);
tmNettyRemotingClient.init();
Method doConnectMethod = TmNettyRemotingClient.class.getDeclaredMethod("reconnect");
doConnectMethod.setAccessible(true);
doConnectMethod.invoke(tmNettyRemotingClient);
}
@Test
public void testSendMsgWithResponse() throws Exception {
workingThreads.submit(new Runnable() {
@Override
public void run() {
NettyRemotingServer nettyRemotingServer = new NettyRemotingServer(workingThreads);
nettyRemotingServer.setHandler(new DefaultCoordinator(nettyRemotingServer));
UUIDGenerator.init(1L);
nettyRemotingServer.init();
}
});
Thread.sleep(3000);
String applicationId = "app 1";
String transactionServiceGroup = "my_test_tx_group";
TmNettyRemotingClient tmNettyRemotingClient = TmNettyRemotingClient.getInstance(applicationId, transactionServiceGroup);
tmNettyRemotingClient.init();
Method doConnectMethod = TmNettyRemotingClient.class.getDeclaredMethod("doConnect", String.class);
doConnectMethod.setAccessible(true);
String serverAddress = "0.0.0.0:8091";
Channel channel = (Channel) doConnectMethod.invoke(tmNettyRemotingClient, serverAddress);
Assertions.assertNotNull(channel);
BranchRegisterRequest request = new BranchRegisterRequest();
request.setXid("127.0.0.1:8091:1249853");
request.setLockKey("lock key testSendMsgWithResponse");
request.setResourceId("resoutceId1");
BranchRegisterResponse branchRegisterResponse = (BranchRegisterResponse) tmNettyRemotingClient.sendSyncRequest(request);
Assertions.assertNotNull(branchRegisterResponse);
Assertions.assertEquals(ResultCode.Failed, branchRegisterResponse.getResultCode());
Assertions.assertEquals("RuntimeException[SessionManager is NOT init!]",
branchRegisterResponse.getMsg());
}
}

View File

@@ -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.core.rpc.netty.v1;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.concurrent.DefaultPromise;
import io.seata.core.protocol.RpcMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Geng Zhang
*/
public class ClientChannelHandler extends ChannelInboundHandlerAdapter {
/**
* Logger for ClientChannelHandler
**/
private static final Logger LOGGER = LoggerFactory.getLogger(ClientChannelHandler.class);
private ProtocolV1Client client;
public ClientChannelHandler(ProtocolV1Client client) {
this.client = client;
}
public void channelActive(final ChannelHandlerContext ctx) throws Exception {
LOGGER.info("Channel active: {}", ctx.channel());
}
@Override
public void channelInactive(final ChannelHandlerContext ctx) throws Exception {
LOGGER.info("Channel inactive: {}", ctx.channel());
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof RpcMessage) {
RpcMessage rpcMessage = (RpcMessage) msg;
int msgId = rpcMessage.getId();
DefaultPromise future = (DefaultPromise) client.futureMap.remove(msgId);
if (future != null) {
future.setSuccess(msg);
} else {
LOGGER.warn("miss msg id:{}", msgId);
}
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, final Throwable cause) throws Exception {
LOGGER.warn("", cause);
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright 1999-2019 Seata.io Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.seata.core.rpc.netty.v1;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
/**
* @author Geng Zhang
*/
class HeadMapSerializerTest {
@Test
public void encode() throws Exception {
HeadMapSerializer simpleMapSerializer = HeadMapSerializer.getInstance();
Map<String, String> map = null;
int bs = simpleMapSerializer.encode(map, null);
Assertions.assertEquals(bs, 0);
ByteBuf byteBuf = ByteBufAllocator.DEFAULT.heapBuffer();
bs = simpleMapSerializer.encode(map, byteBuf);
Assertions.assertEquals(bs, 0);
map = new HashMap<String, String>();
bs = simpleMapSerializer.encode(map, byteBuf);
Assertions.assertEquals(bs, 0);
map.put("1", "2");
map.put("", "x");
map.put("a", "");
map.put("b", null);
bs = simpleMapSerializer.encode(map, byteBuf);
Assertions.assertEquals(21, bs);
Map<String, String> map1 = simpleMapSerializer.decode(byteBuf, 21);
Assertions.assertNotNull(map1);
Assertions.assertEquals(4, map1.size());
Assertions.assertEquals("2", map1.get("1"));
Assertions.assertEquals("x", map1.get(""));
Assertions.assertEquals("", map1.get("a"));
Assertions.assertEquals(null, map1.get("b"));
map1 = simpleMapSerializer.decode(byteBuf, 21);
Assertions.assertNotNull(map1);
Assertions.assertEquals(0, map1.size());
map1 = simpleMapSerializer.decode(null, 21);
Assertions.assertNotNull(map1);
Assertions.assertEquals(0, map1.size());
byteBuf.release();
}
@Test
public void testUTF8() throws Exception {
HeadMapSerializer mapSerializer = HeadMapSerializer.getInstance();
String s = "test";
// utf-8 and gbk same in English
Assertions.assertArrayEquals(s.getBytes(StandardCharsets.UTF_8), s.getBytes("GBK"));
Map<String, String> map = new HashMap<String, String>();
map.put("11", "22");
map.put("222", "333");
ByteBuf byteBuf = ByteBufAllocator.DEFAULT.heapBuffer();
int bs = mapSerializer.encode(map, byteBuf);
Map newmap = mapSerializer.decode(byteBuf, bs);
Assertions.assertEquals(map, newmap);
// support chinese
map.put("你好", "你好?");
bs = mapSerializer.encode(map, byteBuf);
newmap = mapSerializer.decode(byteBuf, bs);
Assertions.assertEquals(map, newmap);
byteBuf.release();
}
}

View File

@@ -0,0 +1,197 @@
/*
* 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.core.rpc.netty.v1;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.AdaptiveRecvByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.concurrent.DefaultEventExecutor;
import io.netty.util.concurrent.DefaultPromise;
import io.seata.common.thread.NamedThreadFactory;
import io.seata.common.thread.PositiveAtomicCounter;
import io.seata.core.serializer.SerializerType;
import io.seata.core.model.BranchType;
import io.seata.core.protocol.ProtocolConstants;
import io.seata.core.protocol.RpcMessage;
import io.seata.core.protocol.transaction.BranchCommitRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
/**
* @author Geng Zhang
*/
public class ProtocolV1Client {
/**
* Logger for ProtocolV1Client
**/
private static final Logger LOGGER = LoggerFactory.getLogger(ProtocolV1Client.class);
Channel channel;
PositiveAtomicCounter idGenerator = new PositiveAtomicCounter();
Map<Integer, Future> futureMap = new ConcurrentHashMap<>();
private EventLoopGroup eventLoopGroup = createWorkerGroup();
private DefaultEventExecutor defaultEventExecutor = new DefaultEventExecutor(eventLoopGroup);
public void connect(String host, int port, int connectTimeout) {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class);
bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
bootstrap.option(ChannelOption.ALLOCATOR, ByteBufAllocator.DEFAULT);
bootstrap.option(ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK, 32 * 1024);
bootstrap.option(ChannelOption.WRITE_BUFFER_LOW_WATER_MARK, 8 * 1024);
bootstrap.option(ChannelOption.RCVBUF_ALLOCATOR, AdaptiveRecvByteBufAllocator.DEFAULT);
bootstrap.handler(new ChannelInitializer() {
@Override
protected void initChannel(Channel channel) throws Exception {
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast(new ProtocolV1Encoder());
pipeline.addLast(new ProtocolV1Decoder(8 * 1024 * 1024));
pipeline.addLast(new ClientChannelHandler(ProtocolV1Client.this));
}
});
// Bind and start to accept incoming connections.
ChannelFuture channelFuture = bootstrap.connect(host, port);
channelFuture.awaitUninterruptibly(connectTimeout, TimeUnit.MILLISECONDS);
if (channelFuture.isSuccess()) {
channel = channelFuture.channel();
} else {
Throwable cause = channelFuture.cause();
throw new RuntimeException("Failed to connect " + host + ":" + port +
(cause != null ? ". Cause by: " + cause.getMessage() : "."));
}
}
private EventLoopGroup createWorkerGroup() {
NamedThreadFactory threadName =
new NamedThreadFactory("CLI-WORKER", false);
return new NioEventLoopGroup(10, threadName);
}
public void close() {
if (channel != null) {
channel.close();
}
if (eventLoopGroup != null) {
eventLoopGroup.shutdownGracefully();
}
channel = null;
}
public Future sendRpc(Map<String, String> head, Object body) {
int msgId = idGenerator.incrementAndGet();
RpcMessage rpcMessage = new RpcMessage();
rpcMessage.setId(msgId);
rpcMessage.setCodec(SerializerType.SEATA.getCode());
rpcMessage.setCompressor(ProtocolConstants.CONFIGURED_COMPRESSOR);
rpcMessage.setHeadMap(head);
rpcMessage.setBody(body);
rpcMessage.setMessageType(ProtocolConstants.MSGTYPE_RESQUEST_SYNC);
if (channel != null) {
DefaultPromise promise = new DefaultPromise(defaultEventExecutor);
futureMap.put(msgId, promise);
channel.writeAndFlush(rpcMessage);
return promise;
} else {
LOGGER.warn("channel is null");
}
return null;
}
// can test tps
public static void main(String[] args) {
ProtocolV1Client client = new ProtocolV1Client();
client.connect("127.0.0.1", 8811, 500);
Map<String, String> head = new HashMap<>();
head.put("tracerId", "xxadadadada");
head.put("token", "adadadad");
BranchCommitRequest body = new BranchCommitRequest();
body.setBranchId(12345L);
body.setApplicationData("application");
body.setBranchType(BranchType.AT);
body.setResourceId("resource-1234");
body.setXid("xid-1234");
final int threads = 50;
final AtomicLong cnt = new AtomicLong(0);
// no queue
final ThreadPoolExecutor service1 = new ThreadPoolExecutor(threads, threads, 0L, TimeUnit.MILLISECONDS,
new SynchronousQueue<Runnable>(), new NamedThreadFactory("client-", false));
for (int i = 0; i < threads; i++) {
service1.execute(() -> {
while (true) {
try {
Future future = client.sendRpc(head, body);
RpcMessage resp = (RpcMessage) future.get(200, TimeUnit.MILLISECONDS);
if (resp != null) {
cnt.incrementAndGet();
}
} catch (Exception e) {
// ignore
}
}
});
}
Thread thread = new Thread(new Runnable() {
private long last = 0;
@Override
public void run() {
while (true) {
long count = cnt.get();
long tps = count - last;
LOGGER.error("last 1s invoke: {}, queue: {}", tps, service1.getQueue().size());
last = count;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
}
}, "Print-tps-THREAD");
thread.start();
}
}

View File

@@ -0,0 +1,106 @@
/*
* 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.core.rpc.netty.v1;
import io.seata.common.thread.NamedThreadFactory;
import io.seata.core.model.BranchType;
import io.seata.core.protocol.RpcMessage;
import io.seata.core.protocol.transaction.BranchCommitRequest;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author <a href="mailto:zhanggeng.zg@antfin.com">GengZhang</a>
*/
public class ProtocolV1SerializerTest {
/**
* Logger for ProtocolV1CodecTest
**/
private static final Logger LOGGER = LoggerFactory.getLogger(ProtocolV1SerializerTest.class);
@Test
public void testAll() {
ProtocolV1Server server = new ProtocolV1Server();
ProtocolV1Client client = new ProtocolV1Client();
try {
server.start();
client.connect("127.0.0.1", 8811, 500);
Assertions.assertTrue(client.channel.isActive());
Map<String, String> head = new HashMap<>();
head.put("tracerId", "xxadadadada");
head.put("token", "adadadad");
head.put("hello", null);
BranchCommitRequest body = new BranchCommitRequest();
body.setBranchId(12345L);
body.setApplicationData("application");
body.setBranchType(BranchType.AT);
body.setResourceId("resource-1234");
body.setXid("xid-1234");
// test run times
int runTimes = 100000;
final int threads = 50;
final CountDownLatch cnt = new CountDownLatch(runTimes);
final AtomicInteger tag = new AtomicInteger(0);
final AtomicInteger success = new AtomicInteger(0);
// no queue
final ThreadPoolExecutor service1 = new ThreadPoolExecutor(threads, threads, 0L, TimeUnit.MILLISECONDS,
new SynchronousQueue<>(), new NamedThreadFactory("client-", false));
for (int i = 0; i < threads; i++) {
service1.execute(() -> {
while (tag.getAndIncrement() < runTimes) {
try {
Future future = client.sendRpc(head, body);
RpcMessage resp = (RpcMessage) future.get(10, TimeUnit.SECONDS);
if (resp != null) {
success.incrementAndGet();
}
} catch (Exception e) {
LOGGER.error("Client send error", e);
} finally {
cnt.countDown();
}
}
});
}
cnt.await();
LOGGER.info("success {}/{}", success.get(), runTimes);
Assertions.assertEquals(success.get(), runTimes);
} catch (InterruptedException e) {
LOGGER.error("Thread interrupted", e);
} finally {
client.close();
server.stop();
}
}
}

View File

@@ -0,0 +1,122 @@
/*
* 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.core.rpc.netty.v1;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.AdaptiveRecvByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.WriteBufferWaterMark;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.seata.common.thread.NamedThreadFactory;
import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;
/**
* @author Geng Zhang
*/
public class ProtocolV1Server {
private int port = 8811;
private ServerBootstrap serverBootstrap;
private EventLoopGroup bossGroup;
private EventLoopGroup workerGroup;
public void start() {
bossGroup = createBossGroup();
workerGroup = createWorkerGroup();
serverBootstrap = new ServerBootstrap().group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.RCVBUF_ALLOCATOR, AdaptiveRecvByteBufAllocator.DEFAULT)
.option(ChannelOption.ALLOCATOR, ByteBufAllocator.DEFAULT)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childOption(ChannelOption.TCP_NODELAY, true)
.childOption(ChannelOption.SO_RCVBUF, 8192 * 128)
.childOption(ChannelOption.SO_SNDBUF, 8192 * 128)
.handler(new LoggingHandler(LogLevel.DEBUG))
.childOption(ChannelOption.ALLOCATOR, ByteBufAllocator.DEFAULT)
.childOption(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(
8192, 31768))
.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(Channel channel) throws Exception {
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast(new ProtocolV1Decoder(8 * 1024 * 1024));
pipeline.addLast(new ProtocolV1Encoder());
pipeline.addLast(new ServerChannelHandler());
}
});
String host = "0.0.0.0";
ChannelFuture future = serverBootstrap.bind(new InetSocketAddress(host, port));
ChannelFuture channelFuture = future.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
throw new RuntimeException("Server start fail !", future.cause());
}
}
});
try {
channelFuture.await(5000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void stop() {
if (bossGroup != null) {
bossGroup.shutdownGracefully();
}
if (workerGroup != null) {
workerGroup.shutdownGracefully();
}
serverBootstrap = null;
}
private EventLoopGroup createBossGroup() {
NamedThreadFactory threadName =
new NamedThreadFactory("SEV-BOSS-" + port, false);
return new NioEventLoopGroup(2, threadName);
}
private EventLoopGroup createWorkerGroup() {
NamedThreadFactory threadName =
new NamedThreadFactory("SEV-WORKER-" + port, false);
return new NioEventLoopGroup(10, threadName);
}
public static void main(String[] args) {
ProtocolV1Server server = new ProtocolV1Server();
server.start();
}
}

View File

@@ -0,0 +1,60 @@
/*
* 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.core.rpc.netty.v1;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.seata.core.protocol.ProtocolConstants;
import io.seata.core.protocol.RpcMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Geng Zhang
*/
@ChannelHandler.Sharable
public class ServerChannelHandler extends ChannelInboundHandlerAdapter {
/**
* Logger for ServerChannelHandler
**/
private static final Logger LOGGER = LoggerFactory.getLogger(ServerChannelHandler.class);
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
Channel channel = ctx.channel();
if (msg instanceof RpcMessage) {
((RpcMessage) msg).setMessageType(ProtocolConstants.MSGTYPE_RESPONSE);
}
channel.writeAndFlush(msg);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
LOGGER.info(ctx.channel().remoteAddress() + " connected!");
ctx.fireChannelActive();
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
LOGGER.info(ctx.channel().remoteAddress() + " disconnected!");
ctx.fireChannelInactive();
}
}

View File

@@ -0,0 +1,303 @@
/*
* 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.saga.engine;
import io.seata.saga.engine.mock.DemoService.People;
import io.seata.saga.proctrl.ProcessContext;
import io.seata.saga.statelang.domain.ExecutionStatus;
import io.seata.saga.statelang.domain.StateMachineInstance;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.HashMap;
import java.util.Map;
/**
* State machine async tests
* @author lorne.cl
*/
public class StateMachineAsyncTests {
private static StateMachineEngine stateMachineEngine;
@BeforeAll
public static void initApplicationContext() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:saga/spring/statemachine_engine_test.xml");
stateMachineEngine = applicationContext.getBean("stateMachineEngine", StateMachineEngine.class);
}
@Test
public void testSimpleCatchesStateMachine() {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 1);
paramMap.put("barThrowException", "true");
String stateMachineName = "simpleCachesStateMachine";
StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback);
waittingForFinish(inst);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertNotNull(inst.getException());
Assertions.assertTrue(ExecutionStatus.FA.equals(inst.getStatus()));
}
@Test
public void testSimpleScriptTaskStateMachine() {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 1);
String stateMachineName = "simpleScriptTaskStateMachine";
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
waittingForFinish(inst);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
Assertions.assertNotNull(inst.getEndParams().get("scriptStateResult"));
start = System.currentTimeMillis();
inst = stateMachineEngine.start(stateMachineName, null, paramMap);
waittingForFinish(inst);
cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
start = System.currentTimeMillis();
paramMap.put("scriptThrowException", true);
inst = stateMachineEngine.start(stateMachineName, null, paramMap);
waittingForFinish(inst);
cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertTrue(ExecutionStatus.FA.equals(inst.getStatus()));
}
@Test
public void testSimpleRetryStateMachine() {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 1);
paramMap.put("barThrowException", "true");
String stateMachineName = "simpleRetryStateMachine";
StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback);
waittingForFinish(inst);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertNotNull(inst.getException());
Assertions.assertTrue(ExecutionStatus.FA.equals(inst.getStatus()));
}
@Test
public void testStatusMatchingStateMachine() {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 1);
paramMap.put("barThrowException", "true");
String stateMachineName = "simpleStatusMatchingStateMachine";
StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback);
waittingForFinish(inst);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertNotNull(inst.getException());
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
}
@Test
public void testCompensationStateMachine() {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 1);
paramMap.put("barThrowException", "true");
String stateMachineName = "simpleCompensationStateMachine";
StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback);
waittingForFinish(inst);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getCompensationStatus()));
}
@Test
public void testCompensationAndSubStateMachine() {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 2);
paramMap.put("barThrowException", "true");
String stateMachineName = "simpleStateMachineWithCompensationAndSubMachine";
StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback);
waittingForFinish(inst);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
}
@Test
public void testCompensationAndSubStateMachineWithLayout() {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 2);
paramMap.put("barThrowException", "true");
String stateMachineName = "simpleStateMachineWithCompensationAndSubMachine_layout";
StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback);
waittingForFinish(inst);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
}
@Test
public void testStateMachineWithComplexParams() {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
People people = new People();
people.setName("lilei");
people.setAge(18);
paramMap.put("people", people);
String stateMachineName = "simpleStateMachineWithComplexParams";
StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback);
waittingForFinish(inst);
long cost = System.currentTimeMillis() - start;
People peopleResult = (People) inst.getEndParams().get("complexParameterMethodResult");
Assertions.assertNotNull(peopleResult);
Assertions.assertTrue(people.getName().equals(people.getName()));
System.out.println("====== cost :" + cost);
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
}
@Test
public void testSimpleStateMachineWithAsyncState() {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 1);
String stateMachineName = "simpleStateMachineWithAsyncState";
StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback);
waittingForFinish(inst);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void waittingForFinish(StateMachineInstance inst) {
synchronized (lock) {
if (ExecutionStatus.RU.equals(inst.getStatus())) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
private volatile Object lock = new Object();
private AsyncCallback callback = new AsyncCallback() {
@Override
public void onFinished(ProcessContext context, StateMachineInstance stateMachineInstance) {
synchronized (lock) {
lock.notifyAll();
}
}
@Override
public void onError(ProcessContext context, StateMachineInstance stateMachineInstance, Exception exp) {
synchronized (lock) {
lock.notifyAll();
}
}
};
}

View File

@@ -0,0 +1,364 @@
/*
* 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.saga.engine;
import io.seata.saga.engine.mock.DemoService.Engineer;
import io.seata.saga.engine.mock.DemoService.People;
import io.seata.saga.statelang.domain.DomainConstants;
import io.seata.saga.statelang.domain.ExecutionStatus;
import io.seata.saga.statelang.domain.StateMachineInstance;
import io.seata.saga.statelang.parser.JsonParserFactory;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* State machine tests
* @author lorne.cl
*/
public class StateMachineTests {
private static StateMachineEngine stateMachineEngine;
@BeforeAll
public static void initApplicationContext() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:saga/spring/statemachine_engine_test.xml");
stateMachineEngine = applicationContext.getBean("stateMachineEngine", StateMachineEngine.class);
}
@Test
public void testSimpleStateMachine() {
stateMachineEngine.start("simpleTestStateMachine", null, new HashMap<>());
}
@Test
public void testSimpleStateMachineWithChoice() {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("a", 1);
String stateMachineName = "simpleChoiceTestStateMachine";
stateMachineEngine.start(stateMachineName, null, paramMap);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
start = System.currentTimeMillis();
paramMap.put("a", 2);
stateMachineEngine.start(stateMachineName, null, paramMap);
cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
}
@Test
public void testSimpleStateMachineWithChoiceAndEnd() {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 1);
String stateMachineName = "simpleChoiceAndEndTestStateMachine";
stateMachineEngine.start(stateMachineName, null, paramMap);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
start = System.currentTimeMillis();
paramMap.put("a", 3);
stateMachineEngine.start(stateMachineName, null, paramMap);
cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
}
@Test
public void testSimpleInputAssignmentStateMachine() {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 1);
String stateMachineName = "simpleInputAssignmentStateMachine";
StateMachineInstance instance = stateMachineEngine.start(stateMachineName, null, paramMap);
String businessKey = instance.getStateList().get(0).getBusinessKey();
Assertions.assertNotNull(businessKey);
System.out.println("====== businessKey :" + businessKey);
String contextBusinessKey = (String) instance.getEndParams().get(
instance.getStateList().get(0).getName() + DomainConstants.VAR_NAME_BUSINESSKEY);
Assertions.assertNotNull(contextBusinessKey);
System.out.println("====== context businessKey :" + businessKey);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
}
@Test
public void testSimpleCatchesStateMachine() {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 1);
paramMap.put("barThrowException", "true");
String stateMachineName = "simpleCachesStateMachine";
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertNotNull(inst.getException());
Assertions.assertTrue(ExecutionStatus.FA.equals(inst.getStatus()));
}
@Test
public void testSimpleScriptTaskStateMachine() {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 1);
String stateMachineName = "simpleScriptTaskStateMachine";
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
Assertions.assertNotNull(inst.getEndParams().get("scriptStateResult"));
start = System.currentTimeMillis();
inst = stateMachineEngine.start(stateMachineName, null, paramMap);
cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
start = System.currentTimeMillis();
paramMap.put("scriptThrowException", true);
inst = stateMachineEngine.start(stateMachineName, null, paramMap);
cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertTrue(ExecutionStatus.FA.equals(inst.getStatus()));
}
@Test
public void testSimpleRetryStateMachine() {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 1);
paramMap.put("barThrowException", "true");
String stateMachineName = "simpleRetryStateMachine";
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertNotNull(inst.getException());
Assertions.assertTrue(ExecutionStatus.FA.equals(inst.getStatus()));
}
@Test
public void testStatusMatchingStateMachine() {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 1);
paramMap.put("barThrowException", "true");
String stateMachineName = "simpleStatusMatchingStateMachine";
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertNotNull(inst.getException());
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
}
@Test
public void testCompensationStateMachine() {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 1);
paramMap.put("barThrowException", "true");
String stateMachineName = "simpleCompensationStateMachine";
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getCompensationStatus()));
}
@Test
public void testCompensationAndSubStateMachine() {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 2);
paramMap.put("barThrowException", "true");
String stateMachineName = "simpleStateMachineWithCompensationAndSubMachine";
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
}
@Test
public void testCompensationAndSubStateMachineWithLayout() {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 2);
paramMap.put("barThrowException", "true");
String stateMachineName = "simpleStateMachineWithCompensationAndSubMachine_layout";
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
}
@Test
public void testStateComplexParams() {
People people1 = new People();
people1.setName("lilei");
people1.setAge(18);
People people2 = new People();
people2.setName("lilei2");
people2.setAge(19);
People people3 = new People();
people3.setName("lilei3");
people3.setAge(20);
People people4 = new People();
people4.setName("lilei4");
people4.setAge(21);
people1.setChildrenArray(new People[] {people2});
people1.setChildrenList(Arrays.asList(people3));
Map<String, People> map1 = new HashMap<>(1);
map1.put("lilei4", people4);
people1.setChildrenMap(map1);
String json = JsonParserFactory.getJsonParser("jackson").toJsonString(people1, false, true);
System.out.println(json);
}
@Test
public void testStateMachineWithComplexParams() {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
People people = new People();
people.setName("lilei");
people.setAge(18);
Engineer engineer = new Engineer();
engineer.setName("programmer");
paramMap.put("people", people);
paramMap.put("career", engineer);
String stateMachineName = "simpleStateMachineWithComplexParams";
StateMachineInstance instance = stateMachineEngine.start(stateMachineName, null, paramMap);
People peopleResult = (People) instance.getEndParams().get("complexParameterMethodResult");
Assertions.assertNotNull(peopleResult);
Assertions.assertTrue(people.getName().equals(people.getName()));
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertTrue(ExecutionStatus.SU.equals(instance.getStatus()));
}
@Test
public void testSimpleStateMachineWithAsyncState() {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 1);
String stateMachineName = "simpleStateMachineWithAsyncState";
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

View File

@@ -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.saga.engine.db;
import io.seata.common.XID;
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.server.ParameterParser;
import io.seata.server.UUIDGenerator;
import io.seata.server.coordinator.DefaultCoordinator;
import io.seata.server.metrics.MetricsManager;
import io.seata.server.session.SessionHolder;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* Abstract Server Test
*
* @author lorne.cl
*/
public abstract class AbstractServerTest {
private static NettyRemotingServer nettyServer;
private static final ThreadPoolExecutor workingThreads = new ThreadPoolExecutor(100, 500, 500, TimeUnit.SECONDS,
new LinkedBlockingQueue(20000), new ThreadPoolExecutor.CallerRunsPolicy());
protected static void startSeataServer() throws InterruptedException {
(new Thread(new Runnable() {
@Override
public void run() {
File file = new File("sessionStore/root.data");
if(file.exists()){
file.delete();
}
ParameterParser parameterParser = new ParameterParser(new String[]{});
//initialize the metrics
MetricsManager.get().init();
System.setProperty(ConfigurationKeys.STORE_MODE, parameterParser.getStoreMode());
nettyServer = new NettyRemotingServer(workingThreads);
//server port
nettyServer.setListenPort(parameterParser.getPort());
UUIDGenerator.init(parameterParser.getServerNode());
//log store mode : file、db
SessionHolder.init(parameterParser.getStoreMode());
DefaultCoordinator coordinator = new DefaultCoordinator(nettyServer);
coordinator.init();
nettyServer.setHandler(coordinator);
// register ShutdownHook
ShutdownHook.getInstance().addDisposable(coordinator);
//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(nettyServer.getListenPort());
nettyServer.init();
}
})).start();
Thread.sleep(5000);
}
protected static final void stopSeataServer() throws InterruptedException {
if(nettyServer != null){
nettyServer.destroy();
Thread.sleep(5000);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,261 @@
/*
* 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.saga.engine.db.mockserver;
import io.seata.saga.engine.AsyncCallback;
import io.seata.saga.engine.StateMachineEngine;
import io.seata.saga.engine.mock.DemoService.People;
import io.seata.saga.proctrl.ProcessContext;
import io.seata.saga.statelang.domain.ExecutionStatus;
import io.seata.saga.statelang.domain.StateMachineInstance;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.HashMap;
import java.util.Map;
/**
* State machine async tests with db log store
* @author lorne.cl
*/
public class StateMachineAsyncDBMockServerTests {
private static StateMachineEngine stateMachineEngine;
@BeforeAll
public static void initApplicationContext() throws InterruptedException {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
"classpath:saga/spring/statemachine_engine_db_mockserver_test.xml");
stateMachineEngine = applicationContext.getBean("stateMachineEngine", StateMachineEngine.class);
}
@Test
public void testSimpleCatchesStateMachine() throws Exception {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 1);
paramMap.put("barThrowException", "true");
String stateMachineName = "simpleCachesStateMachine";
StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback);
waittingForFinish(inst);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertNotNull(inst.getException());
Assertions.assertTrue(ExecutionStatus.FA.equals(inst.getStatus()));
}
@Test
public void testSimpleRetryStateMachine() {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 1);
paramMap.put("barThrowException", "true");
String stateMachineName = "simpleRetryStateMachine";
StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback);
waittingForFinish(inst);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertNotNull(inst.getException());
Assertions.assertTrue(ExecutionStatus.FA.equals(inst.getStatus()));
}
@Test
public void testStatusMatchingStateMachine() throws Exception {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 1);
paramMap.put("barThrowException", "true");
String stateMachineName = "simpleStatusMatchingStateMachine";
StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback);
waittingForFinish(inst);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertNotNull(inst.getException());
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
}
@Test
public void testCompensationStateMachine() throws Exception {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 1);
paramMap.put("barThrowException", "true");
String stateMachineName = "simpleCompensationStateMachine";
StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback);
waittingForFinish(inst);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getCompensationStatus()));
}
@Test
public void testCompensationAndSubStateMachine() throws Exception {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 2);
paramMap.put("barThrowException", "true");
String stateMachineName = "simpleStateMachineWithCompensationAndSubMachine";
StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback);
waittingForFinish(inst);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
}
@Test
public void testCompensationAndSubStateMachineWithLayout() throws Exception {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 2);
paramMap.put("barThrowException", "true");
String stateMachineName = "simpleStateMachineWithCompensationAndSubMachine_layout";
StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback);
waittingForFinish(inst);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
}
@Test
public void testStateMachineWithComplexParams() {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
People people = new People();
people.setName("lilei");
people.setAge(18);
paramMap.put("people", people);
String stateMachineName = "simpleStateMachineWithComplexParamsJackson";
StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback);
waittingForFinish(inst);
long cost = System.currentTimeMillis() - start;
People peopleResult = (People) inst.getEndParams().get("complexParameterMethodResult");
Assertions.assertNotNull(peopleResult);
Assertions.assertTrue(people.getName().equals(people.getName()));
System.out.println("====== cost :" + cost);
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
}
@Test
public void testSimpleStateMachineWithAsyncState() {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 1);
String stateMachineName = "simpleStateMachineWithAsyncState";
StateMachineInstance inst = stateMachineEngine.startAsync(stateMachineName, null, paramMap, callback);
waittingForFinish(inst);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void waittingForFinish(StateMachineInstance inst) {
synchronized (lock) {
if (ExecutionStatus.RU.equals(inst.getStatus())) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
private volatile Object lock = new Object();
private AsyncCallback callback = new AsyncCallback() {
@Override
public void onFinished(ProcessContext context, StateMachineInstance stateMachineInstance) {
synchronized (lock) {
lock.notifyAll();
}
}
@Override
public void onError(ProcessContext context, StateMachineInstance stateMachineInstance, Exception exp) {
synchronized (lock) {
lock.notifyAll();
}
}
};
}

View File

@@ -0,0 +1,675 @@
/*
* 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.saga.engine.db.mockserver;
import io.seata.saga.engine.StateMachineEngine;
import io.seata.saga.engine.mock.DemoService.Engineer;
import io.seata.saga.engine.mock.DemoService.People;
import io.seata.saga.statelang.domain.DomainConstants;
import io.seata.saga.statelang.domain.ExecutionStatus;
import io.seata.saga.statelang.domain.StateMachineInstance;
import io.seata.saga.statelang.parser.JsonParser;
import io.seata.saga.statelang.parser.JsonParserFactory;
import io.seata.saga.statelang.parser.utils.DesignerJsonTransformer;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.HashMap;
import java.util.Map;
/**
* State machine tests with db log store
* @author lorne.cl
*/
public class StateMachineDBMockServerTests {
private static StateMachineEngine stateMachineEngine;
@BeforeAll
public static void initApplicationContext() throws InterruptedException {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
"classpath:saga/spring/statemachine_engine_db_mockserver_test.xml");
stateMachineEngine = applicationContext.getBean("stateMachineEngine", StateMachineEngine.class);
}
@Test
public void testSimpleStateMachine() {
stateMachineEngine.start("simpleTestStateMachine", null, new HashMap<>());
}
@Test
public void testSimpleStateMachineWithChoice() {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 1);
String stateMachineName = "simpleChoiceTestStateMachine";
String businessKey = String.valueOf(start);
StateMachineInstance inst = stateMachineEngine.startWithBusinessKey(stateMachineName, null, businessKey, paramMap);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertNotNull(inst);
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
inst = stateMachineEngine.getStateMachineConfig().getStateLogStore().getStateMachineInstanceByBusinessKey(businessKey, null);
Assertions.assertNotNull(inst);
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
start = System.currentTimeMillis();
paramMap.put("a", 2);
inst = stateMachineEngine.start(stateMachineName, null, paramMap);
cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertNotNull(inst);
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
}
@Test
public void testSimpleStateMachineWithChoiceAndEnd() {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 1);
String stateMachineName = "simpleChoiceAndEndTestStateMachine";
stateMachineEngine.start(stateMachineName, null, paramMap);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
start = System.currentTimeMillis();
paramMap.put("a", 3);
stateMachineEngine.start(stateMachineName, null, paramMap);
cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
}
@Test
public void testSimpleInputAssignmentStateMachine() {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 1);
String stateMachineName = "simpleInputAssignmentStateMachine";
StateMachineInstance instance = stateMachineEngine.start(stateMachineName, null, paramMap);
String businessKey = instance.getStateList().get(0).getBusinessKey();
Assertions.assertNotNull(businessKey);
System.out.println("====== businessKey :" + businessKey);
String contextBusinessKey = (String) instance.getEndParams().get(
instance.getStateList().get(0).getName() + DomainConstants.VAR_NAME_BUSINESSKEY);
Assertions.assertNotNull(contextBusinessKey);
System.out.println("====== context businessKey :" + businessKey);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
}
@Test
public void testSimpleCatchesStateMachine() throws Exception {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 1);
paramMap.put("barThrowException", "true");
String stateMachineName = "simpleCachesStateMachine";
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertNotNull(inst.getException());
Assertions.assertTrue(ExecutionStatus.FA.equals(inst.getStatus()));
}
@Test
public void testSimpleScriptTaskStateMachineWithLayout() {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 1);
String stateMachineName = "designerSimpleScriptTaskStateMachine";
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
Assertions.assertNotNull(inst.getEndParams().get("scriptStateResult"));
start = System.currentTimeMillis();
inst = stateMachineEngine.start(stateMachineName, null, paramMap);
cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
start = System.currentTimeMillis();
paramMap.put("scriptThrowException", true);
inst = stateMachineEngine.start(stateMachineName, null, paramMap);
cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertTrue(ExecutionStatus.FA.equals(inst.getStatus()));
}
@Test
public void testSimpleRetryStateMachine() {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 1);
paramMap.put("barThrowException", "true");
String stateMachineName = "simpleRetryStateMachine";
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertNotNull(inst.getException());
Assertions.assertTrue(ExecutionStatus.FA.equals(inst.getStatus()));
}
@Test
public void testStatusMatchingStateMachine() throws Exception {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 1);
paramMap.put("barThrowException", "true");
String stateMachineName = "simpleStatusMatchingStateMachine";
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertNotNull(inst.getException());
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
}
@Test
public void testCompensationStateMachine() throws Exception {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 1);
paramMap.put("barThrowException", "true");
String stateMachineName = "simpleCompensationStateMachine";
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getCompensationStatus()));
}
@Test
public void testSubStateMachine() throws Exception {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 2);
paramMap.put("barThrowException", "true");
String stateMachineName = "simpleStateMachineWithCompensationAndSubMachine";
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
long cost = System.currentTimeMillis() - start;
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
start = System.currentTimeMillis();
paramMap.put("barThrowException", "false");
inst = stateMachineEngine.forward(inst.getId(), paramMap);
cost = System.currentTimeMillis() - start;
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
}
@Test
public void testSubStateMachineWithLayout() throws Exception {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 2);
paramMap.put("barThrowException", "true");
String stateMachineName = "simpleStateMachineWithCompensationAndSubMachine_layout";
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
long cost = System.currentTimeMillis() - start;
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
start = System.currentTimeMillis();
paramMap.put("barThrowException", "false");
inst = stateMachineEngine.forward(inst.getId(), paramMap);
cost = System.currentTimeMillis() - start;
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
}
@Test
public void testForwardSubStateMachine() throws Exception {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 2);
paramMap.put("fooThrowException", "true");
String stateMachineName = "simpleStateMachineWithCompensationAndSubMachine";
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
long cost = System.currentTimeMillis() - start;
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
start = System.currentTimeMillis();
paramMap.put("fooThrowException", "false");
inst = stateMachineEngine.forward(inst.getId(), paramMap);
cost = System.currentTimeMillis() - start;
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
}
@Test
public void testForwardSubStateMachineWithLayout() throws Exception {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 2);
paramMap.put("fooThrowException", "true");
String stateMachineName = "simpleStateMachineWithCompensationAndSubMachine_layout";
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
long cost = System.currentTimeMillis() - start;
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
JsonParser jsonParser = JsonParserFactory.getJsonParser("jackson");
String graphJson = DesignerJsonTransformer.generateTracingGraphJson(inst, jsonParser);
Assertions.assertNotNull(graphJson);
System.out.println(graphJson);
start = System.currentTimeMillis();
paramMap.put("fooThrowException", "false");
inst = stateMachineEngine.forward(inst.getId(), paramMap);
cost = System.currentTimeMillis() - start;
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
String graphJson2 = DesignerJsonTransformer.generateTracingGraphJson(inst, jsonParser);
Assertions.assertNotNull(graphJson2);
System.out.println(graphJson2);
}
@Test
public void testCompensateSubStateMachine() throws Exception {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 2);
paramMap.put("barThrowException", "true");
paramMap.put("compensateFooThrowException", "true");
String stateMachineName = "simpleStateMachineWithCompensationAndSubMachine";
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
long cost = System.currentTimeMillis() - start;
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
start = System.currentTimeMillis();
inst = stateMachineEngine.compensate(inst.getId(), paramMap);
cost = System.currentTimeMillis() - start;
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getCompensationStatus()));
}
@Test
public void testCompensateSubStateMachineWithLayout() throws Exception {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 2);
paramMap.put("barThrowException", "true");
paramMap.put("compensateFooThrowException", "true");
String stateMachineName = "simpleStateMachineWithCompensationAndSubMachine_layout";
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
long cost = System.currentTimeMillis() - start;
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
JsonParser jsonParser = JsonParserFactory.getJsonParser("jackson");
String graphJson = DesignerJsonTransformer.generateTracingGraphJson(inst, jsonParser);
Assertions.assertNotNull(graphJson);
System.out.println(graphJson);
start = System.currentTimeMillis();
inst = stateMachineEngine.compensate(inst.getId(), paramMap);
cost = System.currentTimeMillis() - start;
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getCompensationStatus()));
String graphJson2 = DesignerJsonTransformer.generateTracingGraphJson(inst, jsonParser);
Assertions.assertNotNull(graphJson2);
System.out.println(graphJson2);
}
@Test
public void testUserDefCompensateSubStateMachine() throws Exception {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 2);
paramMap.put("barThrowException", "true");
paramMap.put("compensateFooThrowException", "true");
String stateMachineName = "simpleStateMachineWithUseDefCompensationSubMachine";
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
long cost = System.currentTimeMillis() - start;
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
start = System.currentTimeMillis();
paramMap.put("compensateFooThrowException", "false");
inst = stateMachineEngine.compensate(inst.getId(), paramMap);
cost = System.currentTimeMillis() - start;
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getCompensationStatus()));
}
@Test
public void testCommitRetryingThenRetryCommitted() throws Exception {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 1);
paramMap.put("fooThrowException", "true");
String stateMachineName = "simpleCompensationStateMachineForRecovery";
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
long cost = System.currentTimeMillis() - start;
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
paramMap.put("fooThrowException", "false");
start = System.currentTimeMillis();
inst = stateMachineEngine.forward(inst.getId(), paramMap);
cost = System.currentTimeMillis() - start;
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
}
@Test
public void testCommitRetryingThenRetryRollbacked() throws Exception {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 1);
paramMap.put("fooThrowException", "true");
String stateMachineName = "simpleCompensationStateMachineForRecovery";
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
long cost = System.currentTimeMillis() - start;
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
paramMap.put("fooThrowException", "false");
paramMap.put("barThrowException", "true");
start = System.currentTimeMillis();
inst = stateMachineEngine.forward(inst.getId(), paramMap);
cost = System.currentTimeMillis() - start;
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getCompensationStatus()));
}
@Test
public void testRollbackRetryingThenRetryRollbacked() throws Exception {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 1);
paramMap.put("barThrowException", "true");
paramMap.put("compensateFooThrowException", "true");
String stateMachineName = "simpleCompensationStateMachineForRecovery";
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
long cost = System.currentTimeMillis() - start;
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getCompensationStatus()));
paramMap.put("barThrowException", "false");
paramMap.put("compensateFooThrowException", "false");
start = System.currentTimeMillis();
inst = stateMachineEngine.compensate(inst.getId(), paramMap);
cost = System.currentTimeMillis() - start;
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getCompensationStatus()));
}
@Test
public void testRollbackRetryingTwiceThenRetryRollbacked() throws Exception {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 1);
paramMap.put("barThrowException", "true");
paramMap.put("compensateFooThrowException", "true");
String stateMachineName = "simpleCompensationStateMachineForRecovery";
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
long cost = System.currentTimeMillis() - start;
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getCompensationStatus()));
start = System.currentTimeMillis();
inst = stateMachineEngine.compensate(inst.getId(), paramMap);
cost = System.currentTimeMillis() - start;
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getStatus()));
Assertions.assertTrue(ExecutionStatus.UN.equals(inst.getCompensationStatus()));
paramMap.put("barThrowException", "false");
paramMap.put("compensateFooThrowException", "false");
start = System.currentTimeMillis();
inst = stateMachineEngine.compensate(inst.getId(), paramMap);
cost = System.currentTimeMillis() - start;
System.out.println("====== XID: " + inst.getId() + " cost :" + cost);
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getCompensationStatus()));
}
@Test
public void testStateMachineWithComplexParams() {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
People people = new People();
people.setName("lilei");
people.setAge(18);
Engineer engineer = new Engineer();
engineer.setName("programmer");
paramMap.put("people", people);
paramMap.put("career", engineer);
String stateMachineName = "simpleStateMachineWithComplexParamsJackson";
StateMachineInstance instance = stateMachineEngine.start(stateMachineName, null, paramMap);
People peopleResult = (People) instance.getEndParams().get("complexParameterMethodResult");
Assertions.assertNotNull(peopleResult);
Assertions.assertTrue(people.getName().equals(people.getName()));
long cost = System.currentTimeMillis() - start;
System.out.println("====== XID: " + instance.getId() + " cost :" + cost);
Assertions.assertTrue(ExecutionStatus.SU.equals(instance.getStatus()));
}
@Test
public void testSimpleStateMachineWithAsyncState() {
long start = System.currentTimeMillis();
Map<String, Object> paramMap = new HashMap<>(1);
paramMap.put("a", 1);
String stateMachineName = "simpleStateMachineWithAsyncState";
StateMachineInstance inst = stateMachineEngine.start(stateMachineName, null, paramMap);
long cost = System.currentTimeMillis() - start;
System.out.println("====== cost :" + cost);
Assertions.assertTrue(ExecutionStatus.SU.equals(inst.getStatus()));
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Test
public void testReloadStateMachineInstance() {
StateMachineInstance instance = stateMachineEngine.getStateMachineConfig().getStateLogStore().getStateMachineInstance(
"10.15.232.93:8091:2019567124");
System.out.println(instance);
}
}

View File

@@ -0,0 +1,42 @@
/*
* 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.saga.engine.mock;
/**
*
* @author lorne.cl
*/
public class DemoException extends RuntimeException {
public DemoException() {
}
public DemoException(String message) {
super(message);
}
public DemoException(String message, Throwable cause) {
super(message, cause);
}
public DemoException(Throwable cause) {
super(cause);
}
public DemoException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

View File

@@ -0,0 +1,187 @@
/*
* 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.saga.engine.mock;
import java.net.ConnectException;
import java.util.List;
import java.util.Map;
/**
* @author lorne.cl
*/
public class DemoService {
public Map<String, Object> foo(Map<String, Object> input) {
if(input == null){
return null;
}
Integer sleepTime = (Integer) input.get("sleepTime");
if(sleepTime != null){
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
throw new DemoException(e);
}
}
if("true".equals(input.get("throwException"))){
throw new DemoException("foo execute failed");
}
if("true".equals(input.get("throwExceptionRandomly"))){
if(Math.random() > 0.5){
throw new DemoException("foo execute failed");
}
}
return input;
}
public Map<String, Object> compensateFoo(Map<String, Object> input) {
if(input == null){
return null;
}
if("true".equals(input.get("throwException"))){
throw new DemoException("compensateFoo execute failed");
}
if("true".equals(input.get("throwExceptionRandomly"))){
if(Math.random() > 0.8){
throw new DemoException("compensateFoo execute failed");
}
}
return input;
}
public Map<String, Object> bar(Map<String, Object> input) {
if(input == null){
return null;
}
Integer sleepTime = (Integer) input.get("sleepTime");
if(sleepTime != null){
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
throw new DemoException(e);
}
}
if("true".equals(input.get("throwException"))){
throw new DemoException("bar execute failed");
}
if("true".equals(input.get("throwExceptionRandomly"))){
if(Math.random() > 0.5){
throw new DemoException("bar execute failed");
}
}
return input;
}
public Map<String, Object> compensateBar(Map<String, Object> input) {
if(input == null){
return null;
}
if("true".equals(input.get("throwException"))){
throw new DemoException("compensateBar execute failed");
}
if("true".equals(input.get("throwExceptionRandomly"))){
if(Math.random() > 0.8){
throw new DemoException("compensateBar execute failed");
}
}
return input;
}
public People complexParameterMethod(String name, int age, People people, People[] peopleArrya, List<People> peopleList, Map<String, People> peopleMap){
return people;
}
public Career interfaceParameterMethod(Career career){
return career;
}
public Map<String, Object> randomExceptionMethod(Map<String, Object> input) {
double random = Math.random();
if (random > 0.5) {
throw new DemoException("randomExceptionMethod execute failed");
}
else {
throw new RuntimeException(new ConnectException("Connect Exception"));
}
}
public static class People {
private String name;
private int age;
private People[] childrenArray;
private List<People> childrenList;
private Map<String, People> childrenMap;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public People[] getChildrenArray() {
return childrenArray;
}
public void setChildrenArray(People[] childrenArray) {
this.childrenArray = childrenArray;
}
public List<People> getChildrenList() {
return childrenList;
}
public void setChildrenList(List<People> childrenList) {
this.childrenList = childrenList;
}
public Map<String, People> getChildrenMap() {
return childrenMap;
}
public void setChildrenMap(Map<String, People> childrenMap) {
this.childrenMap = childrenMap;
}
}
public interface Career {
}
public static class Engineer implements Career {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}

View File

@@ -0,0 +1,112 @@
/*
* 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.saga.engine.mock;
import io.seata.core.context.RootContext;
import io.seata.core.exception.TransactionException;
import io.seata.core.model.GlobalStatus;
import io.seata.saga.engine.sequence.SpringJvmUUIDSeqGenerator;
import io.seata.tm.api.GlobalTransaction;
import io.seata.tm.api.GlobalTransactionRole;
import io.seata.tm.api.transaction.SuspendedResourcesHolder;
/**
*
* @author lorne.cl
*/
public class MockGlobalTransaction implements GlobalTransaction {
private String xid;
private GlobalStatus status;
private static SpringJvmUUIDSeqGenerator uuidSeqGenerator = new SpringJvmUUIDSeqGenerator();
public MockGlobalTransaction() {}
public MockGlobalTransaction(String xid) {
this.xid = xid;
}
public MockGlobalTransaction(String xid, GlobalStatus status) {
this.xid = xid;
this.status = status;
}
@Override
public void begin() throws TransactionException {
begin(60000);
}
@Override
public void begin(int timeout) throws TransactionException {
status = GlobalStatus.Begin;
xid = uuidSeqGenerator.generate(null).toString();
RootContext.bind(xid);
}
@Override
public void begin(int timeout, String name) throws TransactionException {
}
@Override
public void commit() throws TransactionException {
}
@Override
public void rollback() throws TransactionException {
}
@Override
public SuspendedResourcesHolder suspend()
throws TransactionException {
return null;
}
@Override
public void resume(SuspendedResourcesHolder suspendedResourcesHolder)
throws TransactionException {
}
@Override
public GlobalStatus getStatus() throws TransactionException {
return status;
}
@Override
public String getXid() {
return xid;
}
@Override
public void globalReport(GlobalStatus globalStatus) throws TransactionException {
}
@Override
public GlobalStatus getLocalStatus() {
return status;
}
@Override
public GlobalTransactionRole getGlobalTransactionRole() {
return null;
}
}

View File

@@ -0,0 +1,84 @@
/*
* 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.saga.engine.mock;
import io.seata.core.exception.TransactionException;
import io.seata.core.model.BranchStatus;
import io.seata.core.model.GlobalStatus;
import io.seata.saga.tm.SagaTransactionalTemplate;
import io.seata.server.UUIDGenerator;
import io.seata.tm.api.GlobalTransaction;
import io.seata.tm.api.TransactionalExecutor.ExecutionException;
import io.seata.tm.api.transaction.TransactionInfo;
/**
*
* @author lorne.cl
*/
public class MockSagaTransactionTemplate implements SagaTransactionalTemplate {
@Override
public void commitTransaction(GlobalTransaction tx) throws ExecutionException {
}
@Override
public void rollbackTransaction(GlobalTransaction tx, Throwable ex) throws TransactionException, ExecutionException {
}
@Override
public GlobalTransaction beginTransaction(TransactionInfo txInfo) throws ExecutionException {
GlobalTransaction globalTransaction = new MockGlobalTransaction();
try {
globalTransaction.begin();
} catch (TransactionException e) {
e.printStackTrace();
}
return globalTransaction;
}
@Override
public GlobalTransaction reloadTransaction(String xid) throws ExecutionException, TransactionException {
return new MockGlobalTransaction(xid, GlobalStatus.UnKnown);
}
@Override
public void reportTransaction(GlobalTransaction tx, GlobalStatus globalStatus) throws ExecutionException {
}
@Override
public long branchRegister(String resourceId, String clientId, String xid, String applicationData, String lockKeys)
throws TransactionException {
return UUIDGenerator.generateUUID();
}
@Override
public void branchReport(String xid, long branchId, BranchStatus status, String applicationData) throws TransactionException {
}
@Override
public void triggerAfterCompletion() {
}
@Override
public void cleanUp() {
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.saga.engine.mock;
import io.seata.saga.engine.exception.EngineExecutionException;
import io.seata.saga.engine.pcext.InterceptableStateHandler;
import io.seata.saga.engine.pcext.StateHandlerInterceptor;
import io.seata.saga.proctrl.ProcessContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author lorne.cl
*/
public class MockStateHandlerInterceptor implements StateHandlerInterceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(MockStateHandlerInterceptor.class);
@Override
public void preProcess(ProcessContext context) throws EngineExecutionException {
LOGGER.info("test StateHandlerInterceptor preProcess");
}
@Override
public void postProcess(ProcessContext context, Exception e) throws EngineExecutionException {
LOGGER.info("test StateHandlerInterceptor postProcess");
}
@Override
public boolean match(Class<? extends InterceptableStateHandler> clazz) {
return true;
}
}

View File

@@ -0,0 +1,49 @@
/*
* 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.saga.engine.mock;
import io.seata.saga.engine.exception.EngineExecutionException;
import io.seata.saga.engine.pcext.InterceptableStateRouter;
import io.seata.saga.engine.pcext.StateRouterInterceptor;
import io.seata.saga.proctrl.Instruction;
import io.seata.saga.proctrl.ProcessContext;
import io.seata.saga.statelang.domain.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author lorne.cl
*/
public class MockStateRouterInterceptor implements StateRouterInterceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(MockStateRouterInterceptor.class);
@Override
public void preRoute(ProcessContext context, State state) throws EngineExecutionException {
LOGGER.info("test StateRouterInterceptor preRoute");
}
@Override
public void postRoute(ProcessContext context, State state, Instruction instruction, Exception e) throws EngineExecutionException {
LOGGER.info("test StateRouterInterceptor postRoute");
}
@Override
public boolean match(Class<? extends InterceptableStateRouter> clazz) {
return true;
}
}

View File

@@ -0,0 +1,621 @@
/*
* 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.xa;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.xa.DruidXADataSource;
import com.alibaba.druid.util.JdbcUtils;
import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;
import io.seata.core.context.RootContext;
import io.seata.core.exception.TransactionException;
import io.seata.core.model.BranchStatus;
import io.seata.core.model.BranchType;
import io.seata.core.model.Resource;
import io.seata.rm.DefaultResourceManager;
import io.seata.rm.datasource.xa.AbstractDataSourceProxyXA;
import io.seata.rm.datasource.xa.DataSourceProxyXA;
import io.seata.rm.datasource.xa.DataSourceProxyXANative;
import io.seata.rm.datasource.xa.ResourceManagerXA;
import io.seata.rm.datasource.xa.XAXid;
import io.seata.rm.datasource.xa.XAXidBuilder;
import io.seata.spring.annotation.GlobalTransactionScanner;
import io.seata.tm.api.GlobalTransaction;
import io.seata.tm.api.GlobalTransactionContext;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.postgresql.xa.PGXADataSource;
import javax.sql.DataSource;
import javax.sql.XAConnection;
import javax.sql.XADataSource;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class XAModeTest2 {
private static final int testRecordId = 888;
private static final String testRecordName = "xxx";
private static final long testTid = 1582688600006L;
private static final String mockXid = "127.0.0.1:8091:" + testTid;
private static final long mockBranchId = testTid + 1;
private static final String pg_jdbcUrl = "jdbc:postgresql://127.0.0.1:5432/postgres";
private static final String pg_username = "postgres";
private static final String pg_password = "postgres";
private static final String pg_driverClassName = JdbcUtils.POSTGRESQL_DRIVER;
private static final String mysql_jdbcUrl = "jdbc:mysql://127.0.0.1:3306/demo";
private static final String mysql_username = "demo";
private static final String mysql_password = "demo";
private static final String mysql_driverClassName = JdbcUtils.MYSQL_DRIVER;
private static final String mysql8_jdbcUrl = "jdbc:mysql://0.0.0.0:3306/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false";
private static final String mysql8_username = "demo";
private static final String mysql8_password = "demo";
private static final String mysql8_driverClassName = JdbcUtils.MYSQL_DRIVER_6;
private static final String oracle_jdbcUrl = "jdbc:oracle:thin:@localhost:1521:xe";
private static final String oracle_username = "demo";
private static final String oracle_password = "demo";
private static final String oracle_driverClassName = JdbcUtils.ORACLE_DRIVER;
// Test on different DB, including: MySQL(5.7, 8.0), PostgreSQL(11), Oracle(11)
private static final String dbType = JdbcUtils.MYSQL;
private static final boolean nativeXA = false;
private static final boolean mySQL8 = false;
private DruidDataSource createNewDruidDataSource() throws Throwable {
DruidDataSource druidDataSource = new DruidDataSource();
initDruidDataSource(druidDataSource);
return druidDataSource;
}
private DruidXADataSource createNewDruidXADataSource() throws Throwable {
DruidXADataSource druidDataSource = new DruidXADataSource();
initDruidDataSource(druidDataSource);
return druidDataSource;
}
private XADataSource createNewNativeXADataSource() throws Throwable {
if (dbType.equalsIgnoreCase(JdbcUtils.POSTGRESQL)) {
PGXADataSource pgxaDataSource = new PGXADataSource();
pgxaDataSource.setUrl(pg_jdbcUrl);
pgxaDataSource.setUser(pg_username);
pgxaDataSource.setPassword(pg_password);
return pgxaDataSource;
} else if (dbType.equalsIgnoreCase(JdbcUtils.MYSQL)) {
MysqlXADataSource mysqlXADataSource = new MysqlXADataSource();
if (mySQL8) {
mysqlXADataSource.setURL(mysql8_jdbcUrl);
mysqlXADataSource.setUser(mysql8_username);
mysqlXADataSource.setPassword(mysql8_username);
} else {
mysqlXADataSource.setURL(mysql_jdbcUrl);
mysqlXADataSource.setUser(mysql_username);
mysqlXADataSource.setPassword(mysql_username);
}
return mysqlXADataSource;
} else if (dbType.equalsIgnoreCase(JdbcUtils.ORACLE)) {
return createOracleXADataSource();
} else {
throw new IllegalAccessError("Unknown dbType: " + dbType);
}
}
private XADataSource createOracleXADataSource() {
try {
Class oracleXADataSourceClass = Class.forName("oracle.jdbc.xa.client.OracleXADataSource");
XADataSource xaDataSource = (XADataSource)oracleXADataSourceClass.newInstance();
Method setURLMethod = oracleXADataSourceClass.getMethod("setURL", String.class);
setURLMethod.invoke(xaDataSource, oracle_jdbcUrl);
Method setUserMethod = oracleXADataSourceClass.getMethod("setUser", String.class);
setUserMethod.invoke(xaDataSource, oracle_username);
Method setPasswordMethod = oracleXADataSourceClass.getMethod("setPassword", String.class);
setPasswordMethod.invoke(xaDataSource, oracle_password);
Method setDriverTypeMethod = oracleXADataSourceClass.getMethod("setDriverType", String.class);
setDriverTypeMethod.invoke(xaDataSource, oracle_driverClassName);
return xaDataSource;
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
private void initDruidDataSource(DruidDataSource druidDataSource) throws Throwable {
druidDataSource.setDbType(dbType);
if (dbType.equalsIgnoreCase(JdbcUtils.POSTGRESQL)) {
druidDataSource.setUrl(pg_jdbcUrl);
druidDataSource.setUsername(pg_username);
druidDataSource.setPassword(pg_password);
druidDataSource.setDriverClassName(pg_driverClassName);
} else if (dbType.equalsIgnoreCase(JdbcUtils.MYSQL)) {
if (mySQL8) {
druidDataSource.setUrl(mysql8_jdbcUrl);
druidDataSource.setUsername(mysql8_username);
druidDataSource.setPassword(mysql8_password);
druidDataSource.setDriverClassName(mysql8_driverClassName);
} else {
druidDataSource.setUrl(mysql_jdbcUrl);
druidDataSource.setUsername(mysql_username);
druidDataSource.setPassword(mysql_password);
druidDataSource.setDriverClassName(mysql_driverClassName);
}
} else if (dbType.equalsIgnoreCase(JdbcUtils.ORACLE)) {
druidDataSource.setUrl(oracle_jdbcUrl);
druidDataSource.setUsername(oracle_username);
druidDataSource.setPassword(oracle_password);
druidDataSource.setDriverClassName(oracle_driverClassName);
} else {
throw new IllegalAccessError("Unknown dbType: " + dbType);
}
druidDataSource.init();
}
private void initRM() throws Throwable {
// init RM
DefaultResourceManager.get();
// mock the RM of XA
DefaultResourceManager.mockResourceManager(BranchType.XA, new ResourceManagerXA() {
@Override
public void registerResource(Resource resource) {
dataSourceCache.put(resource.getResourceId(), resource);
}
@Override
public Long branchRegister(BranchType branchType, String resourceId, String clientId, String xid,
String applicationData, String lockKeys) throws TransactionException {
return mockBranchId;
}
@Override
public void branchReport(BranchType branchType, String xid, long branchId, BranchStatus status,
String applicationData) throws TransactionException {
}
});
}
@Test
@Disabled
public void testAllInOne() throws Throwable {
testCleanXARecover();
doTestXAModeNormalPrepareData();
doTestXAModeNormalCaseAllInOne(mockXid, mockBranchId);
}
@Test
@Disabled
public void testGlobalCommitOnDifferentDataSource() throws Throwable {
testCleanXARecover();
doTestXAModeNormalPrepareData();
doTestXAModeNormalCasePhase1(mockXid, mockBranchId);
// Use new DataSource in phase 2
doTestXAModeNormalCasePhase2(true, mockXid, mockBranchId);
}
@Test
@Disabled
public void testGlobalRollbackOnDifferentDataSource() throws Throwable {
testCleanXARecover();
doTestXAModeNormalPrepareData();
doTestXAModeNormalCasePhase1(mockXid, mockBranchId);
// Use new DataSource in phase 2
doTestXAModeNormalCasePhase2(false, mockXid, mockBranchId);
}
@Test
@Disabled
public void testOnlyPhase1() throws Throwable {
testCleanXARecover();
doTestXAModeNormalPrepareData();
doTestXAModeNormalCasePhase1(mockXid, mockBranchId);
}
@Test
@Disabled
public void testOnlyPhase2Commit() throws Throwable {
doTestXAModeNormalCasePhase2(true, mockXid, mockBranchId);
}
@Test
@Disabled
public void testOnlyPhase2Rollback() throws Throwable {
doTestXAModeNormalCasePhase2(false, mockXid, mockBranchId);
}
private void doTestXAModeNormalPrepareData() throws Throwable {
// init DataSource: helper
DruidDataSource helperDS = createNewDruidDataSource();
// prepare data for test: make sure no test record there
Connection helperConn = helperDS.getConnection();
Statement helperStat = helperConn.createStatement();
ResultSet helperRes = null;
helperStat.execute("delete from test where id = " + testRecordId);
helperStat.close();
helperConn.close();
}
private void doTestXAModeNormalCasePhase2(boolean globalCommit, String mockXid, Long mockBranchId) throws Throwable {
// init DataSource: helper
DruidDataSource helperDS = createNewDruidDataSource();
Connection helperConn = null;
Statement helperStat = null;
ResultSet helperRes = null;
// init RM
initRM();
AbstractDataSourceProxyXA dataSourceProxyXA = null;
if (nativeXA) {
// init XADataSource runnerXA
XADataSource runnerXADS = createNewNativeXADataSource();
dataSourceProxyXA = new DataSourceProxyXANative(runnerXADS);
} else {
// init DataSource: runner
DruidDataSource runnerDS = createNewDruidDataSource();
dataSourceProxyXA = new DataSourceProxyXA(runnerDS);
}
// Global Tx Phase 2:
if (globalCommit) {
DefaultResourceManager.get().branchCommit(dataSourceProxyXA.getBranchType(), mockXid, mockBranchId,
dataSourceProxyXA.getResourceId(), null);
// have a check
helperConn = helperDS.getConnection();
helperStat = helperConn.createStatement();
helperRes = helperStat.executeQuery("select * from test where id = " + testRecordId);
// should see the test record now
Assertions.assertTrue(helperRes.next());
Assertions.assertEquals(helperRes.getInt(1), testRecordId);
Assertions.assertEquals(helperRes.getString(2), testRecordName);
helperRes.close();
helperStat.close();
helperConn.close();
} else {
DefaultResourceManager.get().branchRollback(dataSourceProxyXA.getBranchType(), mockXid, mockBranchId,
dataSourceProxyXA.getResourceId(), null);
// have a check
helperConn = helperDS.getConnection();
helperStat = helperConn.createStatement();
helperRes = helperStat.executeQuery("select * from test where id = " + testRecordId);
// should NOT see the test record now
Assertions.assertFalse(helperRes.next());
helperRes.close();
helperStat.close();
helperConn.close();
}
System.out.println("Phase2 looks good!");
}
private void doTestXAModeNormalCasePhase1(String mockXid, Long mockBranchId) throws Throwable {
// init DataSource: helper
DruidDataSource helperDS = createNewDruidDataSource();
Connection helperConn = null;
Statement helperStat = null;
ResultSet helperRes = null;
// init RM
initRM();
AbstractDataSourceProxyXA dataSourceProxyXA = null;
if (nativeXA) {
// init XADataSource runnerXA
XADataSource runnerXADS = createNewNativeXADataSource();
dataSourceProxyXA = new DataSourceProxyXANative(runnerXADS);
} else {
// init DataSource: runner
DruidDataSource runnerDS = createNewDruidDataSource();
dataSourceProxyXA = new DataSourceProxyXA(runnerDS);
}
// Global Tx Phase 1:
RootContext.bind(mockXid);
Connection testConn = dataSourceProxyXA.getConnection();
Statement testStat = testConn.createStatement();
// >>> insert the test record with XA mode
testStat.execute("insert into test(id, name) values(" + testRecordId + ", '" + testRecordName + "')");
// >>> close the statement and connection
testStat.close();
testConn.close();
RootContext.unbind();
// have a check
helperConn = helperDS.getConnection();
helperStat = helperConn.createStatement();
helperRes = helperStat.executeQuery("select * from test where id = " + testRecordId);
// should NOT see the record(id=888) now
Assertions.assertFalse(helperRes.next());
helperRes.close();
helperStat.close();
helperConn.close();
if (JdbcUtils.MYSQL.equals(dbType)) {
XAXid xaXid = XAXidBuilder.build(mockXid, mockBranchId);
dataSourceProxyXA.forceClosePhysicalConnection(xaXid);
}
System.out.println("Phase1 looks good!");
}
private void doTestXAModeNormalCaseAllInOne(String mockXid, Long mockBranchId) throws Throwable {
// init DataSource: helper
DruidDataSource helperDS = createNewDruidDataSource();
Connection helperConn = null;
Statement helperStat = null;
ResultSet helperRes = null;
// init RM
initRM();
AbstractDataSourceProxyXA dataSourceProxyXA = null;
if (nativeXA) {
// init XADataSource runnerXA
XADataSource runnerXADS = createNewNativeXADataSource();
dataSourceProxyXA = new DataSourceProxyXANative(runnerXADS);
} else {
// init DataSource: runner
DruidDataSource runnerDS = createNewDruidDataSource();
dataSourceProxyXA = new DataSourceProxyXA(runnerDS);
}
// Global Tx Phase 1:
RootContext.bind(mockXid);
Connection testConn = dataSourceProxyXA.getConnection();
Statement testStat = testConn.createStatement();
// >>> insert the test record with XA mode
testStat.execute("insert into test(id, name) values(" + testRecordId + ", '" + testRecordName + "')");
// >>> close the statement and connection
testStat.close();
testConn.close();
RootContext.unbind();
// have a check
helperConn = helperDS.getConnection();
helperStat = helperConn.createStatement();
helperRes = helperStat.executeQuery("select * from test where id = " + testRecordId);
// should NOT see the record(id=888) now
Assertions.assertFalse(helperRes.next());
helperRes.close();
helperStat.close();
helperConn.close();
// Global Tx Phase 2: run phase 2 with the same runner DS
DefaultResourceManager.get().branchCommit(dataSourceProxyXA.getBranchType(), mockXid, mockBranchId,
dataSourceProxyXA.getResourceId(), null);
// have a check
helperConn = helperDS.getConnection();
helperStat = helperConn.createStatement();
helperRes = helperStat.executeQuery("select * from test where id = " + testRecordId);
// should see the test record now
Assertions.assertTrue(helperRes.next());
Assertions.assertEquals(helperRes.getInt(1), testRecordId);
Assertions.assertEquals(helperRes.getString(2), testRecordName);
helperRes.close();
helperStat.close();
helperConn.close();
System.out.println("All in one looks good!");
}
@Test
@Disabled
public void testXid() throws Throwable {
XAXid xaXid = XAXidBuilder.build(mockXid, mockBranchId);
XAXid retrievedXAXid = XAXidBuilder.build(xaXid.getGlobalTransactionId(), xaXid.getBranchQualifier());
String retrievedXid = retrievedXAXid.getGlobalXid();
long retrievedBranchId = retrievedXAXid.getBranchId();
Assertions.assertEquals(mockXid, retrievedXid);
Assertions.assertEquals(mockBranchId, retrievedBranchId);
}
@Test
@Disabled
public void testCleanXARecover() throws Throwable {
XADataSource xaDataSource = createNewNativeXADataSource();
XAConnection xaConnection = xaDataSource.getXAConnection();
XAResource xaResource = xaConnection.getXAResource();
Xid[] xids = xaResource.recover(XAResource.TMSTARTRSCAN|XAResource.TMENDRSCAN);
for (Xid xid : xids) {
try {
xaResource.rollback(xid);
} catch (XAException xae) {
xae.printStackTrace();
}
}
System.out.println("Unfinished XA branches are ALL cleaned!");
}
@Test
@Disabled
public void testXADataSourceNative() throws Throwable {
XADataSource nativeXADataSource = createOracleXADataSource();
XAConnection xaConnection = nativeXADataSource.getXAConnection();
XAResource xaResource = xaConnection.getXAResource();
Xid xid = XAXidBuilder.build("127.0.0.1:8091:1234", 1235L);
xaResource.start(xid, XAResource.TMNOFLAGS);
}
@Test
@Disabled
public void testXADataSourceNormal() throws Throwable {
DruidXADataSource druidDataSource = new DruidXADataSource();
druidDataSource.setUrl(oracle_jdbcUrl);
druidDataSource.setUsername(oracle_username);
druidDataSource.setPassword(oracle_password);
druidDataSource.setDriverClassName(oracle_driverClassName);
XAConnection xaConnection = druidDataSource.getXAConnection();
XAResource xaResource = xaConnection.getXAResource();
Xid xid = XAXidBuilder.build("127.0.0.1:8091:1234", 1235L);
// Since issue of Druid(https://github.com/alibaba/druid/issues/3707), XA start will fail.
xaResource.start(xid, XAResource.TMNOFLAGS);
}
@Test
@Disabled
// Should RUN with local Seata Server
public void testStandardAppGlobalCommit() throws Throwable {
testCleanXARecover();
doTestXAModeNormalPrepareData();
// Create a standard proxy according to non-XA data source
DataSource ds = createDataSourceProxyXA();
// Create a global tx
GlobalTransaction gtx = createGlobalTransaction();
gtx.begin();
runInGlobalTx(ds);
gtx.commit();
Thread.sleep(5000);
}
@Test
@Disabled
// Should RUN with local Seata Server
public void testXANativeAppGlobalCommit() throws Throwable {
testCleanXARecover();
doTestXAModeNormalPrepareData();
// Create a native proxy according to XA data source
DataSource ds = createDataSourceProxyXANative();
// Create a global tx
GlobalTransaction gtx = createGlobalTransaction();
gtx.begin();
runInGlobalTx(ds);
gtx.commit();
Thread.sleep(5000);
}
@Test
@Disabled
// Should RUN with local Seata Server
public void testStandardAppGlobalRollback() throws Throwable {
testCleanXARecover();
doTestXAModeNormalPrepareData();
// Create a standard proxy according to non-XA data source
DataSource ds = createDataSourceProxyXA();
// Create a global tx
GlobalTransaction gtx = createGlobalTransaction();
gtx.begin();
runInGlobalTx(ds);
gtx.rollback();
Thread.sleep(5000);
}
@Test
@Disabled
// Should RUN with local Seata Server
public void testXANativeAppGlobalRollback() throws Throwable {
testCleanXARecover();
doTestXAModeNormalPrepareData();
// Create a native proxy according to XA data source
DataSource ds = createDataSourceProxyXANative();
// Create a global tx
GlobalTransaction gtx = createGlobalTransaction();
gtx.begin();
runInGlobalTx(ds);
gtx.rollback();
Thread.sleep(5000);
}
private void runInGlobalTx(DataSource ds) throws SQLException {
System.out.println(RootContext.getXID());
Connection testConn = ds.getConnection();
Statement testStat = testConn.createStatement();
// >>> insert the test record with XA mode
testStat.execute("insert into test(id, name) values(" + testRecordId + ", '" + testRecordName + "')");
// >>> close the statement and connection
testStat.close();
testConn.close();
}
private DataSourceProxyXANative createDataSourceProxyXANative() throws Throwable {
XADataSource originalDS = createNewDruidXADataSource();
DataSourceProxyXANative dataSourceProxyXA = new DataSourceProxyXANative(originalDS);
return dataSourceProxyXA;
}
private DataSourceProxyXA createDataSourceProxyXA() throws Throwable {
DataSource originalDS = createNewDruidDataSource();
DataSourceProxyXA dataSourceProxyXA = new DataSourceProxyXA(originalDS);
return dataSourceProxyXA;
}
private GlobalTransaction createGlobalTransaction() {
String vgroup = "my_test_tx_group";
GlobalTransactionScanner scanner = new GlobalTransactionScanner(vgroup);
scanner.afterPropertiesSet();
GlobalTransaction gtx = GlobalTransactionContext.getCurrentOrCreate();
return gtx;
}
}

View File

@@ -0,0 +1,2 @@
io.seata.common.loader.LoaderTestImpl1
io.seata.common.loader.LoaderTestImpl2

View File

@@ -0,0 +1 @@
io.seata.saga.engine.mock.MockStateHandlerInterceptor

View File

@@ -0,0 +1 @@
io.seata.saga.engine.mock.MockStateRouterInterceptor

View File

@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 1999-2019 Seata.io Group.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="mysqlDataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="url" value="jdbc:mysql://xxx:3306/demo"/>
<property name="username" value="xxx"/>
<property name="password" value="xxx"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="initialSize" value="0" />
<property name="maxActive" value="180" />
<property name="minIdle" value="0" />
<property name="maxWait" value="60000" />
<property name="validationQuery" value="Select 'x' from DUAL" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<property name="testWhileIdle" value="true" />
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<property name="minEvictableIdleTimeMillis" value="25200000" />
<property name="removeAbandoned" value="true" />
<property name="removeAbandonedTimeout" value="1800" />
<property name="logAbandoned" value="true" />
<property name="filters" value="mergeStat" />
</bean>
<bean id="dataSourceProxy" class="io.seata.rm.datasource.DataSourceProxy">
<constructor-arg ref="mysqlDataSource" />
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSourceProxy" />
</bean>
<bean id="directJdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="mysqlDataSource" />
</bean>
</beans>

View File

@@ -0,0 +1,13 @@
CREATE TABLE `user0` (
`id` int(11) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`gmt` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `user1` (
`id` int(11) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`gmt` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

View File

@@ -0,0 +1,22 @@
service {
#transaction service group mapping
vgroupMapping.my_test_tx_group = "default"
#only support when registry.type=file, please don't set multiple addresses
default.grouplist = "127.0.0.1:8091"
#disable seata
disableGlobalTransaction = false
}
client {
rm {
reportSuccessEnable = false
sagaBranchRegisterEnable = false
sagaJsonParser = jackson
sagaRetryPersistModeUpdate = false
sagaCompensatePersistModeUpdate = false
}
loadBalance {
type = "RandomLoadBalance"
virtualNodes = 10
}
}

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
~ Copyright 1999-2019 Seata.io Group.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<configuration>
<!-- Console output log -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%date [SEATA] [%thread] %-5level %logger{80} - %msg%n</Pattern>
</layout>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>

View File

@@ -0,0 +1,73 @@
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "file"
nacos {
serverAddr = "localhost"
namespace = ""
cluster = "default"
}
eureka {
serviceUrl = "http://localhost:8761/eureka"
application = "default"
weight = "1"
}
redis {
serverAddr = "localhost:6379"
db = "0"
}
zk {
cluster = "default"
serverAddr = "127.0.0.1:2181"
sessionTimeout = 6000
connectTimeout = 2000
}
consul {
cluster = "default"
serverAddr = "127.0.0.1:8500"
}
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 = "localhost"
namespace = ""
}
consul {
serverAddr = "127.0.0.1:8500"
}
apollo {
appId = "seata-server"
apolloMeta = "http://192.168.1.204:8801"
}
zk {
serverAddr = "127.0.0.1:2181"
sessionTimeout = 6000
connectTimeout = 2000
}
etcd3 {
serverAddr = "http://localhost:2379"
}
file {
name = "file.conf"
}
}

View File

@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 1999-2019 Seata.io Group.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd">
<bean id="dataSource" class="org.h2.jdbcx.JdbcConnectionPool" destroy-method="dispose">
<constructor-arg>
<bean class="org.h2.jdbcx.JdbcDataSource">
<property name="URL" value="jdbc:h2:mem:seata_saga" />
<property name="user" value="sa" />
<property name="password" value="sa" />
</bean>
</constructor-arg>
</bean>
<!-- 初始化数据表结构 -->
<jdbc:initialize-database data-source="dataSource">
<jdbc:script location="classpath:saga/sql/h2_init.sql" />
</jdbc:initialize-database>
<bean id="stateMachineEngine" class="io.seata.saga.engine.impl.ProcessCtrlStateMachineEngine">
<property name="stateMachineConfig" ref="dbStateMachineConfig"></property>
</bean>
<bean id="dbStateMachineConfig" class="io.seata.saga.engine.config.DbStateMachineConfig">
<property name="dataSource" ref="dataSource"></property>
<property name="tablePrefix" value="seata_"></property>
<property name="resources" value="saga/statelang/*.json"></property>
<property name="enableAsync" value="true"></property>
<property name="threadPoolExecutor" ref="threadExecutor"></property>
<property name="applicationId" value="test_saga"></property>
<property name="txServiceGroup" value="my_test_tx_group"></property>
<property name="sagaTransactionalTemplate" ref="mockSagaTransactionTemplate"/>
</bean>
<bean id="threadExecutor"
class="org.springframework.scheduling.concurrent.ThreadPoolExecutorFactoryBean">
<property name="threadNamePrefix" value="SAGA_ASYNC_EXE_" />
<property name="corePoolSize" value="1" />
<property name="maxPoolSize" value="20" />
<property name="queueCapacity" value="100" />
<property name="rejectedExecutionHandler" ref="callerRunsPolicy" />
</bean>
<bean name="callerRunsPolicy" class="java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy">
</bean>
<bean class="io.seata.saga.rm.StateMachineEngineHolder">
<property name="stateMachineEngine" ref="stateMachineEngine"/>
</bean>
<bean id="demoService" class="io.seata.saga.engine.mock.DemoService"/>
<bean id="mockSagaTransactionTemplate" class="io.seata.saga.engine.mock.MockSagaTransactionTemplate"/>
</beans>

View File

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 1999-2019 Seata.io Group.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd">
<bean id="dataSource" class="org.h2.jdbcx.JdbcConnectionPool" destroy-method="dispose">
<constructor-arg>
<bean class="org.h2.jdbcx.JdbcDataSource">
<property name="URL" value="jdbc:h2:mem:seata_saga" />
<property name="user" value="sa" />
<property name="password" value="sa" />
</bean>
</constructor-arg>
</bean>
<!-- 初始化数据表结构 -->
<jdbc:initialize-database data-source="dataSource">
<jdbc:script location="classpath:saga/sql/h2_init.sql" />
</jdbc:initialize-database>
<bean id="stateMachineEngine" class="io.seata.saga.engine.impl.ProcessCtrlStateMachineEngine">
<property name="stateMachineConfig" ref="dbStateMachineConfig"></property>
</bean>
<bean id="dbStateMachineConfig" class="io.seata.saga.engine.config.DbStateMachineConfig">
<property name="dataSource" ref="dataSource"></property>
<property name="resources" value="saga/statelang/*.json"></property>
<property name="enableAsync" value="true"></property>
<property name="threadPoolExecutor" ref="threadExecutor"></property>
<property name="applicationId" value="test_saga"></property>
<property name="txServiceGroup" value="my_test_tx_group"></property>
<property name="sagaBranchRegisterEnable" value="false"></property>
<property name="sagaJsonParser" value="jackson"></property>
<property name="sagaRetryPersistModeUpdate" value="false" />
<property name="sagaCompensatePersistModeUpdate" value="false" />
</bean>
<bean id="threadExecutor"
class="org.springframework.scheduling.concurrent.ThreadPoolExecutorFactoryBean">
<property name="threadNamePrefix" value="SAGA_ASYNC_EXE_" />
<property name="corePoolSize" value="20" />
<property name="maxPoolSize" value="20" />
<property name="queueCapacity" value="100" />
<property name="rejectedExecutionHandler" ref="callerRunsPolicy" />
</bean>
<bean name="callerRunsPolicy" class="java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy">
</bean>
<bean class="io.seata.saga.rm.StateMachineEngineHolder">
<property name="stateMachineEngine" ref="stateMachineEngine"/>
</bean>
<bean id="demoService" class="io.seata.saga.engine.mock.DemoService"/>
</beans>

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 1999-2019 Seata.io Group.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="stateMachineEngine" class="io.seata.saga.engine.impl.ProcessCtrlStateMachineEngine">
<property name="stateMachineConfig" ref="defaultStateMachineConfig"></property>
</bean>
<bean id="defaultStateMachineConfig" class="io.seata.saga.engine.impl.DefaultStateMachineConfig">
<property name="resources" value="saga/statelang/*.json"></property>
<property name="enableAsync" value="true"></property>
<property name="threadPoolExecutor" ref="threadExecutor" />
</bean>
<bean id="threadExecutor"
class="org.springframework.scheduling.concurrent.ThreadPoolExecutorFactoryBean">
<property name="threadNamePrefix" value="SAGA_ASYNC_EXE_" />
<property name="corePoolSize" value="1" />
<property name="maxPoolSize" value="20" />
<property name="queueCapacity" value="100" />
<property name="rejectedExecutionHandler" ref="callerRunsPolicy" />
</bean>
<bean name="callerRunsPolicy" class="java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy">
</bean>
<bean id="demoService" class="io.seata.saga.engine.mock.DemoService"/>
</beans>

View File

@@ -0,0 +1,64 @@
create table seata_state_machine_def
(
id varchar(32) not null,
name varchar(128) not null,
tenant_id varchar(32) not null,
app_name varchar(32) not null,
type varchar(20),
comment_ varchar(255),
ver varchar(16) not null,
gmt_create timestamp(3) not null,
status varchar(2) not null,
content clob(65536) inline length 2048,
recover_strategy varchar(16),
primary key(id)
);
create table seata_state_machine_inst
(
id varchar(128) not null,
machine_id varchar(32) not null,
tenant_id varchar(32) not null,
parent_id varchar(128),
gmt_started timestamp(3) not null,
business_key varchar(48),
uni_business_key varchar(128) not null generated always as( --Unique index does not allow empty columns on DB2
CASE
WHEN "BUSINESS_KEY" IS NULL
THEN "ID"
ELSE "BUSINESS_KEY"
END),
start_params clob(65536) inline length 1024,
gmt_end timestamp(3),
excep blob(10240),
end_params clob(65536) inline length 1024,
status varchar(2),
compensation_status varchar(2),
is_running smallint,
gmt_updated timestamp(3) not null,
primary key(id)
);
create unique index state_machine_inst_unibuzkey on seata_state_machine_inst(uni_business_key, tenant_id);
create table seata_state_inst
(
id varchar(48) not null,
machine_inst_id varchar(128) not null,
name varchar(128) not null,
type varchar(20),
service_name varchar(128),
service_method varchar(128),
service_type varchar(16),
business_key varchar(48),
state_id_compensated_for varchar(50),
state_id_retried_for varchar(50),
gmt_started timestamp(3) not null,
is_for_update smallint,
input_params clob(65536) inline length 1024,
output_params clob(65536) inline length 1024,
status varchar(2) not null,
excep blob(10240),
gmt_updated timestamp(3),
gmt_end timestamp(3),
primary key(id, machine_inst_id)
);

View File

@@ -0,0 +1,58 @@
create table if not exists seata_state_machine_def
(
id varchar(32) not null comment 'id',
name varchar(128) not null comment 'name',
tenant_id varchar(32) not null comment 'tenant id',
app_name varchar(32) not null comment 'application name',
type varchar(20) comment 'state language type',
comment_ varchar(255) comment 'comment',
ver varchar(16) not null comment 'version',
gmt_create timestamp(3) not null comment 'create time',
status varchar(2) not null comment 'status(AC:active|IN:inactive)',
content clob comment 'content',
recover_strategy varchar(16) comment 'transaction recover strategy(compensate|retry)',
primary key (id)
);
create table if not exists seata_state_machine_inst
(
id varchar(128) not null comment 'id',
machine_id varchar(32) not null comment 'state machine definition id',
tenant_id varchar(32) not null comment 'tenant id',
parent_id varchar(128) comment 'parent id',
gmt_started timestamp(3) not null comment 'start time',
business_key varchar(48) comment 'business key',
start_params clob comment 'start parameters',
gmt_end timestamp(3) comment 'end time',
excep blob comment 'exception',
end_params clob comment 'end parameters',
status varchar(2) comment 'status(SU succeed|FA failed|UN unknown|SK skipped|RU running)',
compensation_status varchar(2) comment 'compensation status(SU succeed|FA failed|UN unknown|SK skipped|RU running)',
is_running tinyint(1) comment 'is running(0 no|1 yes)',
gmt_updated timestamp(3) not null,
primary key (id),
unique key unikey_buz_tenant (business_key, tenant_id)
);
create table if not exists seata_state_inst
(
id varchar(48) not null comment 'id',
machine_inst_id varchar(128) not null comment 'state machine instance id',
name varchar(128) not null comment 'state name',
type varchar(20) comment 'state type',
service_name varchar(128) comment 'service name',
service_method varchar(128) comment 'method name',
service_type varchar(16) comment 'service type',
business_key varchar(48) comment 'business key',
state_id_compensated_for varchar(50) comment 'state compensated for',
state_id_retried_for varchar(50) comment 'state retried for',
gmt_started timestamp(3) not null comment 'start time',
is_for_update tinyint(1) comment 'is service for update',
input_params clob comment 'input parameters',
output_params clob comment 'output parameters',
status varchar(2) not null comment 'status(SU succeed|FA failed|UN unknown|SK skipped|RU running)',
excep blob comment 'exception',
gmt_updated timestamp(3) comment 'update time',
gmt_end timestamp(3) comment 'end time',
primary key (id, machine_inst_id)
);

View File

@@ -0,0 +1,64 @@
-- -------------------------------- The script used for sage --------------------------------
CREATE TABLE IF NOT EXISTS `seata_state_machine_def`
(
`id` VARCHAR(32) NOT NULL COMMENT 'id',
`name` VARCHAR(128) NOT NULL COMMENT 'name',
`tenant_id` VARCHAR(32) NOT NULL COMMENT 'tenant id',
`app_name` VARCHAR(32) NOT NULL COMMENT 'application name',
`type` VARCHAR(20) COMMENT 'state language type',
`comment_` VARCHAR(255) COMMENT 'comment',
`ver` VARCHAR(16) NOT NULL COMMENT 'version',
`gmt_create` DATETIME(3) NOT NULL COMMENT 'create time',
`status` VARCHAR(2) NOT NULL COMMENT 'status(AC:active|IN:inactive)',
`content` TEXT COMMENT 'content',
`recover_strategy` VARCHAR(16) COMMENT 'transaction recover strategy(compensate|retry)',
PRIMARY KEY (`id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
CREATE TABLE IF NOT EXISTS `seata_state_machine_inst`
(
`id` VARCHAR(128) NOT NULL COMMENT 'id',
`machine_id` VARCHAR(32) NOT NULL COMMENT 'state machine definition id',
`tenant_id` VARCHAR(32) NOT NULL COMMENT 'tenant id',
`parent_id` VARCHAR(128) COMMENT 'parent id',
`gmt_started` DATETIME(3) NOT NULL COMMENT 'start time',
`business_key` VARCHAR(48) COMMENT 'business key',
`start_params` TEXT COMMENT 'start parameters',
`gmt_end` DATETIME(3) COMMENT 'end time',
`excep` BLOB COMMENT 'exception',
`end_params` TEXT COMMENT 'end parameters',
`status` VARCHAR(2) COMMENT 'status(SU succeed|FA failed|UN unknown|SK skipped|RU running)',
`compensation_status` VARCHAR(2) COMMENT 'compensation status(SU succeed|FA failed|UN unknown|SK skipped|RU running)',
`is_running` TINYINT(1) COMMENT 'is running(0 no|1 yes)',
`gmt_updated` DATETIME(3) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `unikey_buz_tenant` (`business_key`, `tenant_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
CREATE TABLE IF NOT EXISTS `seata_state_inst`
(
`id` VARCHAR(48) NOT NULL COMMENT 'id',
`machine_inst_id` VARCHAR(128) NOT NULL COMMENT 'state machine instance id',
`name` VARCHAR(128) NOT NULL COMMENT 'state name',
`type` VARCHAR(20) COMMENT 'state type',
`service_name` VARCHAR(128) COMMENT 'service name',
`service_method` VARCHAR(128) COMMENT 'method name',
`service_type` VARCHAR(16) COMMENT 'service type',
`business_key` VARCHAR(48) COMMENT 'business key',
`state_id_compensated_for` VARCHAR(50) COMMENT 'state compensated for',
`state_id_retried_for` VARCHAR(50) COMMENT 'state retried for',
`gmt_started` DATETIME(3) NOT NULL COMMENT 'start time',
`is_for_update` TINYINT(1) COMMENT 'is service for update',
`input_params` TEXT COMMENT 'input parameters',
`output_params` TEXT COMMENT 'output parameters',
`status` VARCHAR(2) NOT NULL COMMENT 'status(SU succeed|FA failed|UN unknown|SK skipped|RU running)',
`excep` BLOB COMMENT 'exception',
`gmt_updated` DATETIME(3) COMMENT 'update time',
`gmt_end` DATETIME(3) COMMENT 'end time',
PRIMARY KEY (`id`, `machine_inst_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;

View File

@@ -0,0 +1,64 @@
CREATE TABLE seata_state_machine_def
(
id VARCHAR(32) NOT NULL,
name VARCHAR(128) NOT NULL,
tenant_id VARCHAR(32) NOT NULL,
app_name VARCHAR(32) NOT NULL,
type VARCHAR(20),
comment_ VARCHAR(255),
ver VARCHAR(16) NOT NULL,
gmt_create TIMESTAMP(3) NOT NULL,
status VARCHAR(2) NOT NULL,
content CLOB,
recover_strategy VARCHAR(16),
PRIMARY KEY (id)
);
CREATE TABLE seata_state_machine_inst
(
id VARCHAR(128) NOT NULL,
machine_id VARCHAR(32) NOT NULL,
tenant_id VARCHAR(32) NOT NULL,
parent_id VARCHAR(128),
gmt_started TIMESTAMP(3) NOT NULL,
business_key VARCHAR(48),
uni_business_key VARCHAR(128) GENERATED ALWAYS AS (
CASE
WHEN "BUSINESS_KEY" IS NULL
THEN "ID"
ELSE "BUSINESS_KEY"
END),
start_params CLOB,
gmt_end TIMESTAMP(3),
excep BLOB,
end_params CLOB,
status VARCHAR(2),
compensation_status VARCHAR(2),
is_running SMALLINT,
gmt_updated TIMESTAMP(3) NOT NULL,
PRIMARY KEY (id)
);
CREATE UNIQUE INDEX state_machine_inst_unibuzkey ON seata_state_machine_inst (uni_business_key, tenant_id);
CREATE TABLE seata_state_inst
(
id VARCHAR(48) NOT NULL,
machine_inst_id VARCHAR(46) NOT NULL,
name VARCHAR(128) NOT NULL,
type VARCHAR(20),
service_name VARCHAR(128),
service_method VARCHAR(128),
service_type VARCHAR(16),
business_key VARCHAR(48),
state_id_compensated_for VARCHAR(50),
state_id_retried_for VARCHAR(50),
gmt_started TIMESTAMP(3) NOT NULL,
is_for_update SMALLINT,
input_params CLOB,
output_params CLOB,
status VARCHAR(2) NOT NULL,
excep BLOB,
gmt_updated TIMESTAMP(3),
gmt_end TIMESTAMP(3),
PRIMARY KEY (id, machine_inst_id)
);

View File

@@ -0,0 +1,260 @@
{
"nodes": [
{
"type": "node",
"size": "72*72",
"shape": "flow-circle",
"color": "#FA8C16",
"label": "Start",
"stateId": "Start1",
"stateType": "Start",
"stateProps": {
"StateMachine": {
"Name": "designerSimpleScriptTaskStateMachine",
"Comment": "带ScriptTask的测试状态机定义",
"Version": "0.0.1"
}
},
"x": 318.875,
"y": 102,
"id": "9e56dbe7"
},
{
"type": "node",
"size": "110*48",
"shape": "flow-rect",
"color": "#1890FF",
"label": "FirstState",
"stateId": "FirstState",
"stateType": "ServiceTask",
"stateProps": {
"ServiceName": "demoService",
"ServiceMethod": "foo",
"Input": [
{
"fooInput": "$.[a]"
}
],
"Output": {
"fooResult": "$.#root"
}
},
"x": 318.875,
"y": 202,
"id": "d1d71346"
},
{
"type": "node",
"size": "110*48",
"shape": "flow-rect",
"color": "#13C2C2",
"label": "ScriptState",
"stateId": "ScriptState",
"stateType": "ScriptTask",
"stateProps": {
"ScriptType": "groovy",
"ScriptContent": "if(throwException){ throw new RuntimeException(\"test\") } else { 'hello ' + inputA }",
"Input": [
{
"inputA": "$.[a]",
"throwException": "$.[scriptThrowException]"
}
],
"Output": {
"scriptStateResult": "$.#root"
}
},
"x": 319.375,
"y": 290.5,
"id": "898649d3"
},
{
"type": "node",
"size": "80*72",
"shape": "flow-rhombus",
"color": "#13C2C2",
"label": "Choice",
"stateId": "Choice1",
"stateType": "Choice",
"x": 318.875,
"y": 394.5,
"id": "39859d0b"
},
{
"type": "node",
"size": "110*48",
"shape": "flow-rect",
"color": "#1890FF",
"label": "SecondState",
"stateId": "SecondState",
"stateType": "ServiceTask",
"stateProps": {
"ServiceName": "demoService",
"ServiceMethod": "bar",
"Input": [
{
"barInput": "$.[fooResult]"
}
],
"Output": {
"barResult": "$.#root"
}
},
"x": 173.375,
"y": 493.49999999999994,
"id": "c65f1c20"
},
{
"type": "node",
"size": "110*48",
"shape": "flow-rect",
"color": "#1890FF",
"label": "ThirdState",
"stateId": "ThirdState",
"stateType": "ServiceTask",
"stateProps": {
"ServiceName": "demoService",
"ServiceMethod": "foo",
"Input": [
{
"fooInput": "$.[fooResult]"
}
],
"Output": {
"fooResult": "$.#root"
}
},
"x": 318.875,
"y": 492.99999999999994,
"id": "5857047e"
},
{
"type": "node",
"size": "72*72",
"shape": "flow-circle",
"color": "#05A465",
"label": "Succeed",
"stateId": "Succeed1",
"stateType": "Succeed",
"x": 318.875,
"y": 592,
"id": "18bba9a0"
},
{
"type": "node",
"size": "72*72",
"shape": "flow-circle",
"color": "red",
"label": "Fail",
"stateId": "Fail1",
"stateType": "Fail",
"stateProps": {
"ErrorCode": "",
"Message": ""
},
"x": 474.57499999999993,
"y": 499.99999999999994,
"id": "95def921"
},
{
"type": "node",
"size": "39*39",
"shape": "flow-circle",
"color": "red",
"label": "Catch",
"stateId": "Catch1",
"stateType": "Catch",
"x": 374.375,
"y": 315,
"id": "e264be2d"
}
],
"edges": [
{
"source": "9e56dbe7",
"sourceAnchor": 2,
"target": "d1d71346",
"targetAnchor": 0,
"id": "594d5078"
},
{
"source": "d1d71346",
"sourceAnchor": 2,
"target": "898649d3",
"targetAnchor": 0,
"id": "e1f56d5c"
},
{
"source": "898649d3",
"sourceAnchor": 2,
"target": "39859d0b",
"targetAnchor": 0,
"id": "d25b97b3"
},
{
"source": "39859d0b",
"sourceAnchor": 3,
"target": "c65f1c20",
"targetAnchor": 0,
"id": "d957fcff",
"stateProps": {
"Expression": "[a] == 1",
"Default": false
},
"label": "",
"shape": "flow-smooth"
},
{
"source": "39859d0b",
"sourceAnchor": 2,
"target": "5857047e",
"targetAnchor": 0,
"id": "d7053595",
"stateProps": {
"Expression": "[a] == 2",
"Default": false
},
"label": "",
"shape": "flow-smooth"
},
{
"source": "e264be2d",
"sourceAnchor": 1,
"target": "95def921",
"targetAnchor": 0,
"id": "39925f4b",
"stateProps": {
"Exceptions": [
"java.lang.Throwable"
]
}
},
{
"source": "39859d0b",
"sourceAnchor": 1,
"target": "95def921",
"targetAnchor": 0,
"id": "5500c8da",
"stateProps": {
"Expression": "",
"Default": true
},
"label": "",
"shape": "flow-smooth"
},
{
"source": "c65f1c20",
"sourceAnchor": 2,
"target": "18bba9a0",
"targetAnchor": 3,
"id": "33832e0d"
},
{
"source": "5857047e",
"sourceAnchor": 2,
"target": "18bba9a0",
"targetAnchor": 0,
"id": "1191f8f0"
}
]
}

View File

@@ -0,0 +1,288 @@
{
"nodes": [
{
"type": "node",
"size": "72*72",
"shape": "flow-circle",
"color": "#FA8C16",
"label": "Start",
"stateId": "Start",
"stateType": "Start",
"stateProps": {
"StateMachine": {
"Name": "simpleStateMachineWithCompensationAndSubMachine_layout",
"Comment": "带补偿定义和调用子状态机",
"Version": "0.0.1"
}
},
"x": 199.875,
"y": 95,
"id": "e2d86441"
},
{
"type": "node",
"size": "110*48",
"shape": "flow-rect",
"color": "#1890FF",
"label": "FirstState",
"stateId": "FirstState",
"stateType": "ServiceTask",
"stateProps": {
"ServiceName": "demoService",
"ServiceMethod": "foo",
"Input": [
{
"fooInput": "$.[a]"
}
],
"Output": {
"fooResult": "$.#root"
}
},
"x": 199.875,
"y": 213,
"id": "6111bf54"
},
{
"type": "node",
"size": "80*72",
"shape": "flow-rhombus",
"color": "#13C2C2",
"label": "ChoiceState",
"stateId": "ChoiceState",
"stateType": "Choice",
"x": 199.875,
"y": 341.5,
"id": "5610fa37"
},
{
"type": "node",
"size": "110*48",
"shape": "flow-rect",
"color": "#1890FF",
"label": "SecondState",
"stateId": "SecondState",
"stateType": "ServiceTask",
"stateProps": {
"ServiceName": "demoService",
"ServiceMethod": "bar",
"Input": [
{
"barInput": "$.[fooResult]",
"throwException": "$.[barThrowException]"
}
],
"Output": {
"barResult": "$.#root"
},
"Status": {
"#root != null": "SU",
"#root == null": "FA",
"$Exception{io.seata.saga.engine.exception.EngineExecutionException}": "UN"
}
},
"x": 199.375,
"y": 468,
"id": "af5591f9"
},
{
"type": "node",
"size": "72*72",
"shape": "flow-circle",
"color": "#05A465",
"label": "Succeed",
"stateId": "Succeed",
"stateType": "Succeed",
"x": 199.375,
"y": 609,
"id": "2fd4c8de"
},
{
"type": "node",
"size": "110*48",
"shape": "flow-rect",
"color": "#FA8C16",
"label": "SubStateMachine",
"stateId": "CallSubStateMachine",
"stateType": "SubStateMachine",
"stateProps": {
"StateMachineName": "simpleCompensationStateMachine",
"Input": [
{
"a": "$.1",
"barThrowException": "$.[barThrowException]",
"fooThrowException": "$.[fooThrowException]",
"compensateFooThrowException": "$.[compensateFooThrowException]"
}
],
"Output": {
"fooResult": "$.#root"
}
},
"x": 55.875,
"y": 467,
"id": "04ea55a5"
},
{
"type": "node",
"size": "110*48",
"shape": "flow-capsule",
"color": "#722ED1",
"label": "CompenFirstState",
"stateId": "CompensateFirstState",
"stateType": "Compensation",
"stateProps": {
"ServiceName": "demoService",
"ServiceMethod": "compensateFoo",
"Input": [
{
"compensateFooInput": "$.[fooResult]"
}
]
},
"x": 68.875,
"y": 126,
"id": "6a09a5c2"
},
{
"type": "node",
"size": "39*39",
"shape": "flow-circle",
"color": "red",
"label": "Catch",
"stateId": "Catch",
"stateType": "Catch",
"x": 257.875,
"y": 492,
"id": "e28af1c2"
},
{
"type": "node",
"size": "110*48",
"shape": "flow-capsule",
"color": "red",
"label": "Compensation\nTrigger",
"stateId": "CompensationTrigger",
"stateType": "CompensationTrigger",
"x": 366.875,
"y": 491.5,
"id": "e32417a0"
},
{
"type": "node",
"size": "72*72",
"shape": "flow-circle",
"color": "red",
"label": "Fail",
"stateId": "Fail",
"stateType": "Fail",
"stateProps": {
"ErrorCode": "NOT_FOUND",
"Message": "not found"
},
"x": 513.375,
"y": 491.5,
"id": "d21d24c9"
}
],
"edges": [
{
"source": "e2d86441",
"sourceAnchor": 2,
"target": "6111bf54",
"targetAnchor": 0,
"id": "51f30b96"
},
{
"source": "6111bf54",
"sourceAnchor": 2,
"target": "5610fa37",
"targetAnchor": 0,
"id": "8c3029b1"
},
{
"source": "5610fa37",
"sourceAnchor": 2,
"target": "af5591f9",
"targetAnchor": 0,
"id": "a9e7d5b4",
"stateProps": {
"Expression": "[a] == 1",
"Default": false
},
"label": "",
"shape": "flow-smooth"
},
{
"source": "af5591f9",
"sourceAnchor": 2,
"target": "2fd4c8de",
"targetAnchor": 0,
"id": "61f34a49"
},
{
"source": "6111bf54",
"sourceAnchor": 3,
"target": "6a09a5c2",
"targetAnchor": 2,
"id": "553384ab",
"style": {
"lineDash": "4"
}
},
{
"source": "5610fa37",
"sourceAnchor": 3,
"target": "04ea55a5",
"targetAnchor": 0,
"id": "2ee91c33",
"stateProps": {
"Expression": "[a] == 2",
"Default": false
},
"label": "",
"shape": "flow-smooth"
},
{
"source": "e28af1c2",
"sourceAnchor": 1,
"target": "e32417a0",
"targetAnchor": 3,
"id": "d854a4d0",
"stateProps": {
"Exceptions": [
"io.seata.common.exception.FrameworkException"
]
},
"label": "",
"shape": "flow-smooth"
},
{
"source": "04ea55a5",
"sourceAnchor": 2,
"target": "2fd4c8de",
"targetAnchor": 3,
"id": "28734ad2"
},
{
"source": "5610fa37",
"sourceAnchor": 1,
"target": "d21d24c9",
"targetAnchor": 0,
"id": "7c7595c0",
"stateProps": {
"Expression": "",
"Default": true
},
"label": "",
"shape": "flow-smooth"
},
{
"source": "e32417a0",
"sourceAnchor": 1,
"target": "d21d24c9",
"targetAnchor": 3,
"id": "16d809ce"
}
]
}

View File

@@ -0,0 +1,19 @@
{
"Name": "simpleTestStateMachine",
"Comment": "测试状态机定义",
"StartState": "FirstState",
"Version": "0.0.2",
"States": {
"FirstState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "foo",
"Next": "SecondState"
},
"SecondState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "bar"
}
}
}

View File

@@ -0,0 +1,74 @@
{
"Name": "simpleInputAssignmentStateMachine",
"Comment": "带输入输出参数赋值的测试状态机定义",
"StartState": "FirstState",
"Version": "0.0.1",
"States": {
"FirstState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "foo",
"Next": "ChoiceState",
"Input": [
{
"fooInput": "$.[a]",
"fooBusinessKey": "$Sequence.BUSINESS_KEY|SIMPLE"
}
],
"Output": {
"fooResult": "$.#root"
}
},
"ChoiceState":{
"Type": "Choice",
"Choices":[
{
"Expression":"[a] == 1",
"Next":"SecondState"
},
{
"Expression":"[a] == 2",
"Next":"ThirdState"
}
],
"Default":"Fail"
},
"SecondState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "bar",
"Input": [
{
"barInput": "$.[fooResult]"
}
],
"Output": {
"barResult": "$.#root"
},
"Next": "Succeed"
},
"ThirdState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "foo",
"Input": [
{
"fooInput": "[fooResult][a].list[0]"
}
],
"Output": {
"fooResult": "$.#root"
},
"listener":"",
"Next": "Succeed"
},
"Succeed": {
"Type":"Succeed"
},
"Fail": {
"Type":"Fail",
"ErrorCode": "NOT_FOUND",
"Message": "not found"
}
}
}

View File

@@ -0,0 +1,50 @@
{
"Name": "simpleStateMachineWithAsyncState",
"Comment": "带异步执行节点的测试状态机定义",
"StartState": "FirstState",
"Version": "0.0.1",
"States": {
"FirstState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "foo",
"Next": "ChoiceState"
},
"ChoiceState":{
"Type": "Choice",
"Choices":[
{
"Expression":"[a] == 1",
"Next":"SecondState"
},
{
"Expression":"[a] == 2",
"Next":"ThirdState"
}
],
"Default":"Fail"
},
"SecondState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "bar",
"IsAsync": true,
"Next": "Succeed"
},
"ThirdState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "foo",
"IsAsync": true,
"Next": "Succeed"
},
"Succeed": {
"Type":"Succeed"
},
"Fail": {
"Type":"Fail",
"ErrorCode": "NOT_FOUND",
"Message": "not found"
}
}
}

View File

@@ -0,0 +1,81 @@
{
"Name": "simpleCachesStateMachine",
"Comment": "带Caches异常路由的测试状态机定义",
"StartState": "FirstState",
"Version": "0.0.1",
"States": {
"FirstState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "foo",
"Next": "ChoiceState",
"Input": [
{
"fooInput": "$.[a]"
}
],
"Output": {
"fooResult": "$.#root"
}
},
"ChoiceState":{
"Type": "Choice",
"Choices":[
{
"Expression":"[a] == 1",
"Next":"SecondState"
},
{
"Expression":"[a] == 2",
"Next":"ThirdState"
}
],
"Default":"Fail"
},
"SecondState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "bar",
"Input": [
{
"barInput": "$.[fooResult]",
"throwException": "$.[barThrowException]"
}
],
"Output": {
"barResult": "$.#root"
},
"Catch": [
{
"Exceptions": [
"io.seata.saga.engine.mock.DemoException"
],
"Next": "Fail"
}
],
"Next": "Succeed"
},
"ThirdState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "foo",
"Input": [
{
"fooInput": "$.[fooResult]"
}
],
"Output": {
"fooResult": "$.#root"
},
"Next": "Succeed"
},
"Succeed": {
"Type":"Succeed"
},
"Fail": {
"Type":"Fail",
"ErrorCode": "NOT_FOUND",
"Message": "not found"
}
}
}

View File

@@ -0,0 +1,38 @@
{
"Name": "simpleChoiceTestStateMachine",
"Comment": "带条件分支的测试状态机定义",
"StartState": "FirstState",
"Version": "0.0.1",
"States": {
"FirstState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "foo",
"Next": "ChoiceState"
},
"ChoiceState":{
"Type": "Choice",
"Choices":[
{
"Expression":"[a] == 1",
"Next":"SecondState"
},
{
"Expression":"[a] == 2",
"Next":"ThirdState"
}
],
"Default":"SecondState"
},
"SecondState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "bar"
},
"ThirdState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "foo"
}
}
}

View File

@@ -0,0 +1,48 @@
{
"Name": "simpleChoiceAndEndTestStateMachine",
"Comment": "带条件分支和结束状态的测试状态机定义",
"StartState": "FirstState",
"Version": "0.0.1",
"States": {
"FirstState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "foo",
"Next": "ChoiceState"
},
"ChoiceState":{
"Type": "Choice",
"Choices":[
{
"Expression":"[a] == 1",
"Next":"SecondState"
},
{
"Expression":"[a] == 2",
"Next":"ThirdState"
}
],
"Default":"Fail"
},
"SecondState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "bar",
"Next": "Succeed"
},
"ThirdState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "foo",
"Next": "Succeed"
},
"Succeed": {
"Type":"Succeed"
},
"Fail": {
"Type":"Fail",
"ErrorCode": "NOT_FOUND",
"Message": "not found"
}
}
}

View File

@@ -0,0 +1,38 @@
{
"Name": "simpleChoiceNoDefaultTestStateMachine",
"Comment": "带条件分支但没有默认分支的测试状态机定义",
"StartState": "FirstState",
"Version": "0.0.1",
"States": {
"FirstState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "foo",
"IsForUpdate": true,
"Next": "ChoiceState"
},
"ChoiceState":{
"Type": "Choice",
"Choices":[
{
"Expression":"[a] == 1",
"Next":"SecondState"
},
{
"Expression":"[a] == 2",
"Next":"ThirdState"
}
]
},
"SecondState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "bar"
},
"ThirdState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "foo"
}
}
}

View File

@@ -0,0 +1,130 @@
{
"Name": "simpleCompensationStateMachine",
"Comment": "带补偿定义的测试状态机定义",
"StartState": "FirstState",
"Version": "0.0.1",
"States": {
"FirstState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "foo",
"CompensateState": "CompensateFirstState",
"Next": "ChoiceState",
"Input": [
{
"fooInput": "$.[a]",
"throwException": "$.[fooThrowException]",
"sleepTime": "$.[fooSleepTime]"
}
],
"Output": {
"fooResult": "$.#root"
},
"Status": {
"$Exception{io.seata.saga.engine.mock.DemoException}": "UN",
"#root != null": "SU",
"#root == null": "FA"
},
"Catch": [
{
"Exceptions": [
"io.seata.saga.engine.mock.DemoException"
],
"Next": "CompensationTrigger"
}
]
},
"ChoiceState":{
"Type": "Choice",
"Choices":[
{
"Expression":"[a] == 1",
"Next":"SecondState"
},
{
"Expression":"[a] == 2",
"Next":"ThirdState"
}
],
"Default":"Fail"
},
"SecondState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "bar",
"CompensateState": "CompensateSecondState",
"Input": [
{
"barInput": "$.[fooResult]",
"throwException": "$.[barThrowException]",
"sleepTime": "$.[barSleepTime]"
}
],
"Output": {
"barResult": "$.#root"
},
"Status": {
"$Exception{io.seata.saga.engine.mock.DemoException}": "UN",
"#root != null": "SU",
"#root == null": "FA"
},
"Catch": [
{
"Exceptions": [
"io.seata.saga.engine.mock.DemoException"
],
"Next": "CompensationTrigger"
}
],
"Next": "Succeed"
},
"ThirdState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "foo",
"Input": [
{
"fooInput": "$.[fooResult]"
}
],
"Output": {
"fooResult": "$.#root"
},
"Next": "Succeed"
},
"CompensateFirstState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "compensateFoo",
"Input": [
{
"compensateFooInput": "$.[fooResult]",
"throwException": "$.[compensateFooThrowException]"
}
]
},
"CompensateSecondState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "compensateBar",
"Input": [
{
"compensateBarInput": "$.[barResult]",
"throwException": "$.[compensateBarThrowException]"
}
]
},
"CompensationTrigger": {
"Type": "CompensationTrigger",
"Next": "Fail"
},
"Succeed": {
"Type":"Succeed"
},
"Fail": {
"Type":"Fail",
"ErrorCode": "NOT_FOUND",
"Message": "not found"
}
}
}

View File

@@ -0,0 +1,126 @@
{
"Name": "simpleStateMachineWithCompensationAndSubMachine",
"Comment": "带补偿定义和调用子状态机",
"StartState": "FirstState",
"Version": "0.0.1",
"IsRetryPersistModeUpdate": false,
"IsCompensatePersistModeUpdate": false,
"States": {
"FirstState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "foo",
"CompensateState": "CompensateFirstState",
"Next": "ChoiceState",
"Input": [
{
"fooInput": "$.[a]"
}
],
"Output": {
"fooResult": "$.#root"
}
},
"ChoiceState":{
"Type": "Choice",
"Choices":[
{
"Expression":"[a] == 1",
"Next":"SecondState"
},
{
"Expression":"[a] == 2",
"Next":"CallSubStateMachine"
},
{
"Expression": "[a] == 3 || [a] == 4",
"Next": "CallSubStateMachineAsUpdate"
}
],
"Default":"Fail"
},
"SecondState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "bar",
"Input": [
{
"barInput": "$.[fooResult]",
"throwException": "$.[barThrowException]"
}
],
"Output": {
"barResult": "$.#root"
},
"Status": {
"#root != null": "SU",
"#root == null": "FA",
"$Exception{io.seata.saga.engine.mock.DemoException}": "UN"
},
"Catch": [
{
"Exceptions": [
"io.seata.saga.engine.mock.DemoException"
],
"Next": "CompensationTrigger"
}
],
"Next": "Succeed"
},
"CallSubStateMachine": {
"Type": "SubStateMachine",
"StateMachineName": "simpleCompensationStateMachine",
"Input": [
{
"a": "$.1",
"barThrowException": "$.[barThrowException]",
"fooThrowException": "$.[fooThrowException]",
"compensateFooThrowException": "$.[compensateFooThrowException]"
}
],
"Output": {
"fooResult": "$.#root"
},
"Next": "Succeed"
},
"CallSubStateMachineAsUpdate": {
"Type": "SubStateMachine",
"StateMachineName": "simpleUpdateStateMachine",
"IsRetryPersistModeUpdate": true,
"IsCompensatePersistModeUpdate": true,
"Input": [
{
"a": "$.[a]-2",
"barThrowException": "$.[barThrowException]",
"compensateBarThrowException": "$.[compensateBarThrowException]"
}
],
"Output": {
"fooResult": "$.#root"
},
"Next": "Succeed"
},
"CompensateFirstState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "compensateFoo",
"Input": [
{
"compensateFooInput": "$.[fooResult]"
}
]
},
"CompensationTrigger": {
"Type": "CompensationTrigger",
"Next": "Fail"
},
"Succeed": {
"Type":"Succeed"
},
"Fail": {
"Type":"Fail",
"ErrorCode": "NOT_FOUND",
"Message": "not found"
}
}
}

View File

@@ -0,0 +1,139 @@
{
"Name": "simpleCompensationStateMachineForRecovery",
"Comment": "用于测试事务恢复的状态机定义",
"StartState": "FirstState",
"Version": "0.0.1",
"States": {
"FirstState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "foo",
"CompensateState": "CompensateFirstState",
"Next": "ChoiceState",
"Input": [
{
"fooInput": "$.[a]",
"throwExceptionRandomly": "$.[fooThrowExceptionRandomly]",
"throwException": "$.[fooThrowException]"
}
],
"Output": {
"fooResult": "$.#root"
},
"Status": {
"$Exception{io.seata.saga.engine.mock.DemoException}": "UN",
"#root != null && #root.size() > 0": "SU",
"#root == null || #root.size() == 0": "FA"
}
},
"ChoiceState":{
"Type": "Choice",
"Choices":[
{
"Expression":"[a] == 1",
"Next":"SecondState"
},
{
"Expression":"[a] == 2",
"Next":"ThirdState"
}
],
"Default":"Fail"
},
"SecondState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "bar",
"CompensateState": "CompensateSecondState",
"Input": [
{
"barInput": "$.[fooResult]",
"throwExceptionRandomly": "$.[barThrowExceptionRandomly]",
"throwException": "$.[barThrowException]"
}
],
"Output": {
"barResult": "$.#root"
},
"Status": {
"$Exception{io.seata.saga.engine.mock.DemoException}": "UN",
"#root != null && #root.size() > 0": "SU",
"#root == null || #root.size() == 0": "FA"
},
"Catch": [
{
"Exceptions": [
"io.seata.saga.engine.mock.DemoException"
],
"Next": "CompensationTrigger"
}
],
"Next": "Succeed"
},
"ThirdState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "foo",
"Input": [
{
"fooInput": "$.[fooResult]"
}
],
"Output": {
"fooResult": "$.#root"
},
"Status": {
"$Exception{io.seata.saga.engine.mock.DemoException}": "UN",
"#root != null && #root.size() > 0": "SU",
"#root == null || #root.size() == 0": "FA"
},
"Next": "Succeed"
},
"CompensateFirstState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "compensateFoo",
"Input": [
{
"compensateFooInput": "$.[a]",
"throwExceptionRandomly": "$.[compensateFooThrowExceptionRandomly]",
"throwException": "$.[compensateFooThrowException]"
}
],
"Status": {
"$Exception{io.seata.saga.engine.mock.DemoException}": "UN",
"#root != null && #root.size() > 0": "SU",
"#root == null || #root.size() == 0": "FA"
}
},
"CompensateSecondState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "compensateBar",
"Input": [
{
"compensateBarInput": "$.[a]",
"throwExceptionRandomly": "$.[compensateBarThrowExceptionRandomly]",
"throwException": "$.[compensateBarThrowException]"
}
],
"Status": {
"$Exception{io.seata.saga.engine.mock.DemoException}": "UN",
"#root != null && #root.size() > 0": "SU",
"#root == null || #root.size() == 0": "FA"
}
},
"CompensationTrigger": {
"Type": "CompensationTrigger",
"Next": "Fail"
},
"Succeed": {
"Type":"Succeed"
},
"Fail": {
"Type":"Fail",
"ErrorCode": "NOT_FOUND",
"Message": "not found"
}
}
}

View File

@@ -0,0 +1,119 @@
{
"Name": "simpleStateMachineWithComplexParams",
"Comment": "带复杂参数的测试状态机定义fastjson格式",
"StartState": "FirstState",
"Version": "0.0.1",
"States": {
"FirstState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "complexParameterMethod",
"Next": "ChoiceState",
"ParameterTypes" : ["java.lang.String", "int", "io.seata.saga.engine.mock.DemoService$People", "[Lio.seata.saga.engine.mock.DemoService$People;", "java.util.List", "java.util.Map"],
"Input": [
"$.[people].name",
"$.[people].age",
{
"name": "$.[people].name",
"age": "$.[people].age",
"childrenArray": [
{
"name": "$.[people].name",
"age": "$.[people].age"
},
{
"name": "$.[people].name",
"age": "$.[people].age"
}
],
"childrenList": [
{
"name": "$.[people].name",
"age": "$.[people].age"
},
{
"name": "$.[people].name",
"age": "$.[people].age"
}
],
"childrenMap": {
"lilei": {
"name": "$.[people].name",
"age": "$.[people].age"
}
}
},
[
{
"name": "$.[people].name",
"age": "$.[people].age"
},
{
"name": "$.[people].name",
"age": "$.[people].age"
}
],
[
{
"@type": "io.seata.saga.engine.mock.DemoService$People",
"name": "$.[people].name",
"age": "$.[people].age"
}
],
{
"lilei": {
"@type": "io.seata.saga.engine.mock.DemoService$People",
"name": "$.[people].name",
"age": "$.[people].age"
}
}
],
"Output": {
"complexParameterMethodResult": "$.#root"
}
},
"ChoiceState":{
"Type": "Choice",
"Choices":[
{
"Expression":"[complexParameterMethodResult].age > 0",
"Next":"SecondState"
},
{
"Expression":"[complexParameterMethodResult].age <= 0",
"Next":"ThirdState"
}
],
"Default":"Fail"
},
"SecondState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "interfaceParameterMethod",
"Input": [
"$.[career]"
],
"Output": {
"secondStateResult": "$.#root"
},
"Next": "ThirdState"
},
"ThirdState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "interfaceParameterMethod",
"Input": [
"$.[secondStateResult]"
],
"Next": "Succeed"
},
"Succeed": {
"Type":"Succeed"
},
"Fail": {
"Type":"Fail",
"ErrorCode": "NOT_FOUND",
"Message": "not found"
}
}
}

View File

@@ -0,0 +1,141 @@
{
"Name": "simpleStateMachineWithComplexParamsJackson",
"Comment": "带复杂参数的测试状态机定义jackson格式",
"StartState": "FirstState",
"Version": "0.0.1",
"States": {
"FirstState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "complexParameterMethod",
"Next": "ChoiceState",
"ParameterTypes" : ["java.lang.String", "int", "io.seata.saga.engine.mock.DemoService$People", "[Lio.seata.saga.engine.mock.DemoService$People;", "java.util.List", "java.util.Map"],
"Input": [
"$.[people].name",
"$.[people].age",
{
"@type": "io.seata.saga.engine.mock.DemoService$People",
"name": "lilei",
"age": 18,
"childrenArray": [
"[Lio.seata.saga.engine.mock.DemoService$People;",
[
{
"@type": "io.seata.saga.engine.mock.DemoService$People",
"name": "lilei",
"age": 18
},
{
"@type": "io.seata.saga.engine.mock.DemoService$People",
"name": "lilei",
"age": 18
}
]
],
"childrenList": [
"java.util.ArrayList",
[
{
"@type": "io.seata.saga.engine.mock.DemoService$People",
"name": "lilei",
"age": 18
},
{
"@type": "io.seata.saga.engine.mock.DemoService$People",
"name": "lilei",
"age": 18
}
]
],
"childrenMap": {
"@type": "java.util.LinkedHashMap",
"lilei": {
"@type": "io.seata.saga.engine.mock.DemoService$People",
"name": "lilei",
"age": 18
}
}
},
[
"[Lio.seata.saga.engine.mock.DemoService$People;",
[
{
"@type": "io.seata.saga.engine.mock.DemoService$People",
"name": "$.[people].name",
"age": "$.[people].age"
},
{
"@type": "io.seata.saga.engine.mock.DemoService$People",
"name": "$.[people].name",
"age": "$.[people].age"
}
]
],
[
"java.util.ArrayList",
[
{
"@type": "io.seata.saga.engine.mock.DemoService$People",
"name": "$.[people].name",
"age": "$.[people].age"
}
]
],
{
"@type": "java.util.LinkedHashMap",
"lilei": {
"@type": "io.seata.saga.engine.mock.DemoService$People",
"name": "$.[people].name",
"age": "$.[people].age"
}
}
],
"Output": {
"complexParameterMethodResult": "$.#root"
}
},
"ChoiceState":{
"Type": "Choice",
"Choices":[
{
"Expression":"[complexParameterMethodResult].age > 0",
"Next":"SecondState"
},
{
"Expression":"[complexParameterMethodResult].age <= 0",
"Next":"ThirdState"
}
],
"Default":"Fail"
},
"SecondState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "interfaceParameterMethod",
"Input": [
"$.[career]"
],
"Output": {
"secondStateResult": "$.#root"
},
"Next": "ThirdState"
},
"ThirdState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "interfaceParameterMethod",
"Input": [
"$.[secondStateResult]"
],
"Next": "Succeed"
},
"Succeed": {
"Type":"Succeed"
},
"Fail": {
"Type":"Fail",
"ErrorCode": "NOT_FOUND",
"Message": "not found"
}
}
}

View File

@@ -0,0 +1,137 @@
{
"Name": "simpleLoopTestStateMachine",
"Comment": "带循环参数的测试状态机定义",
"StartState": "FirstState",
"Version": "0.0.1",
"States": {
"FirstState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "foo",
"CompensateState": "CompensateFirstState",
"Loop": {
"Parallel": 3,
"Collection": "$.[collection]",
"ElementVariableName": "element",
"ElementIndexName": "loopCounter",
"CompletionCondition": "[nrOfCompletedInstances] == ([collection].size()-4)"
},
"Input": [
{
"loopCounter": "$.[loopCounter]",
"element": "$.[element]",
"throwException": "$.[fooThrowException]"
}
],
"Output": {
"fooResult": "$.#root"
},
"Next": "ChoiceState"
},
"ChoiceState":{
"Type": "Choice",
"Choices":[
{
"Expression": "[loopResult].?[#this[fooResult] == null].size() == 0 && [a] == 1",
"Next":"SecondState"
},
{
"Expression": "[loopResult].?[#this[fooResult] == null].size() == 0 && [a] == 2",
"Next":"CallSubStateMachine"
}
],
"Default":"Fail"
},
"SecondState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "bar",
"CompensateState": "CompensateSecondState",
"Loop": {
"Parallel": 3,
"Collection": "$.[collection]",
"ElementVariableName": "element",
"CompletionCondition": "[nrOfCompletedInstances] / [nrOfInstances] >= 0.4",
"ElementIndexName": "loopCounter"
},
"Input": [
{
"loopCounter": "$.[loopCounter]",
"loopElement": "$.[element]",
"throwException": "$.[barThrowException]"
}
],
"Catch": [
{
"Exceptions": [
"io.seata.saga.engine.mock.DemoException"
],
"Next": "CompensationTriggerTest"
}
]
},
"CallSubStateMachine": {
"Type": "SubStateMachine",
"StateMachineName": "simpleCompensationStateMachine",
"Loop": {
"Parallel": 3,
"Collection": "$.[collection]",
"ElementVariableName": "element",
"CompletionCondition": "[nrOfCompletedInstances] / [nrOfInstances] >= 0.4",
"ElementIndexName": "loopCounter"
},
"Input": [
{
"a": 1,
"collection": "$.[collection]",
"loopCounter": "$.[loopCounter]",
"element": "$.[element]",
"barThrowException": "$.[barThrowException]",
"fooThrowException": "$.[fooThrowException]",
"compensateFooThrowException": "$.[compensateFooThrowException]"
}
],
"Output": {
"fooResult": "$.#root"
},
"Next": "Succeed"
},
"CompensateFirstState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "compensateFoo",
"Input": [
{
"compensateFooInput": "$.[fooResult]",
"throwException": "$.[compensateFooThrowException]",
"loopCounter": "$.[loopCounter]",
"element": "$.[element]"
}
]
},
"CompensateSecondState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "compensateBar",
"Input": [
{
"compensateBarInput": "$.[barResult]",
"loopCounter": "$.[loopCounter]",
"loopElement": "$.[element]"
}
]
},
"CompensationTriggerTest": {
"Type": "CompensationTrigger",
"Next": "Fail"
},
"Succeed": {
"Type":"Succeed"
},
"Fail": {
"Type":"Fail",
"ErrorCode": "NOT_FOUND",
"Message": "not found"
}
}
}

View File

@@ -0,0 +1,116 @@
{
"Name": "simpleUpdateStateMachine",
"Comment": "自定义中间状态是否持久化的测试状态机定义",
"StartState": "FirstState",
"Version": "0.0.1",
"IsRetryPersistModeUpdate": true,
"IsCompensatePersistModeUpdate": true,
"States": {
"FirstState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "foo",
"Next": "ChoiceState",
"CompensateState": "CompensateFirstState",
"Input": [
{
"fooInput": "$.[a]"
}
],
"Output": {
"fooResult": "$.#root"
}
},
"ChoiceState":{
"Type": "Choice",
"Choices":[
{
"Expression":"[a] == 1",
"Next":"SecondState"
},
{
"Expression":"[a] == 2",
"Next":"ThirdState"
}
],
"Default":"Fail"
},
"SecondState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "bar",
"Input": [
{
"barInput": "$.[fooResult]",
"throwException": "$.[barThrowException]"
}
],
"Output": {
"barResult": "$.#root"
},
"Status": {
"$Exception{java.lang.Throwable}": "UN",
"#root != null": "SU",
"#root == null": "FA"
},
"Next": "Succeed"
},
"ThirdState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "randomExceptionMethod",
"CompensateState": "CompensateThirdState",
"Input": [
{
"barInput": "$.[fooResult]",
"throwException": "$.[barThrowException]"
}
],
"Output": {
"barResult": "$.#root"
},
"Status": {
"$Exception{java.lang.Throwable}": "UN",
"#root != null": "SU",
"#root == null": "FA"
},
"Catch": [
{
"Exceptions": [
"java.lang.Throwable"
],
"Next": "CompensationTrigger"
}
],
"Next": "Succeed"
},
"CompensateFirstState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "compensateFoo"
},
"CompensateThirdState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "compensateBar",
"Input": [
{
"compensateBarInput": "$.[barResult]",
"throwException": "$.[compensateBarThrowException]"
}
]
},
"CompensationTrigger": {
"Type": "CompensationTrigger",
"Next": "Fail"
},
"Succeed": {
"Type":"Succeed"
},
"Fail": {
"Type":"Fail",
"ErrorCode": "NOT_FOUND",
"Message": "not found"
}
}
}

View File

@@ -0,0 +1,89 @@
{
"Name": "simpleStateMachineWithRecoverStrategy",
"Comment": "带自定义恢复策略的测试状态机定义",
"StartState": "FirstState",
"Version": "0.0.1",
"RecoverStrategy": "Forward",
"States": {
"FirstState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "foo",
"Input": [
{
"fooInput": "$.[a]",
"throwExceptionRandomly": "$.[fooThrowExceptionRandomly]",
"sleepTime": "$.[fooSleepTime]"
}
],
"Output": {
"fooResult": "$.#root"
},
"Status": {
"$Exception{io.seata.saga.engine.mock.DemoException}": "UN",
"#root != null": "SU",
"#root == null": "FA"
},
"Catch": [
{
"Exceptions": [
"java.lang.Throwable"
],
"Next": "Fail"
}
],
"Next": "ChoiceState"
},
"ChoiceState":{
"Type": "Choice",
"Choices":[
{
"Expression":"[a] == 1",
"Next":"SecondState"
},
{
"Expression":"[a] == 2",
"Next":"Fail"
}
],
"Default":"Fail"
},
"SecondState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "bar",
"Input": [
{
"barInput": "$.[fooResult]",
"throwExceptionRandomly": "$.[barThrowExceptionRandomly]",
"sleepTime": "$.[barSleepTime]"
}
],
"Output": {
"barResult": "$.#root"
},
"Status": {
"$Exception{io.seata.saga.engine.mock.DemoException}": "UN",
"#root != null": "SU",
"#root == null": "FA"
},
"Catch": [
{
"Exceptions": [
"java.lang.Throwable"
],
"Next": "Fail"
}
],
"Next": "Succeed"
},
"Succeed": {
"Type":"Succeed"
},
"Fail": {
"Type":"Fail",
"ErrorCode": "NOT_FOUND",
"Message": "not found"
}
}
}

View File

@@ -0,0 +1,94 @@
{
"Name": "simpleRetryStateMachine",
"Comment": "带异常重试的测试状态机定义",
"StartState": "FirstState",
"Version": "0.0.1",
"States": {
"FirstState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "foo",
"Next": "ChoiceState",
"Input": [
{
"fooInput": "$.[a]"
}
],
"Output": {
"fooResult": "$.#root"
}
},
"ChoiceState":{
"Type": "Choice",
"Choices":[
{
"Expression":"[a] == 1",
"Next":"SecondState"
},
{
"Expression":"[a] == 2",
"Next":"ThirdState"
}
],
"Default":"Fail"
},
"SecondState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "randomExceptionMethod",
"Input": [
{
"barInput": "$.[fooResult]",
"throwException": "$.[barThrowException]"
}
],
"Output": {
"barResult": "$.#root"
},
"Retry": [
{
"Exceptions": ["io.seata.saga.engine.mock.DemoException"],
"IntervalSeconds": 1.5,
"MaxAttempts": 3,
"BackoffRate": 1.5
},
{
"IntervalSeconds": 1,
"MaxAttempts": 3,
"BackoffRate": 1.5
}
],
"Catch": [
{
"Exceptions": [
"io.seata.saga.engine.mock.DemoException"
],
"Next": "Fail"
}
],
"Next": "Succeed"
},
"ThirdState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "foo",
"Input": [
{
"fooInput": "$.[fooResult]"
}
],
"Output": {
"fooResult": "$.#root"
},
"Next": "Succeed"
},
"Succeed": {
"Type":"Succeed"
},
"Fail": {
"Type":"Fail",
"ErrorCode": "NOT_FOUND",
"Message": "not found"
}
}
}

View File

@@ -0,0 +1,104 @@
{
"Name": "simpleScriptTaskStateMachine",
"Comment": "带ScriptTask的测试状态机定义",
"StartState": "FirstState",
"Version": "0.0.1",
"States": {
"FirstState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "foo",
"Next": "ScriptState",
"Input": [
{
"fooInput": "$.[a]"
}
],
"Output": {
"fooResult": "$.#root"
}
},
"ScriptState": {
"Type": "ScriptTask",
"ScriptType": "groovy",
"ScriptContent": "if(throwException){ throw new RuntimeException(\"test\") } else { 'hello ' + inputA }",
"Input": [
{
"inputA": "$.[a]",
"throwException": "$.[scriptThrowException]"
}
],
"Output": {
"scriptStateResult": "$.#root"
},
"Catch": [
{
"Exceptions": [
"java.lang.Throwable"
],
"Next": "Fail"
}
],
"Next": "ChoiceState"
},
"ChoiceState":{
"Type": "Choice",
"Choices":[
{
"Expression":"[a] == 1",
"Next":"SecondState"
},
{
"Expression":"[a] == 2",
"Next":"ThirdState"
}
],
"Default":"Fail"
},
"SecondState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "bar",
"Input": [
{
"barInput": "$.[fooResult]",
"throwException": "$.[barThrowException]"
}
],
"Output": {
"barResult": "$.#root"
},
"Catch": [
{
"Exceptions": [
"io.seata.saga.engine.mock.DemoException"
],
"Next": "Fail"
}
],
"Next": "Succeed"
},
"ThirdState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "foo",
"Input": [
{
"fooInput": "$.[fooResult]"
}
],
"Output": {
"fooResult": "$.#root"
},
"Next": "Succeed"
},
"Succeed": {
"Type":"Succeed"
},
"Fail": {
"Type":"Fail",
"ErrorCode": "NOT_FOUND",
"Message": "not found"
}
}
}

View File

@@ -0,0 +1,99 @@
{
"Name": "simpleStatusMatchingStateMachine",
"Comment": "带Task执行状态匹配的测试状态机定义",
"StartState": "FirstState",
"Version": "0.0.1",
"States": {
"FirstState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "foo",
"Next": "ReturnNullState",
"Input": [
{
"fooInput": "$.[a]"
}
],
"Output": {
"fooResult": "$.#root"
}
},
"ReturnNullState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "foo",
"Next": "ChoiceState",
"Status": {
"$Exception{io.seata.saga.engine.mock.DemoException}": "UN",
"$Exception{java.lang.Exception}": "FA",
"#root != null && #root.size() > 0": "SU",
"#root == null || #root.size() == 0": "FA"
}
},
"ChoiceState":{
"Type": "Choice",
"Choices":[
{
"Expression":"[a] == 1",
"Next":"SecondState"
},
{
"Expression":"[a] == 2",
"Next":"ThirdState"
}
],
"Default":"Fail"
},
"SecondState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "bar",
"Input": [
{
"barInput": "$.[fooResult]",
"throwException": "$.[barThrowException]"
}
],
"Output": {
"barResult": "$.#root"
},
"Status": {
"$Exception{io.seata.saga.engine.mock.DemoException}": "UN",
"$Exception{java.lang.Exception}": "FA",
"#root != null && #root.size() > 0": "SU",
"#root == null || #root.size() == 0": "FA"
},
"Catch": [
{
"Exceptions": [
"io.seata.saga.engine.mock.DemoException"
],
"Next": "Fail"
}
],
"Next": "Succeed"
},
"ThirdState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "foo",
"Input": [
{
"fooInput": "$.[fooResult]"
}
],
"Output": {
"fooResult": "$.#root"
},
"Next": "Succeed"
},
"Succeed": {
"Type":"Succeed"
},
"Fail": {
"Type":"Fail",
"ErrorCode": "NOT_FOUND",
"Message": "not found"
}
}
}

View File

@@ -0,0 +1,118 @@
{
"Name": "simpleStateMachineWithUseDefCompensationSubMachine",
"Comment": "自定义补偿子状态机",
"StartState": "FirstState",
"Version": "0.0.1",
"States": {
"FirstState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "foo",
"CompensateState": "CompensateFirstState",
"Next": "ChoiceState",
"Input": [
{
"fooInput": "$.[a]"
}
],
"Output": {
"fooResult": "$.#root"
},
"Status": {
"#root != null": "SU",
"#root == null": "FA",
"$Exception{io.seata.saga.engine.mock.DemoException}": "UN"
}
},
"ChoiceState":{
"Type": "Choice",
"Choices":[
{
"Expression":"[a] == 1",
"Next":"SecondState"
},
{
"Expression":"[a] == 2",
"Next":"CallSubStateMachine"
}
],
"Default":"Fail"
},
"SecondState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "bar",
"CompensateState": "CompensateSecondState",
"Input": [
{
"barInput": "$.[fooResult]",
"throwException": "$.[barThrowException]"
}
],
"Output": {
"barResult": "$.#root"
},
"Status": {
"#root != null": "SU",
"#root == null": "FA",
"$Exception{io.seata.saga.engine.mock.DemoException}": "UN"
},
"Catch": [
{
"Exceptions": [
"io.seata.saga.engine.mock.DemoException"
],
"Next": "CompensationTrigger"
}
],
"Next": "Succeed"
},
"CallSubStateMachine": {
"Type": "SubStateMachine",
"StateMachineName": "simpleCompensationStateMachine",
"CompensateState": "CompensateSubMachine",
"Input": [
{
"a": "$.1",
"barThrowException": "$.[barThrowException]",
"fooThrowException": "$.[fooThrowException]",
"compensateFooThrowException": "$.[compensateFooThrowException]"
}
],
"Output": {
"fooResult": "$.#root"
},
"Next": "Succeed"
},
"CompensateFirstState": {
"Type": "ServiceTask",
"ServiceName": "demoService",
"ServiceMethod": "compensateFoo",
"Input": [
{
"compensateFooInput": "$.[fooResult]"
}
]
},
"CompensateSubMachine": {
"Type": "CompensateSubMachine",
"Input": [
{
"compensateFooThrowException": "$.[compensateFooThrowException]"
}
]
},
"CompensationTrigger": {
"Type": "CompensationTrigger",
"Next": "Fail"
},
"Succeed": {
"Type":"Succeed"
},
"Fail": {
"Type":"Fail",
"ErrorCode": "NOT_FOUND",
"Message": "not found"
}
}
}