diff --git a/.gitignore b/.gitignore index e34b7b267cd5f0f3f6bcec4a544be0f55896447b..44b06f93f74c2f13f603c08d372ee6f1a9916c5d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ target/ *.tar.gz *.class .flattened-pom.xml +dependency-reduced-pom.xml # eclipse ignore .settings/ @@ -36,4 +37,4 @@ target/ # system ignore .DS_Store Thumbs.db -*.orig \ No newline at end of file +*.orig diff --git a/README.md b/README.md index a4676b789b661abd4042e378ad139435b2fcc6db..766f57396908bb6a08a090b9e0108954f2a5135c 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ For more details about principle and design, please go to [Seata wiki page](http ## Maven dependency ```xml -<seata.version>0.5.1</seata.version> +<seata.version>0.5.2</seata.version> <dependency> <groupId>io.seata</groupId> diff --git a/all/pom.xml b/all/pom.xml index 8f092f4971df482b34d259b9ce14998a4336f3db..e05b28dcc51fc273b1ece3c53da6649cfb4c7bf4 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -21,7 +21,7 @@ <groupId>io.seata</groupId> <artifactId>seata-all</artifactId> - <version>0.5.2</version> + <version>0.6.0</version> <name>Seata All-in-one ${project.version}</name> <url>http://seata.io</url> @@ -112,6 +112,11 @@ <artifactId>seata-config-consul</artifactId> <version>${project.version}</version> </dependency> + <dependency> + <groupId>io.seata</groupId> + <artifactId>seata-config-etcd3</artifactId> + <version>${project.version}</version> + </dependency> <dependency> <groupId>io.seata</groupId> <artifactId>seata-core</artifactId> @@ -423,7 +428,7 @@ <createSourcesJar>true</createSourcesJar> <promoteTransitiveDependencies>false</promoteTransitiveDependencies> <keepDependenciesWithProvidedScope>false</keepDependenciesWithProvidedScope> - <createDependencyReducedPom>false</createDependencyReducedPom> + <createDependencyReducedPom>true</createDependencyReducedPom> <artifactSet> <includes> <include>io.seata:seata-common</include> @@ -433,6 +438,7 @@ <include>io.seata:seata-config-nacos</include> <include>io.seata:seata-config-zk</include> <include>io.seata:seata-config-consul</include> + <include>io.seata:seata-config-etcd3</include> <include>io.seata:seata-discovery-core</include> <include>io.seata:seata-discovery-consul</include> <include>io.seata:seata-discovery-eureka</include> @@ -473,6 +479,12 @@ <exclude>registry.conf</exclude> </excludes> </filter> + <filter> + <artifact>*:*</artifact> + <excludes> + <exclude>META-INF/maven/**</exclude> + </excludes> + </filter> </filters> </configuration> </execution> @@ -497,20 +509,12 @@ <artifactId>maven-javadoc-plugin</artifactId> <version>2.10.4</version> <configuration> - <encoding>${project.build.sourceEncoding}</encoding> - <detectOfflineLinks>true</detectOfflineLinks> - <breakiterator>true</breakiterator> - <author>false</author> - <keywords>true</keywords> - <quiet>true</quiet> - <includeDependencySources>true</includeDependencySources> - <dependencySourceIncludes> - <dependencySourceInclude>io.seata:seata-*</dependencySourceInclude> - </dependencySourceIncludes> + <charset>${project.build.sourceEncoding}</charset> + <failOnError>false</failOnError> </configuration> <executions> <execution> - <id>attach-javadocs</id> + <phase>package</phase> <goals> <goal>jar</goal> </goals> diff --git a/bom/pom.xml b/bom/pom.xml index dc5ca5ed57d17aeb4a6dc867a52838db9739f390..76b370961a07827c951514fb175132b7753b6c67 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -20,7 +20,7 @@ <groupId>io.seata</groupId> <artifactId>seata-bom</artifactId> - <version>0.5.2</version> + <version>0.6.0</version> <modelVersion>4.0.0</modelVersion> <packaging>pom</packaging> @@ -30,29 +30,28 @@ <properties> <spring.version>4.3.23.RELEASE</spring.version> <netty4.version>4.1.24.Final</netty4.version> - <junit.version>4.12</junit.version> <dubbo.version>2.7.0</dubbo.version> <dubbo.alibaba.version>2.6.5</dubbo.alibaba.version> <sofa.rpc.version>5.5.3</sofa.rpc.version> - <fastjson.version>1.2.48</fastjson.version> + <fastjson.version>1.2.58</fastjson.version> <config.version>1.2.1</config.version> <slf4j-api.version>1.7.22</slf4j-api.version> <logback-classic.version>1.2.0</logback-classic.version> <commons-lang.version>2.6</commons-lang.version> <commons-pool2.version>2.4.2</commons-pool2.version> <commons-pool.version>1.6</commons-pool.version> + <commons-dbcp.version>1.3</commons-dbcp.version> <cglib.version>3.1</cglib.version> <aopalliance.version>1.0</aopalliance.version> <zkclient.version>0.10</zkclient.version> <curator-test.version>2.9.1</curator-test.version> <spring-context-support.version>1.0.2</spring-context-support.version> - <testng.version>6.4</testng.version> <jacoco-maven-plugin.version>0.8.3</jacoco-maven-plugin.version> <apollo-client.version>1.1.0</apollo-client.version> <redis-clients.version>2.9.0</redis-clients.version> <eureka-clients.version>1.9.5</eureka-clients.version> <consul-clients.version>1.4.2</consul-clients.version> - <nacos-client.version>0.9.1</nacos-client.version> + <nacos-client.version>1.0.0</nacos-client.version> <etcd-client-v3.version>0.3.0</etcd-client-v3.version> <testcontainers.version>1.11.2</testcontainers.version> <guava.version>27.0.1-jre</guava.version> @@ -63,6 +62,9 @@ <httpcore.version>4.4.11</httpcore.version> <druid.version>1.1.12</druid.version> <caffeine.version>2.7.0</caffeine.version> + <oracle.client.version>10.2.0.3.0</oracle.client.version> + <mysql.client.version>5.1.30</mysql.client.version> + <h2.version>1.4.181</h2.version> </properties> <dependencyManagement> @@ -270,6 +272,21 @@ <artifactId>caffeine</artifactId> <version>${caffeine.version}</version> </dependency> + <dependency> + <groupId>commons-dbcp</groupId> + <artifactId>commons-dbcp</artifactId> + <version>${commons-dbcp.version}</version> + </dependency> + <dependency> + <groupId>com.h2database</groupId> + <artifactId>h2</artifactId> + <version>${h2.version}</version> + </dependency> + <dependency> + <groupId>mysql</groupId> + <artifactId>mysql-connector-java</artifactId> + <version>${mysql.client.version}</version> + </dependency> </dependencies> </dependencyManagement> diff --git a/common/src/main/java/io/seata/common/Constants.java b/common/src/main/java/io/seata/common/Constants.java index a76bacb155dc6214c17b7940b45c608442f8b10c..e39d856f5b9584fa0bb18826a4248dde8fac7acd 100644 --- a/common/src/main/java/io/seata/common/Constants.java +++ b/common/src/main/java/io/seata/common/Constants.java @@ -15,6 +15,8 @@ */ package io.seata.common; +import java.nio.charset.Charset; + /** * The type Constants. * @@ -97,4 +99,13 @@ public class Constants { */ public final static String TCC_ACTION_CONTEXT = "actionContext"; + /** + * default charset name + */ + public static final String DEFAULT_CHARSET_NAME = "UTF-8"; + + /** + * default charset is utf-8 + */ + public static final Charset DEFAULT_CHARSET = Charset.forName(DEFAULT_CHARSET_NAME); } diff --git a/common/src/main/java/io/seata/common/loader/EnhancedServiceLoader.java b/common/src/main/java/io/seata/common/loader/EnhancedServiceLoader.java index d10d95999bce5fb89b5c12b2aa19024c8c7fd767..4c80b9c59f32c13c106d2c59f72b00ff104bd050 100644 --- a/common/src/main/java/io/seata/common/loader/EnhancedServiceLoader.java +++ b/common/src/main/java/io/seata/common/loader/EnhancedServiceLoader.java @@ -18,6 +18,8 @@ package io.seata.common.loader; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.util.ArrayList; import java.util.Collections; @@ -27,6 +29,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import io.seata.common.Constants; import io.seata.common.executor.Initialize; import io.seata.common.util.CollectionUtils; @@ -102,12 +105,48 @@ public class EnhancedServiceLoader { return loadFile(service, activateName, loader); } + /** + * Load s. + * + * @param <S> the type parameter + * @param service the service + * @param activateName the activate name + * @param args the args + * @return the s + * @throws EnhancedServiceNotFoundException the enhanced service not found exception + */ + public static <S> S load(Class<S> service, String activateName, Object[] args) throws EnhancedServiceNotFoundException { + Class[] argsType = null; + if(args != null && args.length > 0){ + argsType = new Class[args.length]; + for(int i = 0; i < args.length; i ++){ + argsType[i] = args[i].getClass(); + } + } + return loadFile(service, activateName, findClassLoader(), argsType, args); + } + + /** + * Load s. + * + * @param <S> the type parameter + * @param service the service + * @param activateName the activate name + * @param argsType the args type + * @param args the args + * @return the s + * @throws EnhancedServiceNotFoundException the enhanced service not found exception + */ + public static <S> S load(Class<S> service, String activateName, Class[] argsType, Object[] args) throws EnhancedServiceNotFoundException { + return loadFile(service, activateName, findClassLoader(), argsType, args); + } + /** * get all implements * * @param <S> the type parameter * @param service the service - * @return list + * @return list list */ public static <S> List<S> loadAll(Class<S> service) { List<S> allInstances = new ArrayList<>(); @@ -117,7 +156,7 @@ public class EnhancedServiceLoader { } try { for (Class clazz : allClazzs) { - allInstances.add(initInstance(service, clazz)); + allInstances.add(initInstance(service, clazz, null, null)); } } catch (Throwable t) { throw new EnhancedServiceNotFoundException(t); @@ -150,8 +189,12 @@ public class EnhancedServiceLoader { return findAllExtensionClass(service, null, loader); } - @SuppressWarnings("rawtypes") private static <S> S loadFile(Class<S> service, String activateName, ClassLoader loader) { + return loadFile(service, activateName, loader, null, null); + } + + @SuppressWarnings("rawtypes") + private static <S> S loadFile(Class<S> service, String activateName, ClassLoader loader, Class[] argTypes, Object[] args) { try { boolean foundFromCache = true; List<Class> extensions = providers.get(service); @@ -173,7 +216,7 @@ public class EnhancedServiceLoader { Class clz = extensions.get(i); @SuppressWarnings("unchecked") LoadLevel activate = (LoadLevel)clz.getAnnotation(LoadLevel.class); - if (activate != null && activateName.equals(activate.name())) { + if (activate != null && activateName.equalsIgnoreCase(activate.name())) { activateExtensions.add(clz); } } @@ -187,7 +230,7 @@ public class EnhancedServiceLoader { + "] and classloader : " + ObjectUtils.toString(loader)); } Class<?> extension = extensions.get(extensions.size() - 1); - S result = initInstance(service, extension); + S result = initInstance(service, extension, argTypes, args); if (!foundFromCache && LOGGER.isInfoEnabled()) { LOGGER.info("load " + service.getSimpleName() + "[" + activateName + "] extension by class[" + extension .getName() + "]"); @@ -259,7 +302,7 @@ public class EnhancedServiceLoader { java.net.URL url = urls.nextElement(); BufferedReader reader = null; try { - reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8")); + reader = new BufferedReader(new InputStreamReader(url.openStream(), Constants.DEFAULT_CHARSET)); String line = null; while ((line = reader.readLine()) != null) { final int ci = line.indexOf('#'); @@ -292,13 +335,26 @@ public class EnhancedServiceLoader { * @param <S> the type parameter * @param service the service * @param implClazz the impl clazz - * @return s - * @throws IllegalAccessException the illegal access exception - * @throws InstantiationException the instantiation exception + * @param argTypes the arg types + * @param args the args + * @return s s + * @throws IllegalAccessException the illegal access exception + * @throws InstantiationException the instantiation exception + * @throws NoSuchMethodException the no such method exception + * @throws InvocationTargetException the invocation target exception */ - protected static <S> S initInstance(Class<S> service, Class implClazz) - throws IllegalAccessException, InstantiationException { - S s = service.cast(implClazz.newInstance()); + protected static <S> S initInstance(Class<S> service, Class implClazz, Class[] argTypes, Object[] args) + throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { + S s = null; + if(argTypes != null && args != null){ + // Constructor with arguments + Constructor<S> constructor = implClazz.getDeclaredConstructor(argTypes); + constructor.setAccessible(true); + s = service.cast(constructor.newInstance(args)); + }else { + // default Constructor + s = service.cast(implClazz.newInstance()); + } if (s instanceof Initialize) { ((Initialize)s).init(); } diff --git a/common/src/main/java/io/seata/common/loader/LoadLevel.java b/common/src/main/java/io/seata/common/loader/LoadLevel.java index b01ebf76c744fdd5c81cb0c9dce906b77171e92b..b887ed509b0736b68735babcc4fc74ed4c0e63ed 100644 --- a/common/src/main/java/io/seata/common/loader/LoadLevel.java +++ b/common/src/main/java/io/seata/common/loader/LoadLevel.java @@ -43,5 +43,5 @@ public @interface LoadLevel { * * @return the int */ - int order(); + int order() default 0; } diff --git a/common/src/main/java/io/seata/common/util/BlobUtils.java b/common/src/main/java/io/seata/common/util/BlobUtils.java index 3eb62aa0c282eb63f94ee9dce9a070817c63ca1d..60ddc7eaabc774d6be132cca358500edce9893e1 100644 --- a/common/src/main/java/io/seata/common/util/BlobUtils.java +++ b/common/src/main/java/io/seata/common/util/BlobUtils.java @@ -15,19 +15,18 @@ */ package io.seata.common.util; -import java.io.ByteArrayOutputStream; -import java.io.InputStream; import java.sql.Blob; import javax.sql.rowset.serial.SerialBlob; -import io.seata.common.exception.ShouldNeverHappenException; +import io.seata.common.Constants; import io.seata.common.exception.ShouldNeverHappenException; /** * The type Blob utils. * * @author jimin.jm @alibaba-inc.com + * @author Geng Zhang */ public class BlobUtils { @@ -47,7 +46,7 @@ public class BlobUtils { } try { - return new SerialBlob(str.getBytes()); + return new SerialBlob(str.getBytes(Constants.DEFAULT_CHARSET)); } catch (Exception e) { throw new ShouldNeverHappenException(e); } @@ -65,26 +64,43 @@ public class BlobUtils { } try { - return new String(blob.getBytes((long)1, (int)blob.length())); + return new String(blob.getBytes((long) 1, (int) blob.length()), Constants.DEFAULT_CHARSET); } catch (Exception e) { throw new ShouldNeverHappenException(e); } } /** - * Input stream 2 string string. + * Byte array to blob * - * @param is the is - * @return the string + * @param bytes the byte array + * @return the blob + */ + public static Blob bytes2Blob(byte[] bytes) { + if (bytes == null) { + return null; + } + + try { + return new SerialBlob(bytes); + } catch (Exception e) { + throw new ShouldNeverHappenException(e); + } + } + + /** + * Blob to byte array. + * + * @param blob the blob + * @return the byte array */ - public static String inputStream2String(InputStream is) { + public static byte[] blob2Bytes(Blob blob) { + if (blob == null) { + return null; + } + try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - int i = -1; - while ((i = is.read()) != -1) { - baos.write(i); - } - return baos.toString(); + return blob.getBytes((long) 1, (int) blob.length()); } catch (Exception e) { throw new ShouldNeverHappenException(e); } diff --git a/common/src/main/java/io/seata/common/util/CollectionUtils.java b/common/src/main/java/io/seata/common/util/CollectionUtils.java index 76126fc4cf4d51da9d26ebdcad2914519a906a7c..d56b80eb1ec4eadd79428c829f38541aa83f36ad 100644 --- a/common/src/main/java/io/seata/common/util/CollectionUtils.java +++ b/common/src/main/java/io/seata/common/util/CollectionUtils.java @@ -16,6 +16,7 @@ package io.seata.common.util; import java.util.Collection; +import java.util.Iterator; /** * The type Collection utils. @@ -45,6 +46,49 @@ public class CollectionUtils { return col != null && col.size() > 0; } + /** + * Is empty boolean. + * + * @param array the array + * @return the boolean + */ + public static boolean isEmpty(Object[] array){ + return !isNotEmpty(array); + } + + /** + * Is not empty boolean. + * + * @param array the array + * @return the boolean + */ + public static boolean isNotEmpty(Object[] array){ + return array != null && array.length > 0; + } + + /** + * To string string. + * + * @param col the col + * @return the string + */ + public static String toString(Collection col){ + if(isEmpty(col)){ + return ""; + } + StringBuffer sb = new StringBuffer(); + sb.append("["); + Iterator it = col.iterator(); + while (it.hasNext()){ + Object obj = it.next(); + sb.append(StringUtils.toString(obj)); + sb.append(","); + } + sb.deleteCharAt(sb.length() - 1); + sb.append("]"); + return sb.toString(); + } + /** * Is size equals boolean. * diff --git a/common/src/main/java/io/seata/common/util/DurationUtil.java b/common/src/main/java/io/seata/common/util/DurationUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..567fc85e3f5acbbfdc6a4568388821ce4c029b62 --- /dev/null +++ b/common/src/main/java/io/seata/common/util/DurationUtil.java @@ -0,0 +1,74 @@ +/* + * 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.util; + +import java.time.Duration; + +/** + * @author XCXCXCXCX + */ +public class DurationUtil { + + public static final Duration DEFAULT_DURATION = Duration.ofMillis(-1); + + public static final String DAY_UNIT = "d"; + public static final String HOUR_UNIT = "h"; + public static final String MINIUTE_UNIT = "m"; + public static final String SECOND_UNIT = "s"; + public static final String MILLIS_SECOND_UNIT = "ms"; + + public static Duration parse(String str) { + if(StringUtils.isBlank(str)){ + return DEFAULT_DURATION; + } + + if (str.contains(MILLIS_SECOND_UNIT)) { + Long value = doParse(MILLIS_SECOND_UNIT, str); + return value == null ? null : Duration.ofMillis(value); + } else if (str.contains(DAY_UNIT)) { + Long value = doParse(DAY_UNIT, str); + return value == null ? null : Duration.ofDays(value); + } else if (str.contains(HOUR_UNIT)) { + Long value = doParse(HOUR_UNIT, str); + return value == null ? null : Duration.ofHours(value); + } else if (str.contains(MINIUTE_UNIT)) { + Long value = doParse(MINIUTE_UNIT, str); + return value == null ? null : Duration.ofMinutes(value); + } else if (str.contains(SECOND_UNIT)) { + Long value = doParse(SECOND_UNIT, str); + return value == null ? null : Duration.ofSeconds(value); + } + try { + int millis = Integer.parseInt(str); + return Duration.ofMillis(millis); + }catch (Exception e){ + throw new UnsupportedOperationException(str + " can't parse to duration", e); + } + } + + private static Long doParse(String unit, String str) { + str = str.replace(unit, ""); + if ("".equals(str)) { + return null; + } + try { + return Long.parseLong(str); + } catch (NumberFormatException e) { + throw new UnsupportedOperationException("\"" + str + "\" can't parse to Duration", e); + } + } + +} diff --git a/common/src/main/java/io/seata/common/util/StringUtils.java b/common/src/main/java/io/seata/common/util/StringUtils.java index 217538aae17674ca53488f42b9a03268df82745f..e6a1714e78bedfc5a7e35b09dc8184f7df641bd9 100644 --- a/common/src/main/java/io/seata/common/util/StringUtils.java +++ b/common/src/main/java/io/seata/common/util/StringUtils.java @@ -15,19 +15,17 @@ */ package io.seata.common.util; +import io.seata.common.Constants; +import io.seata.common.exception.ShouldNeverHappenException; + import java.io.ByteArrayOutputStream; -import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; -import java.sql.Blob; -import java.sql.SQLException; import java.text.SimpleDateFormat; import java.util.Collection; import java.util.Date; import java.util.Map; -import javax.sql.rowset.serial.SerialBlob; - /** * The type String utils. * @@ -37,7 +35,6 @@ import javax.sql.rowset.serial.SerialBlob; public class StringUtils { private StringUtils() { - } /** @@ -54,7 +51,7 @@ public class StringUtils { * Is blank string ? * * @param str the str - * @return boolean + * @return boolean boolean */ public static boolean isBlank(String str) { int length; @@ -74,7 +71,7 @@ public class StringUtils { * Is Not blank string ? * * @param str the str - * @return boolean + * @return boolean boolean */ public static boolean isNotBlank(String str) { int length; @@ -91,56 +88,83 @@ public class StringUtils { return false; } + /** + * Equals boolean. + * + * @param a the a + * @param b the b + * @return boolean + */ + public static boolean equals(String a, String b) { + if (a == null) { + return b == null; + } + return a.equals(b); + } + /** - * String 2 blob blob. + * Equals ignore case boolean. * - * @param str the str - * @return the blob - * @throws SQLException the sql exception + * @param a the a + * @param b the b + * @return the boolean */ - public static Blob string2blob(String str) throws SQLException { - if (str == null) { - return null; + public static boolean equalsIgnoreCase(String a, String b) { + if (a == null) { + return b == null; } - return new SerialBlob(str.getBytes()); + return a.equalsIgnoreCase(b); } /** - * Blob 2 string string. + * Input stream 2 string string. * - * @param blob the blob + * @param is the is * @return the string - * @throws SQLException the sql exception */ - public static String blob2string(Blob blob) throws SQLException { - if (blob == null) { + public static String inputStream2String(InputStream is) { + if (is == null) { return null; } - - return new String(blob.getBytes((long)1, (int)blob.length())); + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int i = -1; + while ((i = is.read()) != -1) { + baos.write(i); + } + return baos.toString(Constants.DEFAULT_CHARSET_NAME); + } catch (Exception e) { + throw new ShouldNeverHappenException(e); + } } /** - * Input stream 2 string string. + * Input stream to byte array * * @param is the is - * @return the string - * @throws IOException the io exception + * @return the byte array */ - public static String inputStream2String(InputStream is) throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - int i = -1; - while ((i = is.read()) != -1) { - baos.write(i); + public static byte[] inputStream2Bytes(InputStream is) { + if (is == null) { + return null; + } + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int i = -1; + while ((i = is.read()) != -1) { + baos.write(i); + } + return baos.toByteArray(); + } catch (Exception e) { + throw new ShouldNeverHappenException(e); } - return baos.toString(); } /** * Object.toString() * * @param obj the obj - * @return string + * @return string string */ public static String toString(Object obj){ if (obj == null){ @@ -197,32 +221,4 @@ public class StringUtils { } return sb.toString(); } - - /** - * Equals boolean. - * - * @param a the a - * @param b the b - * @return boolean - */ - public static boolean equals(String a, String b) { - if (a == null) { - return b == null; - } - return a.equals(b); - } - - /** - * Equals ignore case boolean. - * - * @param a the a - * @param b the b - * @return the boolean - */ - public static boolean equalsIgnoreCase(String a, String b) { - if (a == null) { - return b == null; - } - return a.equalsIgnoreCase(b); - } } diff --git a/common/src/test/java/io/seata/common/util/BlobUtilsTest.java b/common/src/test/java/io/seata/common/util/BlobUtilsTest.java index 53a0a6217aeb29cc6e9bdc5713beb568817d6e04..f643ad49caa625d2aea7c8df6b61f397d18dec0b 100644 --- a/common/src/test/java/io/seata/common/util/BlobUtilsTest.java +++ b/common/src/test/java/io/seata/common/util/BlobUtilsTest.java @@ -15,21 +15,22 @@ */ package io.seata.common.util; -import java.io.InputStream; -import java.nio.charset.Charset; +import java.io.UnsupportedEncodingException; import java.sql.SQLException; import javax.sql.rowset.serial.SerialBlob; -import org.junit.jupiter.api.Disabled; +import io.seata.common.Constants; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertNull; /** * The type Blob utils test. * * @author Otis.z + * @author Geng Zhang * @date 2019 /2/26 */ public class BlobUtilsTest { @@ -41,8 +42,9 @@ public class BlobUtilsTest { */ @Test public void testString2blob() throws SQLException { + assertNull(BlobUtils.string2blob(null)); assertThat(BlobUtils.string2blob("123abc")).isEqualTo( - new SerialBlob("123abc".getBytes(Charset.forName("UTF-8")))); + new SerialBlob("123abc".getBytes(Constants.DEFAULT_CHARSET))); } /** @@ -52,19 +54,24 @@ public class BlobUtilsTest { */ @Test public void testBlob2string() throws SQLException { - assertThat(BlobUtils.blob2string(new SerialBlob("123absent".getBytes(Charset.forName("UTF-8"))))).isEqualTo( + assertNull(BlobUtils.blob2string(null)); + assertThat(BlobUtils.blob2string(new SerialBlob("123absent".getBytes(Constants.DEFAULT_CHARSET)))).isEqualTo( "123absent"); } - /** - * Test input stream 2 string. - */ @Test - @Disabled - public void testInputStream2String() { - InputStream inputStream = BlobUtilsTest.class.getClassLoader().getResourceAsStream("test.txt"); - assertThat(BlobUtils.inputStream2String(inputStream)).isEqualTo("abc\n" - + ":\"klsdf\n" - + "2ks,x:\".,-3sd˚ø≤ø¬≥"); + void bytes2Blob() throws UnsupportedEncodingException, SQLException { + assertNull(BlobUtils.bytes2Blob(null)); + byte[] bs = "xxa哈哈dd".getBytes(Constants.DEFAULT_CHARSET_NAME); + assertThat(BlobUtils.bytes2Blob(bs)).isEqualTo( + new SerialBlob(bs)); + } + + @Test + void blob2Bytes() throws UnsupportedEncodingException, SQLException { + assertNull(BlobUtils.blob2Bytes(null)); + byte[] bs = "xxa哈哈dd".getBytes(Constants.DEFAULT_CHARSET_NAME); + assertThat(BlobUtils.blob2Bytes(new SerialBlob(bs))).isEqualTo( + bs); } } diff --git a/common/src/test/java/io/seata/common/util/StringUtilsTest.java b/common/src/test/java/io/seata/common/util/StringUtilsTest.java index 1eae80462bf81c5027cf26d27b03472e1c1cb1d4..c816c0358cb28d6585e64155debf1431d57d9732 100644 --- a/common/src/test/java/io/seata/common/util/StringUtilsTest.java +++ b/common/src/test/java/io/seata/common/util/StringUtilsTest.java @@ -15,17 +15,15 @@ */ package io.seata.common.util; +import java.io.ByteArrayInputStream; import java.io.IOException; -import java.io.InputStream; -import java.sql.SQLException; - -import javax.sql.rowset.serial.SerialBlob; +import io.seata.common.Constants; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertNull; /** * The type String utils test. @@ -48,43 +46,25 @@ public class StringUtilsTest { assertThat(StringUtils.isNullOrEmpty(" ")).isFalse(); } - /** - * Test string 2 blob. - * - * @throws SQLException the sql exception - */ @Test - public void testString2blob() throws SQLException { - assertThat(StringUtils.string2blob(null)).isNull(); - String[] strs = new String[] {"abc", "", " "}; - for (String str : strs) { - assertThat(StringUtils.string2blob(str)).isEqualTo(new SerialBlob(str.getBytes())); - } - } - - /** - * Test blob 2 string. - * - * @throws SQLException the sql exception - */ - @Test - public void testBlob2string() throws SQLException { - String[] strs = new String[] {"abc", " "}; - for (String str : strs) { - assertThat(StringUtils.blob2string(new SerialBlob(str.getBytes()))).isEqualTo(str); - - } + public void testInputStream2String() throws IOException { + assertNull(StringUtils.inputStream2String(null)); + String data = "abc\n" + + ":\"klsdf\n" + + "2ks,x:\".,-3sd˚ø≤ø¬≥"; + ByteArrayInputStream inputStream = new ByteArrayInputStream(data.getBytes(Constants.DEFAULT_CHARSET)); + assertThat(StringUtils.inputStream2String(inputStream)).isEqualTo(data); } - /** - * Test input stream 2 string. - */ @Test - @Disabled - public void testInputStream2String() throws IOException { - InputStream inputStream = StringUtilsTest.class.getClassLoader().getResourceAsStream("test.txt"); - assertThat(StringUtils.inputStream2String(inputStream)) - .isEqualTo("abc\n" + ":\"klsdf\n" + "2ks,x:\".,-3sd˚ø≤ø¬≥"); + void inputStream2Bytes() { + assertNull(StringUtils.inputStream2Bytes(null)); + String data = "abc\n" + + ":\"klsdf\n" + + "2ks,x:\".,-3sd˚ø≤ø¬≥"; + byte[] bs = data.getBytes(Constants.DEFAULT_CHARSET); + ByteArrayInputStream inputStream = new ByteArrayInputStream(data.getBytes(Constants.DEFAULT_CHARSET)); + assertThat(StringUtils.inputStream2Bytes(inputStream)).isEqualTo(bs); } @Test diff --git a/config/pom.xml b/config/pom.xml index cfdf735708d9a990e40b3d4c67684db6b14e5584..36b384ebda602dee7c1c79dd701a55b0556e94be 100644 --- a/config/pom.xml +++ b/config/pom.xml @@ -32,6 +32,7 @@ <module>seata-config-nacos</module> <module>seata-config-zk</module> <module>seata-config-all</module> + <module>seata-config-etcd3</module> <module>seata-config-consul</module> </modules> </project> diff --git a/config/seata-config-all/pom.xml b/config/seata-config-all/pom.xml index 3cf62928e1bac49a9d6203b4258a5078b54ddf48..46da42a13db477fe6b62752993ebbb50c5c0808e 100644 --- a/config/seata-config-all/pom.xml +++ b/config/seata-config-all/pom.xml @@ -41,6 +41,11 @@ <artifactId>seata-config-nacos</artifactId> <version>${project.version}</version> </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>seata-config-etcd3</artifactId> + <version>${project.version}</version> + </dependency> <dependency> <groupId>${project.groupId}</groupId> <artifactId>seata-config-consul</artifactId> diff --git a/config/seata-config-core/src/main/java/io/seata/config/AbstractConfiguration.java b/config/seata-config-core/src/main/java/io/seata/config/AbstractConfiguration.java index 7cd509eccdd5cdafdc960f322a2fda8f4c0a2311..0092f4f36ac5020307908c5969b4aa5fd918f300 100644 --- a/config/seata-config-core/src/main/java/io/seata/config/AbstractConfiguration.java +++ b/config/seata-config-core/src/main/java/io/seata/config/AbstractConfiguration.java @@ -15,6 +15,11 @@ */ package io.seata.config; + +import io.seata.common.util.DurationUtil; + +import java.time.Duration; + /** * The type Abstract configuration. * @@ -61,6 +66,22 @@ public abstract class AbstractConfiguration<T> implements Configuration<T> { return getLong(dataId, 0L); } + @Override + public Duration getDuration(String dataId) { + return getDuration(dataId, Duration.ZERO); + } + + @Override + public Duration getDuration(String dataId, Duration defaultValue) { + return getDuration(dataId, defaultValue, DEFAULT_CONFIG_TIMEOUT); + } + + @Override + public Duration getDuration(String dataId, Duration defaultValue, long timeoutMills) { + String result = getConfig(dataId, String.valueOf(defaultValue.toMillis() + "ms"), timeoutMills); + return DurationUtil.parse(result); + } + @Override public boolean getBoolean(String dataId, boolean defaultValue, long timeoutMills) { String result = getConfig(dataId, String.valueOf(defaultValue), timeoutMills); diff --git a/config/seata-config-core/src/main/java/io/seata/config/ConfigType.java b/config/seata-config-core/src/main/java/io/seata/config/ConfigType.java index 866fddb8d67eec8836d9e0799ff7bcaf29b99755..6f8cf55a4ede1c2a2e1e629c3396df06f3d05c56 100644 --- a/config/seata-config-core/src/main/java/io/seata/config/ConfigType.java +++ b/config/seata-config-core/src/main/java/io/seata/config/ConfigType.java @@ -41,7 +41,11 @@ public enum ConfigType { /** * Consul config type */ - Consul; + Consul, + /** + * Etcd3 config type + */ + Etcd3; /** * Gets type. diff --git a/config/seata-config-core/src/main/java/io/seata/config/Configuration.java b/config/seata-config-core/src/main/java/io/seata/config/Configuration.java index bb3e1a7c18cdbcb96d47a8b498b35f46baf8292a..67ec8679191004e5ca74dd3439c93728106ec809 100644 --- a/config/seata-config-core/src/main/java/io/seata/config/Configuration.java +++ b/config/seata-config-core/src/main/java/io/seata/config/Configuration.java @@ -15,6 +15,7 @@ */ package io.seata.config; +import java.time.Duration; import java.util.List; /** @@ -80,6 +81,33 @@ public interface Configuration<T> { */ long getLong(String dataId); + /** + * Gets duration. + * + * @param dataId + * @return the duration + */ + Duration getDuration(String dataId); + + /** + * Gets duration. + * + * @param dataId + * @param defaultValue + * @return the duration + */ + Duration getDuration(String dataId, Duration defaultValue); + + /** + * Gets duration. + * + * @param dataId + * @param defaultValue + * @param timeoutMills + * @return he duration + */ + Duration getDuration(String dataId, Duration defaultValue, long timeoutMills); + /** * Gets boolean. * diff --git a/config/seata-config-core/src/main/java/io/seata/config/ConfigurationFactory.java b/config/seata-config-core/src/main/java/io/seata/config/ConfigurationFactory.java index 0b802b3d344e9c3010500f6bc34a3dd79ba8d7b7..cd5ea345d5cf050f3213a354a6120ea8810488b1 100644 --- a/config/seata-config-core/src/main/java/io/seata/config/ConfigurationFactory.java +++ b/config/seata-config-core/src/main/java/io/seata/config/ConfigurationFactory.java @@ -17,8 +17,6 @@ package io.seata.config; import io.seata.common.exception.NotSupportYetException; import io.seata.common.loader.EnhancedServiceLoader; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.util.Objects; @@ -26,10 +24,9 @@ import java.util.Objects; * The type Configuration factory. * * @author jimin.jm @alibaba-inc.com - * @date 2018 /12/24 + * @author Geng Zhang */ public final class ConfigurationFactory { - private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationFactory.class); private static final String REGISTRY_CONF = "registry.conf"; /** * The constant FILE_INSTANCE. @@ -38,20 +35,33 @@ public final class ConfigurationFactory { private static final String NAME_KEY = "name"; private static final String FILE_TYPE = "file"; + private static volatile Configuration CONFIG_INSTANCE = null; + /** * Gets instance. * * @return the instance */ public static Configuration getInstance() { + if (CONFIG_INSTANCE == null) { + synchronized (Configuration.class) { + if (CONFIG_INSTANCE == null) { + CONFIG_INSTANCE = buildConfiguration(); + } + } + } + return CONFIG_INSTANCE; + } + + private static Configuration buildConfiguration() { ConfigType configType = null; String configTypeName = null; try { configTypeName = FILE_INSTANCE.getConfig(ConfigurationKeys.FILE_ROOT_CONFIG + ConfigurationKeys.FILE_CONFIG_SPLIT_CHAR + ConfigurationKeys.FILE_ROOT_TYPE); configType = ConfigType.getType(configTypeName); - } catch (Exception exx) { - throw new NotSupportYetException("not support register type: " + configTypeName); + } catch (Exception e) { + throw new NotSupportYetException("not support register type: " + configTypeName, e); } if (ConfigType.File == configType) { String pathDataId = ConfigurationKeys.FILE_ROOT_CONFIG + ConfigurationKeys.FILE_CONFIG_SPLIT_CHAR diff --git a/config/seata-config-core/src/main/resources/file.conf b/config/seata-config-core/src/main/resources/file.conf index dc09514d367b205f6a0fdb8dfc45d462c7bddf20..a166f8d5c5cc9ca87bc056f68e49b0c5dfbe7577 100644 --- a/config/seata-config-core/src/main/resources/file.conf +++ b/config/seata-config-core/src/main/resources/file.conf @@ -69,4 +69,8 @@ client { retry.times = 30 } report.retry.count = 5 -} \ No newline at end of file +} + +transaction { + undo.data.validation = true +} diff --git a/config/seata-config-core/src/main/resources/registry.conf b/config/seata-config-core/src/main/resources/registry.conf index 1d6c35982564b2f8330a6a9bdb46f2aa12ae407c..5115876d916fa5e59a5511796e5cc92622f5a442 100644 --- a/config/seata-config-core/src/main/resources/registry.conf +++ b/config/seata-config-core/src/main/resources/registry.conf @@ -45,7 +45,7 @@ registry { } config { - # file、nacos 、apollo、zk、consul + # file、nacos 、apollo、zk、consul、etcd3 type = "file" nacos { @@ -65,6 +65,9 @@ config { session.timeout = 6000 connect.timeout = 2000 } + etcd3 { + serverAddr = "http://localhost:2379" + } file { name = "file.conf" } diff --git a/config/seata-config-etcd3/pom.xml b/config/seata-config-etcd3/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..beb590dfffecbf490d7b14acefa3725f12fc4cf1 --- /dev/null +++ b/config/seata-config-etcd3/pom.xml @@ -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. + --> +<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-config</artifactId> + <version>${revision}</version> + </parent> + <modelVersion>4.0.0</modelVersion> + <artifactId>seata-config-etcd3</artifactId> + <name>seata-config-etcd3 ${project.version}</name> + + <dependencies> + <dependency> + <groupId>io.seata</groupId> + <artifactId>seata-config-core</artifactId> + <version>${project.parent.version}</version> + </dependency> + <dependency> + <groupId>io.etcd</groupId> + <artifactId>jetcd-core</artifactId> + <exclusions> + <exclusion> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + </dependency> + </dependencies> +</project> diff --git a/config/seata-config-etcd3/src/main/java/io/seata/config/etcd/EtcdConfiguration.java b/config/seata-config-etcd3/src/main/java/io/seata/config/etcd/EtcdConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..6eed7d53035ab7a7ba1f540a7e9a769399758114 --- /dev/null +++ b/config/seata-config-etcd3/src/main/java/io/seata/config/etcd/EtcdConfiguration.java @@ -0,0 +1,318 @@ +/* + * 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.config.etcd; + +import io.etcd.jetcd.ByteSequence; +import io.etcd.jetcd.Client; +import io.etcd.jetcd.KeyValue; +import io.etcd.jetcd.Watch; +import io.etcd.jetcd.kv.DeleteResponse; +import io.etcd.jetcd.kv.GetResponse; +import io.etcd.jetcd.kv.PutResponse; +import io.etcd.jetcd.kv.TxnResponse; +import io.etcd.jetcd.op.Cmp; +import io.etcd.jetcd.op.CmpTarget; +import io.etcd.jetcd.op.Op; +import io.etcd.jetcd.options.PutOption; +import io.etcd.jetcd.watch.WatchResponse; +import io.seata.common.exception.ShouldNeverHappenException; +import io.seata.common.thread.NamedThreadFactory; +import io.seata.common.util.CollectionUtils; +import io.seata.config.AbstractConfiguration; +import io.seata.config.ConfigChangeListener; +import io.seata.config.ConfigFuture; +import io.seata.config.Configuration; +import io.seata.config.ConfigurationFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import static io.netty.util.CharsetUtil.UTF_8; +import static io.seata.config.ConfigurationKeys.FILE_CONFIG_SPLIT_CHAR; +import static io.seata.config.ConfigurationKeys.FILE_ROOT_CONFIG; + +/** + * @author xingfudeshi@gmail.com + * @date 2019/05/10 + */ +public class EtcdConfiguration extends AbstractConfiguration<ConfigChangeListener> { + private static final Logger LOGGER = LoggerFactory.getLogger(EtcdConfiguration.class); + private static volatile EtcdConfiguration instance; + private static volatile Client client; + + private static final Configuration FILE_CONFIG = ConfigurationFactory.FILE_INSTANCE; + private static final String SERVER_ADDR_KEY = "serverAddr"; + private static final String CONFIG_TYPE = "etcd3"; + private static final String FILE_CONFIG_KEY_PREFIX = FILE_ROOT_CONFIG + FILE_CONFIG_SPLIT_CHAR + CONFIG_TYPE + FILE_CONFIG_SPLIT_CHAR; + private static final int THREAD_POOL_NUM = 1; + private static final int MAP_INITIAL_CAPACITY = 8; + private static ExecutorService etcdConfigExecutor = null; + private static ExecutorService etcdNotifierExecutor = null; + private static ConcurrentMap<String, List<ConfigChangeListener>> configListenersMap = null; + private static ConcurrentHashMap<String, List<ConfigChangeNotifier>> configChangeNotifiersMap = null; + + private static final long VERSION_NOT_EXIST = 0; + + private EtcdConfiguration() { + } + + /** + * get instance + * + * @return + */ + public static EtcdConfiguration getInstance() { + if (null == instance) { + synchronized (EtcdConfiguration.class) { + if (null == instance) { + etcdConfigExecutor = new ThreadPoolExecutor(THREAD_POOL_NUM, THREAD_POOL_NUM, + Integer.MAX_VALUE, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), new NamedThreadFactory("etcd-config-executor", THREAD_POOL_NUM)); + etcdNotifierExecutor = new ThreadPoolExecutor(THREAD_POOL_NUM, THREAD_POOL_NUM, + Integer.MAX_VALUE, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), new NamedThreadFactory("etcd-config-notifier-executor", THREAD_POOL_NUM)); + configListenersMap = new ConcurrentHashMap<>(MAP_INITIAL_CAPACITY); + configChangeNotifiersMap = new ConcurrentHashMap<>(MAP_INITIAL_CAPACITY); + instance = new EtcdConfiguration(); + } + } + } + return instance; + } + + + @Override + public String getTypeName() { + return CONFIG_TYPE; + } + + @Override + public String getConfig(String dataId, String defaultValue, long timeoutMills) { + ConfigFuture configFuture = new ConfigFuture(dataId, defaultValue, ConfigFuture.ConfigOperation.GET, timeoutMills); + etcdConfigExecutor.execute(() -> { + complete(getClient().getKVClient().get(ByteSequence.from(dataId, UTF_8)), configFuture); + }); + return (String) configFuture.get(); + } + + @Override + public boolean putConfig(String dataId, String content, long timeoutMills) { + ConfigFuture configFuture = new ConfigFuture(dataId, content, ConfigFuture.ConfigOperation.PUT, timeoutMills); + etcdConfigExecutor.execute(() -> { + complete(getClient().getKVClient().put(ByteSequence.from(dataId, UTF_8), ByteSequence.from(content, UTF_8)), configFuture); + }); + return (Boolean) configFuture.get(); + } + + @Override + public boolean putConfigIfAbsent(String dataId, String content, long timeoutMills) { + ConfigFuture configFuture = new ConfigFuture(dataId, content, ConfigFuture.ConfigOperation.PUTIFABSENT, timeoutMills); + etcdConfigExecutor.execute(() -> { + //use etcd transaction to ensure the atomic operation + complete(client.getKVClient().txn() + //whether the key exists + .If(new Cmp(ByteSequence.from(dataId, UTF_8), Cmp.Op.EQUAL, CmpTarget.version(VERSION_NOT_EXIST))) + //not exist,then will create + .Then(Op.put(ByteSequence.from(dataId, UTF_8), ByteSequence.from(content, UTF_8), PutOption.DEFAULT)) + .commit(), configFuture); + }); + return (Boolean) configFuture.get(); + } + + @Override + public boolean removeConfig(String dataId, long timeoutMills) { + ConfigFuture configFuture = new ConfigFuture(dataId, null, ConfigFuture.ConfigOperation.REMOVE, timeoutMills); + etcdConfigExecutor.execute(() -> { + complete(getClient().getKVClient().delete(ByteSequence.from(dataId, UTF_8)), configFuture); + }); + return (Boolean) configFuture.get(); + } + + @Override + public void addConfigListener(String dataId, ConfigChangeListener listener) { + configListenersMap.putIfAbsent(dataId, new ArrayList<>()); + configChangeNotifiersMap.putIfAbsent(dataId, new ArrayList<>()); + ConfigChangeNotifier configChangeNotifier = new ConfigChangeNotifier(dataId, listener); + configChangeNotifiersMap.get(dataId).add(configChangeNotifier); + if (null != listener.getExecutor()) { + listener.getExecutor().submit(configChangeNotifier); + } else { + etcdNotifierExecutor.submit(configChangeNotifier); + } + } + + @Override + public void removeConfigListener(String dataId, ConfigChangeListener listener) { + List<ConfigChangeListener> configChangeListeners = getConfigListeners(dataId); + if (configChangeListeners == null) { + return; + } + List<ConfigChangeListener> newChangeListenerList = new ArrayList<>(); + for (ConfigChangeListener changeListener : configChangeListeners) { + if (!changeListener.equals(listener)) { + newChangeListenerList.add(changeListener); + } + } + configListenersMap.put(dataId, newChangeListenerList); + if (null != listener.getExecutor()) { + listener.getExecutor().shutdownNow(); + } + //remove and stop the configChangeNotifier + List<ConfigChangeNotifier> configChangeNotifiers = configChangeNotifiersMap.get(dataId); + List<ConfigChangeNotifier> newConfigChangeNotifiers = new ArrayList<>(); + for (ConfigChangeNotifier configChangeNotifier : configChangeNotifiers) { + if (!listener.equals(configChangeNotifier.getListener())) { + newConfigChangeNotifiers.add(configChangeNotifier); + } else { + configChangeNotifier.stop(); + } + } + configChangeNotifiersMap.put(dataId, newConfigChangeNotifiers); + } + + @Override + public List getConfigListeners(String dataId) { + return configListenersMap.get(dataId); + } + + /** + * get client + * + * @return client + */ + private static Client getClient() { + if (null == client) { + synchronized (EtcdConfiguration.class) { + if (null == client) { + client = Client.builder().endpoints(FILE_CONFIG.getConfig(FILE_CONFIG_KEY_PREFIX + SERVER_ADDR_KEY)).build(); + } + } + } + return client; + } + + /** + * complete the future + * + * @param completableFuture + * @param configFuture + * @param <T> + */ + private <T> void complete(CompletableFuture<T> completableFuture, ConfigFuture configFuture) { + try { + T response = completableFuture.get(); + if (response instanceof GetResponse) { + List<KeyValue> keyValues = ((GetResponse) response).getKvs(); + if (CollectionUtils.isNotEmpty(keyValues)) { + ByteSequence value = keyValues.get(0).getValue(); + if (null != value) { + configFuture.setResult(value.toString(UTF_8)); + } + } + } else if (response instanceof PutResponse) { + configFuture.setResult(Boolean.TRUE); + } else if (response instanceof TxnResponse) { + boolean result = ((TxnResponse) response).isSucceeded(); + //create key if file does not exist) + if (result) { + configFuture.setResult(Boolean.TRUE); + } + } else if (response instanceof DeleteResponse) { + configFuture.setResult(Boolean.TRUE); + } else { + throw new ShouldNeverHappenException("unsupported response type"); + } + } catch (Exception e) { + LOGGER.error("error occurred while completing the future{}", e.getMessage()); + } + } + + /** + * the type config change notifier + */ + private static class ConfigChangeNotifier implements Runnable { + private final String dataId; + private final ConfigChangeListener listener; + private Watch.Watcher watcher; + + ConfigChangeNotifier(String dataId, ConfigChangeListener listener) { + this.dataId = dataId; + this.listener = listener; + } + + /** + * get the listener + * + * @return ConfigChangeListener + */ + ConfigChangeListener getListener() { + return this.listener; + } + + @Override + public void run() { + Watch watchClient = getClient().getWatchClient(); + watcher = watchClient.watch(ByteSequence.from(dataId, UTF_8), new Watch.Listener() { + @Override + public void onNext(WatchResponse response) { + notifyListeners(); + } + + @Override + public void onError(Throwable throwable) { + + } + + @Override + public void onCompleted() { + + } + }); + } + + /** + * notify listeners + */ + private void notifyListeners() { + try { + GetResponse getResponse = getClient().getKVClient().get(ByteSequence.from(dataId, UTF_8)).get(); + List<KeyValue> keyValues = getResponse.getKvs(); + if (CollectionUtils.isNotEmpty(keyValues)) { + for (ConfigChangeListener listener : configListenersMap.get(this.dataId)) { + listener.receiveConfigInfo(keyValues.get(0).getValue().toString(UTF_8)); + } + } + } catch (Exception e) { + LOGGER.error("error occurred while getting value{}", e.getMessage()); + } + } + + + /** + * stop the notifier + */ + public void stop() { + this.watcher.close(); + } + } +} diff --git a/config/seata-config-etcd3/src/main/java/io/seata/config/etcd/EtcdConfigurationProvider.java b/config/seata-config-etcd3/src/main/java/io/seata/config/etcd/EtcdConfigurationProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..acb7e28db96a3a89bd13a14dccbab2043b1894b0 --- /dev/null +++ b/config/seata-config-etcd3/src/main/java/io/seata/config/etcd/EtcdConfigurationProvider.java @@ -0,0 +1,32 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.config.etcd; + +import io.seata.common.loader.LoadLevel; +import io.seata.config.Configuration; +import io.seata.config.ConfigurationProvider; + +/** + * @author xingfudeshi@gmail.com + * @date 2019/04/12 + */ +@LoadLevel(name = "Etcd3", order = 1) +public class EtcdConfigurationProvider implements ConfigurationProvider { + @Override + public Configuration provide() { + return EtcdConfiguration.getInstance(); + } +} diff --git a/config/seata-config-etcd3/src/main/resources/META-INF/services/io.seata.config.ConfigurationProvider b/config/seata-config-etcd3/src/main/resources/META-INF/services/io.seata.config.ConfigurationProvider new file mode 100644 index 0000000000000000000000000000000000000000..ce01ac197600438bb941beca28567a291b72de90 --- /dev/null +++ b/config/seata-config-etcd3/src/main/resources/META-INF/services/io.seata.config.ConfigurationProvider @@ -0,0 +1 @@ +io.seata.config.etcd.EtcdConfigurationProvider \ No newline at end of file diff --git a/core/pom.xml b/core/pom.xml index a6a71f8e60dd4515aefbe2b27e6ac8e3830ed300..9670f9fd495d0d935e7c983159d6dbdc0149e737 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -55,6 +55,17 @@ <groupId>commons-pool</groupId> <artifactId>commons-pool</artifactId> </dependency> + <dependency> + <groupId>commons-dbcp</groupId> + <artifactId>commons-dbcp</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.h2database</groupId> + <artifactId>h2</artifactId> + <scope>test</scope> + </dependency> + </dependencies> </project> diff --git a/core/src/main/java/io/seata/core/constants/ConfigurationKeys.java b/core/src/main/java/io/seata/core/constants/ConfigurationKeys.java index 4df7b3f11c6a64cc8a5d17133149c422fb686ce6..dc6f93fef8cb28a49d0ebc8a63ec92bfe78c5dcb 100644 --- a/core/src/main/java/io/seata/core/constants/ConfigurationKeys.java +++ b/core/src/main/java/io/seata/core/constants/ConfigurationKeys.java @@ -92,4 +92,111 @@ public class ConfigurationKeys { * The constant CLIENT_REPORT_RETRY_COUNT. */ public static final String CLIENT_REPORT_RETRY_COUNT = CLIENT_PREFIX + "report.retry.count"; + + /** + * The constant STORE_DB_GLOBAL_TABLE. + */ + public static final String STORE_DB_GLOBAL_TABLE = "store.db.global.table"; + + /** + * The constant STORE_DB_BRANCH_TABLE. + */ + public static final String STORE_DB_BRANCH_TABLE = "store.db.branch.table"; + + /** + * The constant STORE_DB_GLOBAL_DEFAULT_TABLE. + */ + public static final String STORE_DB_GLOBAL_DEFAULT_TABLE = "global_table"; + + /** + * The constant STORE_DB_BRANCH_DEFAULT_TABLE. + */ + public static final String STORE_DB_BRANCH_DEFAULT_TABLE = "branch_table"; + + /** + * The constant STORE_DB_DATASOURCE_TYPE. + */ + public static final String STORE_DB_DATASOURCE_TYPE = "store.db.datasource"; + + + /** + * The constant STORE_DB_TYPE. + */ + public static final String STORE_DB_TYPE = "store.db.db-type"; + + /** + * The constant STORE_DB_URL. + */ + public static final String STORE_DB_URL = "store.db.url"; + + /** + * The constant STORE_DB_USER. + */ + public static final String STORE_DB_USER = "store.db.user"; + + /** + * The constant STORE_DB_PASSWORD. + */ + public static final String STORE_DB_PASSWORD = "store.db.password"; + + /** + * The constant STORE_DB_MIN_CONN. + */ + public static final String STORE_DB_MIN_CONN = "store.db.min-conn"; + + /** + * The constant STORE_DB_MAX_CONN. + */ + public static final String STORE_DB_MAX_CONN = "store.db.max-conn"; + + /** + * The constant STORE_DB_LOG_QUERY_LIMIT. + */ + public static final String STORE_DB_LOG_QUERY_LIMIT = "store.db.query-limit"; + + /** + * The constant LOCK_MODE. + */ + public static final String LOCK_MODE = "lock.mode"; + + /** + * The constant LOCK_DB_TABLE. + */ + public static final String LOCK_DB_TABLE = "lock.db.lock-table"; + + /** + * The constant LOCK_DB_DEFAULT_TABLE. + */ + public static final String LOCK_DB_DEFAULT_TABLE = "lock_table"; + + /** + * The constant COMMITING_RETRY_DELAY. + */ + public static final String COMMITING_RETRY_DELAY = "recovery.committing-retry-delay"; + + /** + * The constant ASYN_COMMITING_RETRY_DELAY. + */ + public static final String ASYN_COMMITING_RETRY_DELAY = "recovery.asyn-committing-retry-delay"; + + /** + * The constant ROLLBACKING_RETRY_DELAY. + */ + public static final String ROLLBACKING_RETRY_DELAY = "recovery.rollbacking-retry-delay"; + + /** + * The constant TIMEOUT_RETRY_DELAY. + */ + public static final String TIMEOUT_RETRY_DELAY = "recovery.timeout-retry-delay"; + + + /** + * The constant TRANSACTION_PREFIX. + */ + public static final String TRANSACTION_PREFIX = "transaction."; + + /** + * The constant RM_DATASOURCE_UNOD_VALIDATION. + */ + public static final String TRANSACTION_UNOD_DATA_VALIDATION = TRANSACTION_PREFIX + "undo.data.validation"; } diff --git a/core/src/main/java/io/seata/core/constants/DBType.java b/core/src/main/java/io/seata/core/constants/DBType.java new file mode 100644 index 0000000000000000000000000000000000000000..cdb0174b98522575ce93f1178a10db4e58724bac --- /dev/null +++ b/core/src/main/java/io/seata/core/constants/DBType.java @@ -0,0 +1,93 @@ +/* + * 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.constants; + +import io.seata.common.util.StringUtils; + +/** + * database type + * + * @author zhangsen + * @data 2019 /4/2 + */ +public enum DBType { + + /** + * Mysql db type. + */ + MYSQL, + + /** + * Oracle db type. + */ + ORACLE, + + /** + * Db 2 db type. + */ + DB2, + + /** + * Sqlserver db type. + */ + SQLSERVER, + + /** + * Sybaee db type. + */ + SYBAEE, + + /** + * H2 db type. + */ + H2, + + /** + * Sqlite db type. + */ + SQLITE, + + /** + * Access db type. + */ + ACCESS, + + /** + * Postgresql db type. + */ + POSTGRESQL, + + /** + * Oceanbase db type. + */ + OCEANBASE; + + /** + * Valueof db type. + * + * @param dbType the db type + * @return the db type + */ + public static DBType valueof (String dbType){ + for(DBType dt : values()){ + if(StringUtils.equalsIgnoreCase(dt.name(),dbType)){ + return dt; + } + } + throw new IllegalArgumentException("unknown dbtype:" + dbType); + } + +} \ No newline at end of file diff --git a/core/src/main/java/io/seata/core/exception/AbstractExceptionHandler.java b/core/src/main/java/io/seata/core/exception/AbstractExceptionHandler.java index 9aa741dd28dcaf3754de65586e880f74b25a8284..a8501be705ae2c278b4b787cefd27de54e899694 100644 --- a/core/src/main/java/io/seata/core/exception/AbstractExceptionHandler.java +++ b/core/src/main/java/io/seata/core/exception/AbstractExceptionHandler.java @@ -15,6 +15,8 @@ */ package io.seata.core.exception; +import io.seata.config.Configuration; +import io.seata.config.ConfigurationFactory; import io.seata.core.protocol.ResultCode; import io.seata.core.protocol.transaction.AbstractTransactionRequest; import io.seata.core.protocol.transaction.AbstractTransactionResponse; @@ -26,6 +28,11 @@ import io.seata.core.protocol.transaction.AbstractTransactionResponse; */ public abstract class AbstractExceptionHandler { + /** + * The constant CONFIG. + */ + protected static final Configuration CONFIG = ConfigurationFactory.getInstance(); + /** * The interface Callback. * diff --git a/core/src/main/java/io/seata/core/lock/AbstractLocker.java b/core/src/main/java/io/seata/core/lock/AbstractLocker.java new file mode 100644 index 0000000000000000000000000000000000000000..ddb0dd073ef8c6c0ffa7ac535fd2dd756be8d8df --- /dev/null +++ b/core/src/main/java/io/seata/core/lock/AbstractLocker.java @@ -0,0 +1,85 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.core.lock; + +import io.seata.common.util.CollectionUtils; +import io.seata.core.store.LockDO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +/** + * The type Abstract locker. + * + * @author zhangsen + * @data 2019 -05-15 + */ +public abstract class AbstractLocker implements Locker { + + /** + * The constant LOGGER. + */ + protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractLocker.class); + + /** + * The constant LOCK_SPLIT. + */ + protected static final String LOCK_SPLIT = "^^^"; + + /** + * Convert to lock do list. + * + * @param locks the locks + * @return the list + */ + protected List<LockDO> convertToLockDO(List<RowLock> locks){ + List<LockDO> lockDOs = new ArrayList<>(); + if(CollectionUtils.isEmpty(locks)){ + return lockDOs; + } + for(RowLock rowLock : locks){ + LockDO lockDO = new LockDO(); + lockDO.setBranchId(rowLock.getBranchId()); + lockDO.setPk(rowLock.getPk()); + lockDO.setResourceId(rowLock.getResourceId()); + lockDO.setRowKey(getRowKey(rowLock.getResourceId(), rowLock.getTableName(), rowLock.getPk())); + lockDO.setXid(rowLock.getXid()); + lockDO.setTransactionId(rowLock.getTransactionId()); + lockDO.setTableName(rowLock.getTableName()); + lockDOs.add(lockDO); + } + return lockDOs; + } + + /** + * Get row key string. + * + * @param resourceId the resource id + * @param tableName the table name + * @param pk the pk + * @return the string + */ + protected String getRowKey(String resourceId, String tableName, String pk){ + return new StringBuilder().append(resourceId).append(LOCK_SPLIT).append(tableName).append(LOCK_SPLIT).append(pk).toString(); + } + + @Override + public void cleanAllLocks() { + + } +} diff --git a/core/src/main/java/io/seata/core/lock/LocalDBLocker.java b/core/src/main/java/io/seata/core/lock/LocalDBLocker.java new file mode 100644 index 0000000000000000000000000000000000000000..c342e8f6ecfeaf6adb951b79b63e8551e6071ab8 --- /dev/null +++ b/core/src/main/java/io/seata/core/lock/LocalDBLocker.java @@ -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.core.lock; + +import java.util.List; + +/** + * The type Local db locker. + * + * @author zhangsen + * @data 2019 -05-15 + */ +public class LocalDBLocker extends AbstractLocker { + + @Override + public boolean acquireLock(List<RowLock> rowLock) { + return false; + } + + @Override + public boolean releaseLock(List<RowLock> rowLock) { + return false; + } + + @Override + public boolean isLockable(List<RowLock> rowLock) { + return false; + } +} diff --git a/core/src/main/java/io/seata/core/lock/LockMode.java b/core/src/main/java/io/seata/core/lock/LockMode.java new file mode 100644 index 0000000000000000000000000000000000000000..f1a11d0b9ea30818009ebd3324c7ebf155f7334e --- /dev/null +++ b/core/src/main/java/io/seata/core/lock/LockMode.java @@ -0,0 +1,37 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.core.lock; + +/** + * lock mode + * + * @author zhangsen + * @data 2019 /4/25 + */ +public enum LockMode { + + /** + * store the lock in memory of server + */ + MEMORY, + + /** + * store the lock in db of server + */ + DB; + + +} diff --git a/core/src/main/java/io/seata/core/lock/Locker.java b/core/src/main/java/io/seata/core/lock/Locker.java new file mode 100644 index 0000000000000000000000000000000000000000..8990699d3179e672b709dc270ca4e2bfd3df6d44 --- /dev/null +++ b/core/src/main/java/io/seata/core/lock/Locker.java @@ -0,0 +1,59 @@ +/* + * 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.lock; + +import java.util.List; + +/** + * The interface Locker. + * + * @author zhangsen + * @data 2019 -05-15 + */ +public interface Locker { + + /** + * Acquire lock boolean. + * + * @param rowLock the row lock + * @return the boolean + */ + boolean acquireLock(List<RowLock> rowLock) ; + + /** + * Un lock boolean. + * + * @param rowLock the row lock + * @return the boolean + */ + boolean releaseLock(List<RowLock> rowLock); + + /** + * Is lockable boolean. + * + * @param rowLock the row lock + * @return the boolean + */ + boolean isLockable(List<RowLock> rowLock); + + /** + * Clean all locks boolean. + * + * @return the boolean + */ + void cleanAllLocks(); +} + diff --git a/core/src/main/java/io/seata/core/lock/RowLock.java b/core/src/main/java/io/seata/core/lock/RowLock.java new file mode 100644 index 0000000000000000000000000000000000000000..9675cb9011dfbfb43cdf93cad7e72b5fcc8041ec --- /dev/null +++ b/core/src/main/java/io/seata/core/lock/RowLock.java @@ -0,0 +1,193 @@ +/* + * 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.lock; + +import io.seata.common.util.StringUtils; + +/** + * The type Row lock. + * + * @author zhangsen + * @data 2019 -05-15 + */ +public class RowLock { + + private String xid; + + private Long transactionId; + + private Long branchId; + + private String resourceId; + + private String tableName; + + private String pk; + + private String rowKey; + + private String feature; + + /** + * Gets xid. + * + * @return the xid + */ + public String getXid() { + return xid; + } + + /** + * Sets xid. + * + * @param xid the xid + */ + public void setXid(String xid) { + this.xid = xid; + } + + /** + * Gets transaction id. + * + * @return the transaction id + */ + public Long getTransactionId() { + return transactionId; + } + + /** + * Sets transaction id. + * + * @param transactionId the transaction id + */ + public void setTransactionId(Long transactionId) { + this.transactionId = transactionId; + } + + /** + * Gets branch id. + * + * @return the branch id + */ + public Long getBranchId() { + return branchId; + } + + /** + * Sets branch id. + * + * @param branchId the branch id + */ + public void setBranchId(Long branchId) { + this.branchId = branchId; + } + + /** + * Gets resource id. + * + * @return the resource id + */ + public String getResourceId() { + return resourceId; + } + + /** + * Sets resource id. + * + * @param resourceId the resource id + */ + public void setResourceId(String resourceId) { + this.resourceId = resourceId; + } + + /** + * Gets table name. + * + * @return the table name + */ + public String getTableName() { + return tableName; + } + + /** + * Sets table name. + * + * @param tableName the table name + */ + public void setTableName(String tableName) { + this.tableName = tableName; + } + + /** + * Gets pk. + * + * @return the pk + */ + public String getPk() { + return pk; + } + + /** + * Sets pk. + * + * @param pk the pk + */ + public void setPk(String pk) { + this.pk = pk; + } + + /** + * Gets row key. + * + * @return the row key + */ + public String getRowKey() { + return rowKey; + } + + /** + * Sets row key. + * + * @param rowKey the row key + */ + public void setRowKey(String rowKey) { + this.rowKey = rowKey; + } + + /** + * Gets feature. + * + * @return the feature + */ + public String getFeature() { + return feature; + } + + /** + * Sets feature. + * + * @param feature the feature + */ + public void setFeature(String feature) { + this.feature = feature; + } + + @Override + public String toString(){ + return StringUtils.toString(this); + } +} + diff --git a/core/src/main/java/io/seata/core/protocol/AbstractMessage.java b/core/src/main/java/io/seata/core/protocol/AbstractMessage.java index e6f3dd07e09806d84d15d33ebeb789bb5a27e2b6..60ec91b978dc47578dcaff7feab954784b14759f 100644 --- a/core/src/main/java/io/seata/core/protocol/AbstractMessage.java +++ b/core/src/main/java/io/seata/core/protocol/AbstractMessage.java @@ -18,6 +18,7 @@ package io.seata.core.protocol; import java.io.Serializable; import java.nio.charset.Charset; +import io.seata.common.Constants; import io.seata.core.protocol.transaction.BranchCommitRequest; import io.seata.core.protocol.transaction.BranchCommitResponse; import io.seata.core.protocol.transaction.BranchRegisterRequest; @@ -152,7 +153,7 @@ public abstract class AbstractMessage implements MessageCodec, Serializable { /** * The constant UTF8. */ - protected static final Charset UTF8 = Charset.forName("utf-8"); + protected static final Charset UTF8 = Constants.DEFAULT_CHARSET; /** * The Ctx. */ diff --git a/core/src/main/java/io/seata/core/protocol/Version.java b/core/src/main/java/io/seata/core/protocol/Version.java index 3f5a40470e71efcebe125749b3f86fcb6a191363..de8dcf726bdfaa06499bd858c897b0e72a5a12e9 100644 --- a/core/src/main/java/io/seata/core/protocol/Version.java +++ b/core/src/main/java/io/seata/core/protocol/Version.java @@ -31,7 +31,7 @@ public class Version { /** * The constant CURRENT. */ - public static final String CURRENT = "0.5.2"; + public static final String CURRENT = "0.6.0"; /** * The constant VERSION_MAP. diff --git a/core/src/main/java/io/seata/core/rpc/ChannelManager.java b/core/src/main/java/io/seata/core/rpc/ChannelManager.java index d63086ea2510701a7d83819fc67228eed69e8287..9de2904e93e0adb312fd7d2161fc69a82da4a036 100644 --- a/core/src/main/java/io/seata/core/rpc/ChannelManager.java +++ b/core/src/main/java/io/seata/core/rpc/ChannelManager.java @@ -29,7 +29,6 @@ import io.seata.core.protocol.IncompatibleVersionException; import io.seata.core.protocol.RegisterRMRequest; import io.seata.core.protocol.RegisterTMRequest; import io.seata.core.protocol.Version; -import io.seata.core.rpc.netty.NettyPoolKey.TransactionRole; import io.netty.channel.Channel; import io.seata.core.rpc.netty.NettyPoolKey; diff --git a/core/src/main/java/io/seata/core/rpc/RpcContext.java b/core/src/main/java/io/seata/core/rpc/RpcContext.java index b605b72d067841bc0481c08f7cc24fc621d34697..6ba531eb16a7f183ad4acb68aef5b01ac331a2d8 100644 --- a/core/src/main/java/io/seata/core/rpc/RpcContext.java +++ b/core/src/main/java/io/seata/core/rpc/RpcContext.java @@ -23,7 +23,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import io.seata.common.Constants; -import io.seata.core.rpc.netty.NettyPoolKey.TransactionRole; import io.netty.channel.Channel; import io.seata.core.rpc.netty.NettyPoolKey; diff --git a/core/src/main/java/io/seata/core/rpc/netty/AbstractRpcRemoting.java b/core/src/main/java/io/seata/core/rpc/netty/AbstractRpcRemoting.java index 44c33d31fdf1f6c6e4c9ffbe6c5b655971f6fd3e..d5bf8625ca79b07c032939ee0b2cb043cecba62d 100644 --- a/core/src/main/java/io/seata/core/rpc/netty/AbstractRpcRemoting.java +++ b/core/src/main/java/io/seata/core/rpc/netty/AbstractRpcRemoting.java @@ -35,12 +35,7 @@ import java.util.concurrent.TimeoutException; import io.seata.common.exception.FrameworkErrorCode; import io.seata.common.exception.FrameworkException; import io.seata.common.thread.NamedThreadFactory; -import io.seata.core.protocol.HeartbeatMessage; -import io.seata.core.protocol.MergeMessage; -import io.seata.core.protocol.MessageFuture; -import io.seata.core.protocol.RpcMessage; -import io.seata.core.rpc.Disposable; import io.netty.channel.Channel; import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelFuture; diff --git a/core/src/main/java/io/seata/core/rpc/netty/NettyClientConfig.java b/core/src/main/java/io/seata/core/rpc/netty/NettyClientConfig.java index 2ae97ced878cd0769a94a07cb83ba51ccd7b7578..24c7a0f7c513c833f89ef5cdf1e436f4861e9c54 100644 --- a/core/src/main/java/io/seata/core/rpc/netty/NettyClientConfig.java +++ b/core/src/main/java/io/seata/core/rpc/netty/NettyClientConfig.java @@ -15,8 +15,6 @@ */ package io.seata.core.rpc.netty; -import io.seata.core.rpc.netty.NettyPoolKey.TransactionRole; - import io.netty.channel.Channel; /** diff --git a/core/src/main/java/io/seata/core/rpc/netty/NettyPoolableFactory.java b/core/src/main/java/io/seata/core/rpc/netty/NettyPoolableFactory.java index db29b7804655a27606d1fff89d7b681cb430a639..9d69006f9fb758d045d82ed4cbfe42ac0b68959a 100644 --- a/core/src/main/java/io/seata/core/rpc/netty/NettyPoolableFactory.java +++ b/core/src/main/java/io/seata/core/rpc/netty/NettyPoolableFactory.java @@ -21,7 +21,6 @@ import io.seata.common.exception.FrameworkException; import io.seata.common.util.NetUtil; import io.seata.core.protocol.RegisterRMResponse; import io.seata.core.protocol.RegisterTMResponse; -import io.seata.core.rpc.netty.NettyPoolKey.TransactionRole; import io.netty.channel.Channel; import org.apache.commons.pool.KeyedPoolableObjectFactory; diff --git a/core/src/main/java/io/seata/core/rpc/netty/RegisterCheckAuthHandler.java b/core/src/main/java/io/seata/core/rpc/netty/RegisterCheckAuthHandler.java index d078a4df0dadcae0de5fd50f01012ecb84ff1878..ef0afc75f63805f4c2aac2ad037fa4ea6e18241a 100644 --- a/core/src/main/java/io/seata/core/rpc/netty/RegisterCheckAuthHandler.java +++ b/core/src/main/java/io/seata/core/rpc/netty/RegisterCheckAuthHandler.java @@ -15,8 +15,6 @@ */ package io.seata.core.rpc.netty; -import io.seata.core.protocol.RegisterRMRequest; -import io.seata.core.protocol.RegisterTMRequest; import io.seata.core.protocol.RegisterRMRequest; import io.seata.core.protocol.RegisterTMRequest; diff --git a/core/src/main/java/io/seata/core/rpc/netty/RegisterMsgListener.java b/core/src/main/java/io/seata/core/rpc/netty/RegisterMsgListener.java index 42a9eedbac5694f4334dc45a000bb58de1777401..98ba76fad0374ae459e138973143a08443817aa4 100644 --- a/core/src/main/java/io/seata/core/rpc/netty/RegisterMsgListener.java +++ b/core/src/main/java/io/seata/core/rpc/netty/RegisterMsgListener.java @@ -15,8 +15,6 @@ */ package io.seata.core.rpc.netty; -import io.seata.core.protocol.AbstractMessage; - import io.netty.channel.Channel; import io.seata.core.protocol.AbstractMessage; diff --git a/core/src/main/java/io/seata/core/store/BranchTransactionDO.java b/core/src/main/java/io/seata/core/store/BranchTransactionDO.java new file mode 100644 index 0000000000000000000000000000000000000000..63c784daf6c6676238e22e55dc7f589720bbab63 --- /dev/null +++ b/core/src/main/java/io/seata/core/store/BranchTransactionDO.java @@ -0,0 +1,277 @@ +/* + * 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.store; + + +import io.seata.common.util.StringUtils; +import io.seata.core.model.BranchStatus; + +import java.util.Date; + +/** + * branch transaction data object + * + * @author zhangsen + * @data 2019 /3/26 + */ +public class BranchTransactionDO { + + private String xid; + + private long transactionId; + + private long branchId; + + private String resourceGroupId; + + private String resourceId; + + private String lockKey; + + private String branchType; + + private int status = BranchStatus.Unknown.getCode(); + + private String clientId; + + private String applicationData; + + private Date gmtCreate; + + private Date gmtModified; + + /** + * Gets xid. + * + * @return the xid + */ + public String getXid() { + return xid; + } + + /** + * Sets xid. + * + * @param xid the xid + */ + public void setXid(String xid) { + this.xid = xid; + } + + /** + * Gets transaction id. + * + * @return the transaction id + */ + public long getTransactionId() { + return transactionId; + } + + /** + * Sets transaction id. + * + * @param transactionId the transaction id + */ + public void setTransactionId(long transactionId) { + this.transactionId = transactionId; + } + + /** + * Gets branch id. + * + * @return the branch id + */ + public long getBranchId() { + return branchId; + } + + /** + * Sets branch id. + * + * @param branchId the branch id + */ + public void setBranchId(long branchId) { + this.branchId = branchId; + } + + /** + * Gets resource group id. + * + * @return the resource group id + */ + public String getResourceGroupId() { + return resourceGroupId; + } + + /** + * Sets resource group id. + * + * @param resourceGroupId the resource group id + */ + public void setResourceGroupId(String resourceGroupId) { + this.resourceGroupId = resourceGroupId; + } + + /** + * Gets resource id. + * + * @return the resource id + */ + public String getResourceId() { + return resourceId; + } + + /** + * Sets resource id. + * + * @param resourceId the resource id + */ + public void setResourceId(String resourceId) { + this.resourceId = resourceId; + } + + /** + * Gets lock key. + * + * @return the lock key + */ + public String getLockKey() { + return lockKey; + } + + /** + * Sets lock key. + * + * @param lockKey the lock key + */ + public void setLockKey(String lockKey) { + this.lockKey = lockKey; + } + + /** + * Gets branch type. + * + * @return the branch type + */ + public String getBranchType() { + return branchType; + } + + /** + * Sets branch type. + * + * @param branchType the branch type + */ + public void setBranchType(String branchType) { + this.branchType = branchType; + } + + /** + * Gets status. + * + * @return the status + */ + public int getStatus() { + return status; + } + + /** + * Sets status. + * + * @param status the status + */ + public void setStatus(int status) { + this.status = status; + } + + /** + * Gets client id. + * + * @return the client id + */ + public String getClientId() { + return clientId; + } + + /** + * Sets client id. + * + * @param clientId the client id + */ + public void setClientId(String clientId) { + this.clientId = clientId; + } + + /** + * Gets application data. + * + * @return the application data + */ + public String getApplicationData() { + return applicationData; + } + + /** + * Sets application data. + * + * @param applicationData the application data + */ + public void setApplicationData(String applicationData) { + this.applicationData = applicationData; + } + + /** + * Gets gmt create. + * + * @return the gmt create + */ + public Date getGmtCreate() { + return gmtCreate; + } + + /** + * Sets gmt create. + * + * @param gmtCreate the gmt create + */ + public void setGmtCreate(Date gmtCreate) { + this.gmtCreate = gmtCreate; + } + + /** + * Gets gmt modified. + * + * @return the gmt modified + */ + public Date getGmtModified() { + return gmtModified; + } + + /** + * Sets gmt modified. + * + * @param gmtModified the gmt modified + */ + public void setGmtModified(Date gmtModified) { + this.gmtModified = gmtModified; + } + + @Override + public String toString(){ + return StringUtils.toString(this); + } + +} diff --git a/core/src/main/java/io/seata/core/store/GlobalTransactionDO.java b/core/src/main/java/io/seata/core/store/GlobalTransactionDO.java new file mode 100644 index 0000000000000000000000000000000000000000..74c5e9ae13f7e5e978fb88d4c6fb40f816c78502 --- /dev/null +++ b/core/src/main/java/io/seata/core/store/GlobalTransactionDO.java @@ -0,0 +1,256 @@ +/* + * 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.store; + + +import io.seata.common.util.StringUtils; + +import java.util.Date; + +/** + * Global Transaction data object + * + * @author zhangsen + * @data 2019 /3/26 + */ +public class GlobalTransactionDO { + + private String xid; + + private long transactionId; + + private int status; + + private String applicationId; + + private String transactionServiceGroup; + + private String transactionName; + + private int timeout; + + private long beginTime; + + private String applicationData; + + private Date gmtCreate; + + private Date gmtModified; + + /** + * Gets xid. + * + * @return the xid + */ + public String getXid() { + return xid; + } + + /** + * Sets xid. + * + * @param xid the xid + */ + public void setXid(String xid) { + this.xid = xid; + } + + /** + * Gets status. + * + * @return the status + */ + public int getStatus() { + return status; + } + + /** + * Sets status. + * + * @param status the status + */ + public void setStatus(int status) { + this.status = status; + } + + /** + * Gets application id. + * + * @return the application id + */ + public String getApplicationId() { + return applicationId; + } + + /** + * Sets application id. + * + * @param applicationId the application id + */ + public void setApplicationId(String applicationId) { + this.applicationId = applicationId; + } + + /** + * Gets transaction service group. + * + * @return the transaction service group + */ + public String getTransactionServiceGroup() { + return transactionServiceGroup; + } + + /** + * Sets transaction service group. + * + * @param transactionServiceGroup the transaction service group + */ + public void setTransactionServiceGroup(String transactionServiceGroup) { + this.transactionServiceGroup = transactionServiceGroup; + } + + /** + * Gets transaction name. + * + * @return the transaction name + */ + public String getTransactionName() { + return transactionName; + } + + /** + * Sets transaction name. + * + * @param transactionName the transaction name + */ + public void setTransactionName(String transactionName) { + this.transactionName = transactionName; + } + + /** + * Gets timeout. + * + * @return the timeout + */ + public int getTimeout() { + return timeout; + } + + /** + * Sets timeout. + * + * @param timeout the timeout + */ + public void setTimeout(int timeout) { + this.timeout = timeout; + } + + /** + * Gets begin time. + * + * @return the begin time + */ + public long getBeginTime() { + return beginTime; + } + + /** + * Sets begin time. + * + * @param beginTime the begin time + */ + public void setBeginTime(long beginTime) { + this.beginTime = beginTime; + } + + /** + * Gets transaction id. + * + * @return the transaction id + */ + public long getTransactionId() { + return transactionId; + } + + /** + * Sets transaction id. + * + * @param transactionId the transaction id + */ + public void setTransactionId(long transactionId) { + this.transactionId = transactionId; + } + + /** + * Gets application data. + * + * @return the application data + */ + public String getApplicationData() { + return applicationData; + } + + /** + * Sets application data. + * + * @param applicationData the application data + */ + public void setApplicationData(String applicationData) { + this.applicationData = applicationData; + } + + /** + * Gets gmt create. + * + * @return the gmt create + */ + public Date getGmtCreate() { + return gmtCreate; + } + + /** + * Sets gmt create. + * + * @param gmtCreate the gmt create + */ + public void setGmtCreate(Date gmtCreate) { + this.gmtCreate = gmtCreate; + } + + /** + * Gets gmt modified. + * + * @return the gmt modified + */ + public Date getGmtModified() { + return gmtModified; + } + + /** + * Sets gmt modified. + * + * @param gmtModified the gmt modified + */ + public void setGmtModified(Date gmtModified) { + this.gmtModified = gmtModified; + } + + @Override + public String toString(){ + return StringUtils.toString(this); + } + +} \ No newline at end of file diff --git a/core/src/main/java/io/seata/core/store/LockDO.java b/core/src/main/java/io/seata/core/store/LockDO.java new file mode 100644 index 0000000000000000000000000000000000000000..51b242de627e4f581c1d99ebe048f93770b31f1a --- /dev/null +++ b/core/src/main/java/io/seata/core/store/LockDO.java @@ -0,0 +1,178 @@ +/* + * 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.store; + +import io.seata.common.util.StringUtils; + +/** + * The type Lock do. + * + * @author zhangsen + * @data 2019 /4/25 + */ +public class LockDO { + + private String xid; + + private Long transactionId; + + private Long branchId; + + private String resourceId; + + private String tableName; + + private String pk; + + private String rowKey; + + /** + * Instantiates a new Lock do. + */ + public LockDO() { + } + + /** + * Gets xid. + * + * @return the xid + */ + public String getXid() { + return xid; + } + + /** + * Sets xid. + * + * @param xid the xid + */ + public void setXid(String xid) { + this.xid = xid; + } + + /** + * Gets transaction id. + * + * @return the transaction id + */ + public Long getTransactionId() { + return transactionId; + } + + /** + * Sets transaction id. + * + * @param transactionId the transaction id + */ + public void setTransactionId(Long transactionId) { + this.transactionId = transactionId; + } + + /** + * Gets resource id. + * + * @return the resource id + */ + public String getResourceId() { + return resourceId; + } + + /** + * Sets resource id. + * + * @param resourceId the resource id + */ + public void setResourceId(String resourceId) { + this.resourceId = resourceId; + } + + /** + * Gets row key. + * + * @return the row key + */ + public String getRowKey() { + return rowKey; + } + + /** + * Sets row key. + * + * @param rowKey the row key + */ + public void setRowKey(String rowKey) { + this.rowKey = rowKey; + } + + /** + * Gets table name. + * + * @return the table name + */ + public String getTableName() { + return tableName; + } + + /** + * Sets table name. + * + * @param tableName the table name + */ + public void setTableName(String tableName) { + this.tableName = tableName; + } + + /** + * Gets pk. + * + * @return the pk + */ + public String getPk() { + return pk; + } + + /** + * Sets pk. + * + * @param pk the pk + */ + public void setPk(String pk) { + this.pk = pk; + } + + /** + * Gets branch id. + * + * @return the branch id + */ + public Long getBranchId() { + return branchId; + } + + /** + * Sets branch id. + * + * @param branchId the branch id + */ + public void setBranchId(Long branchId) { + this.branchId = branchId; + } + + @Override + public String toString() { + return StringUtils.toString(this); + } +} diff --git a/core/src/main/java/io/seata/core/store/LockStore.java b/core/src/main/java/io/seata/core/store/LockStore.java new file mode 100644 index 0000000000000000000000000000000000000000..49e92e6ab104a88af069a3a8087906964cb1dd5d --- /dev/null +++ b/core/src/main/java/io/seata/core/store/LockStore.java @@ -0,0 +1,68 @@ +/* + * 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.store; + +import java.util.List; + +/** + * The interface Lock store. + * + * @author zhangsen + * @data 2019 /4/25 + */ +public interface LockStore { + + /** + * Acquire lock boolean. + * + * @param lockDO the lock do + * @return the boolean + */ + boolean acquireLock(LockDO lockDO); + + + /** + * Acquire lock boolean. + * + * @param lockDOs the lock d os + * @return the boolean + */ + boolean acquireLock(List<LockDO> lockDOs); + + /** + * Un lock boolean. + * + * @param lockDO the lock do + * @return the boolean + */ + boolean unLock(LockDO lockDO); + + /** + * Un lock boolean. + * + * @param lockDOs the lock d os + * @return the boolean + */ + boolean unLock(List<LockDO> lockDOs); + + /** + * Is lockable boolean. + * + * @param lockDOs the lock do + * @return the boolean + */ + boolean isLockable(List<LockDO> lockDOs); +} diff --git a/core/src/main/java/io/seata/core/store/LogStore.java b/core/src/main/java/io/seata/core/store/LogStore.java new file mode 100644 index 0000000000000000000000000000000000000000..f02b0ddc6d2d2fe1ed7b4fcf6c2b67206b33d6b0 --- /dev/null +++ b/core/src/main/java/io/seata/core/store/LogStore.java @@ -0,0 +1,110 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.core.store; + + +import java.util.List; + +/** + * the transaction log store + * + * @author zhangsen + * @data 2019 /3/26 + */ +public interface LogStore { + + /** + * Query global transaction do global transaction do. + * + * @param xid the xid + * @return the global transaction do + */ + GlobalTransactionDO queryGlobalTransactionDO(String xid); + + /** + * Query global transaction do global transaction do. + * + * @param transactionId the transaction id + * @return the global transaction do + */ + GlobalTransactionDO queryGlobalTransactionDO(long transactionId); + + /** + * Query global transaction do list. + * + * @param status the status + * @param limit the limit + * @return the list + */ + List<GlobalTransactionDO> queryGlobalTransactionDO(int[] status, int limit); + + /** + * Insert global transaction do boolean. + * + * @param globalTransactionDO the global transaction do + * @return the boolean + */ + boolean insertGlobalTransactionDO(GlobalTransactionDO globalTransactionDO); + + /** + * Update global transaction do boolean. + * + * @param globalTransactionDO the global transaction do + * @return the boolean + */ + boolean updateGlobalTransactionDO(GlobalTransactionDO globalTransactionDO); + + /** + * Delete global transaction do boolean. + * + * @param globalTransactionDO the global transaction do + * @return the boolean + */ + boolean deleteGlobalTransactionDO(GlobalTransactionDO globalTransactionDO); + + /** + * Query branch transaction do boolean. + * + * @param xid the xid + * @return the boolean + */ + List<BranchTransactionDO> queryBranchTransactionDO(String xid); + + /** + * Insert branch transaction do boolean. + * + * @param branchTransactionDO the branch transaction do + * @return the boolean + */ + boolean insertBranchTransactionDO(BranchTransactionDO branchTransactionDO); + + /** + * Update branch transaction do boolean. + * + * @param branchTransactionDO the branch transaction do + * @return the boolean + */ + boolean updateBranchTransactionDO(BranchTransactionDO branchTransactionDO); + + /** + * Delete branch transaction do boolean. + * + * @param branchTransactionDO the branch transaction do + * @return the boolean + */ + boolean deleteBranchTransactionDO(BranchTransactionDO branchTransactionDO); + +} \ No newline at end of file diff --git a/core/src/main/java/io/seata/core/store/db/AbstractDataSourceGenerator.java b/core/src/main/java/io/seata/core/store/db/AbstractDataSourceGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..2cdbcb00ce6237948bc7d6bc0df50c16be6236cc --- /dev/null +++ b/core/src/main/java/io/seata/core/store/db/AbstractDataSourceGenerator.java @@ -0,0 +1,148 @@ +/* + * 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.store.db; + +import io.seata.common.exception.StoreException; +import io.seata.common.util.StringUtils; +import io.seata.config.Configuration; +import io.seata.config.ConfigurationFactory; +import io.seata.core.constants.ConfigurationKeys; +import io.seata.core.constants.DBType; + +/** + * The type Abstract data source generator. + * + * @author zhangsen + * @data 2019 /4/24 + */ +public abstract class AbstractDataSourceGenerator implements DataSourceGenerator { + + /** + * The constant CONFIG. + */ + protected static final Configuration CONFIG = ConfigurationFactory.getInstance(); + + /** + * Get db type db type. + * + * @return the db type + */ + protected DBType getDBType(){ + return DBType.valueof(CONFIG.getConfig(ConfigurationKeys.STORE_DB_TYPE)); + } + + /** + * Get url string. + * + * @return the string + */ + protected String getUrl(){ + String url = CONFIG.getConfig(ConfigurationKeys.STORE_DB_URL); + if(StringUtils.isBlank(url)){ + throw new StoreException("the {store.db.url} can't empty."); + } + return url; + } + + /** + * Get user string. + * + * @return the string + */ + protected String getUser(){ + String user = CONFIG.getConfig(ConfigurationKeys.STORE_DB_USER); + if(StringUtils.isBlank(user)){ + throw new StoreException("the {store.db.user} can't empty."); + } + return user; + } + + /** + * Get password string. + * + * @return the string + */ + protected String getPassword(){ + String password = CONFIG.getConfig(ConfigurationKeys.STORE_DB_PASSWORD); + return password; + } + + /** + * Get min conn int. + * + * @return the int + */ + protected int getMinConn(){ + int minConn = CONFIG.getInt(ConfigurationKeys.STORE_DB_MIN_CONN); + return minConn<0?0:minConn; + } + + /** + * Get max conn int. + * + * @return the int + */ + protected int getMaxConn(){ + int maxConn = CONFIG.getInt(ConfigurationKeys.STORE_DB_MAX_CONN); + return maxConn<0?1:maxConn; + } + + + /** + * Get driver name string. + * + * @param dbType the db type + * @return the string + */ + protected static String getDriverName(DBType dbType){ + if(DBType.H2.equals(dbType)){ + return "org.h2.Driver"; + }else if(DBType.MYSQL.equals(dbType)){ + return "com.mysql.jdbc.Driver"; + }else if(DBType.ORACLE.equals(dbType)){ + return "oracle.jdbc.OracleDriver"; + }else if(DBType.SYBAEE.equals(dbType)){ + return "com.sybase.jdbc2.jdbc.SybDriver"; + }else if(DBType.SQLSERVER.equals(dbType)){ + return "com.microsoft.sqlserver.jdbc.SQLServerDriver"; + }else if(DBType.SQLITE.equals(dbType)){ + return "org.sqlite.JDBC"; + }else if(DBType.POSTGRESQL.equals(dbType)){ + return "org.postgresql.Driver"; + }else if(DBType.ACCESS.equals(dbType)){ + return "com.hxtt.sql.access.AccessDriver"; + }else if(DBType.DB2.equals(dbType)){ + return "com.ibm.db2.jcc.DB2Driver"; + }else { + throw new StoreException("Unsupported database type, dbType:" + dbType); + } + } + + /** + * Get validation query string. + * + * @param dbType the db type + * @return the string + */ + protected String getValidationQuery(DBType dbType){ + if(DBType.ORACLE.equals(dbType)){ + return "select sysdate from dual"; + }else { + return "select 1"; + } + } + +} diff --git a/core/src/main/java/io/seata/core/store/db/DataSourceGenerator.java b/core/src/main/java/io/seata/core/store/db/DataSourceGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..5687513969b78841e5753f4494c775da014682bc --- /dev/null +++ b/core/src/main/java/io/seata/core/store/db/DataSourceGenerator.java @@ -0,0 +1,35 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.core.store.db; + +import javax.sql.DataSource; + +/** + * The interface Data source generator. + * + * @author zhangsen + * @data 2019 /4/24 + */ +public interface DataSourceGenerator { + + /** + * create DataSource from config + * + * @return data source + */ + DataSource generateDataSource(); + +} diff --git a/core/src/main/java/io/seata/core/store/db/LockStoreDataBaseDAO.java b/core/src/main/java/io/seata/core/store/db/LockStoreDataBaseDAO.java new file mode 100644 index 0000000000000000000000000000000000000000..b1aac28881446cb93480abae031c65a7d214c774 --- /dev/null +++ b/core/src/main/java/io/seata/core/store/db/LockStoreDataBaseDAO.java @@ -0,0 +1,356 @@ +/* + * 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.store.db; + +import io.seata.common.exception.StoreException; +import io.seata.common.executor.Initialize; +import io.seata.common.loader.LoadLevel; +import io.seata.common.util.StringUtils; +import io.seata.config.Configuration; +import io.seata.config.ConfigurationFactory; +import io.seata.core.constants.ConfigurationKeys; +import io.seata.core.store.LockDO; +import io.seata.core.store.LockStore; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +/** + * The type Data base lock store. + * + * @author zhangsen + * @data 2019 /4/25 + */ +@LoadLevel(name = "db") +public class LockStoreDataBaseDAO implements LockStore, Initialize { + + /** + * The constant CONFIG. + */ + protected static final Configuration CONFIG = ConfigurationFactory.getInstance(); + + /** + * The Log store data source. + */ + protected DataSource logStoreDataSource = null; + + /** + * The Lock table. + */ + protected String lockTable; + + /** + * The Db type. + */ + protected String dbType; + + /** + * Instantiates a new Data base lock store dao. + * + * @param logStoreDataSource the log store data source + */ + public LockStoreDataBaseDAO(DataSource logStoreDataSource) { + this.logStoreDataSource = logStoreDataSource; + } + + @Override + public void init() { + lockTable = CONFIG.getConfig(ConfigurationKeys.LOCK_DB_TABLE, ConfigurationKeys.LOCK_DB_DEFAULT_TABLE); + dbType = CONFIG.getConfig(ConfigurationKeys.STORE_DB_TYPE); + if (StringUtils.isBlank(dbType)) { + throw new StoreException("there must be db type."); + } + if (logStoreDataSource == null) { + throw new StoreException("there must be logStoreDataSource."); + } + } + + @Override + public boolean acquireLock(LockDO lockDO) { + List<LockDO> locks = new ArrayList<>(); + locks.add(lockDO); + return acquireLock(locks); + } + + @Override + public boolean acquireLock(List<LockDO> lockDOs) { + Connection conn = null; + PreparedStatement ps = null; + ResultSet rs = null; + try { + conn = logStoreDataSource.getConnection(); + conn.setAutoCommit(false); + + //check lock + StringBuilder sb = new StringBuilder(); + for(int i = 0; i < lockDOs.size(); i++){ + sb.append("?"); + if( i != (lockDOs.size() - 1)){ + sb.append(", "); + } + } + boolean canLock = true, isReLock = false; + //query + String checkLockSQL = LockStoreSqls.getCheckLockableSql(lockTable, sb.toString(), dbType); + ps = conn.prepareStatement(checkLockSQL); + for (int i = 0; i < lockDOs.size(); i++){ + ps.setString(i+1, lockDOs.get(i).getRowKey()); + } + rs = ps.executeQuery(); + while (rs.next()) { + if(StringUtils.equals(rs.getString("xid"), lockDOs.get(0).getXid())){ + isReLock = true; + }else { + canLock &= false; + } + } + + if(!canLock){ + conn.rollback(); + return false; + } + + if(isReLock){ + conn.rollback(); + return true; + } + + //lock + for(LockDO lockDO : lockDOs){ + if(!doAcquireLock(conn, lockDO)) { + conn.rollback(); + return false; + } + } + conn.commit(); + return true; + } catch (SQLException e) { + throw new StoreException(e); + } finally { + if (rs != null) { + try { + rs.close(); + } catch (SQLException e) { + } + } + if (ps != null) { + try { + ps.close(); + } catch (SQLException e) { + } + } + if (conn != null) { + try { + conn.close(); + } catch (SQLException e) { + } + } + } + } + + @Override + public boolean unLock(LockDO lockDO) { + List<LockDO> locks = new ArrayList<>(); + locks.add(lockDO); + return unLock(locks); + } + + @Override + public boolean unLock(List<LockDO> lockDOs) { + Connection conn = null; + PreparedStatement ps = null; + try { + conn = logStoreDataSource.getConnection(); + conn.setAutoCommit(true); + + StringBuilder sb = new StringBuilder(); + for(int i = 0; i< lockDOs.size(); i++){ + sb.append("?"); + if(i != (lockDOs.size() - 1)){ + sb.append(", "); + } + } + //batch release lock + String batchDeleteSQL = LockStoreSqls.getBatchDeleteLockSql(lockTable, sb.toString(), dbType); + ps = conn.prepareStatement(batchDeleteSQL); + ps.setString(1, lockDOs.get(0).getXid()); + for(int i = 0; i< lockDOs.size(); i++){ + ps.setString(i+2, lockDOs.get(i).getRowKey()); + } + return ps.executeUpdate() > 0; + } catch (SQLException e) { + throw new StoreException(e); + } finally { + if(ps != null){ + try { + ps.close(); + } catch (SQLException e) { + } + } + if (conn != null) { + try { + conn.close(); + } catch (SQLException e) { + } + } + } + } + + @Override + public boolean isLockable(List<LockDO> lockDOs) { + Connection conn = null; + try { + conn = logStoreDataSource.getConnection(); + conn.setAutoCommit(true); + if(!checkLockable(conn, lockDOs)){ + return false; + } + return true; + } catch (SQLException e) { + throw new StoreException(e); + } finally { + if (conn != null) { + try { + conn.close(); + } catch (SQLException e) { + } + } + } + } + + /** + * Do acquire lock boolean. + * + * @param conn the conn + * @param lockDO the lock do + * @return the boolean + */ + protected boolean doAcquireLock(Connection conn, LockDO lockDO) { + PreparedStatement ps = null; + ResultSet rs = null; + try { + //insert + String insertLockSQL = LockStoreSqls.getInsertLockSQL(lockTable, dbType); + ps = conn.prepareStatement(insertLockSQL); + ps.setString(1, lockDO.getXid()); + ps.setLong(2, lockDO.getTransactionId()); + ps.setLong(3, lockDO.getBranchId()); + ps.setString(4, lockDO.getResourceId()); + ps.setString(5, lockDO.getTableName()); + ps.setString(6, lockDO.getPk()); + ps.setString(7, lockDO.getRowKey()); + return ps.executeUpdate() > 0; + } catch (SQLException e) { + throw new StoreException(e); + } finally { + if (rs != null) { + try { + rs.close(); + } catch (SQLException e) { + } + } + if (ps != null) { + try { + ps.close(); + } catch (SQLException e) { + } + } + } + } + + /** + * Check lock boolean. + * + * @param conn the conn + * @param lockDOs the lock do + * @return the boolean + */ + protected boolean checkLockable(Connection conn, List<LockDO> lockDOs){ + PreparedStatement ps = null; + ResultSet rs = null; + try { + StringBuilder sb = new StringBuilder(); + for(int i = 0; i < lockDOs.size(); i++){ + sb.append("?"); + if( i != (lockDOs.size() - 1)){ + sb.append(", "); + } + } + + //query + String checkLockSQL = LockStoreSqls.getCheckLockableSql(lockTable, sb.toString(), dbType); + ps = conn.prepareStatement(checkLockSQL); + for (int i = 0; i < lockDOs.size(); i++){ + ps.setString(i+1, lockDOs.get(i).getRowKey()); + } + rs = ps.executeQuery(); + while (rs.next()) { + String xid = rs.getString("xid"); + if(!StringUtils.equals(xid, lockDOs.get(0).getXid())){ + return false; + } + } + return true; + }catch (SQLException e) { + throw new StoreException(e); + } finally { + if (rs != null) { + try { + rs.close(); + } catch (SQLException e) { + } + } + if (ps != null) { + try { + ps.close(); + } catch (SQLException e) { + } + } + } + } + + /** + * Sets lock table. + * + * @param lockTable the lock table + */ + public void setLockTable(String lockTable) { + this.lockTable = lockTable; + } + + /** + * Sets db type. + * + * @param dbType the db type + */ + public void setDbType(String dbType) { + this.dbType = dbType; + } + + /** + * Sets log store data source. + * + * @param logStoreDataSource the log store data source + */ + public void setLogStoreDataSource(DataSource logStoreDataSource) { + this.logStoreDataSource = logStoreDataSource; + } +} \ No newline at end of file diff --git a/core/src/main/java/io/seata/core/store/db/LockStoreSqls.java b/core/src/main/java/io/seata/core/store/db/LockStoreSqls.java new file mode 100644 index 0000000000000000000000000000000000000000..7c95473e2693b3b7fe2e35c850aae4f34186649a --- /dev/null +++ b/core/src/main/java/io/seata/core/store/db/LockStoreSqls.java @@ -0,0 +1,143 @@ +/* + * 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.store.db; + +import io.seata.common.exception.NotSupportYetException; +import io.seata.core.constants.DBType; + +/** + * The type Lock store sqls. + * + * @author zhangsen + * @data 2019 /4/26 + */ +public class LockStoreSqls { + + /** + * The constant LOCK_TABLE_PLACEHOLD. + */ + public static final String LOCK_TABLE_PLACEHOLD = " #lock_table# "; + + /** + * The constant IN_PARAMS_PLACEHOLD. + */ + public static final String IN_PARAMS_PLACEHOLD = " #in_params# "; + + /** + * The constant ALL_COLUMNS. + */ + public static final String ALL_COLUMNS = "xid, transaction_id, branch_id, resource_id, table_name, pk, row_key, gmt_create, gmt_modified"; + + /** + * The constant INSERT_LOCK_SQL_MYSQL. + */ + public static final String INSERT_LOCK_SQL_MYSQL = "insert into " + LOCK_TABLE_PLACEHOLD + "("+ ALL_COLUMNS +")" + + "values (?, ?, ?, ?, ?, ?, ?, now(), now())"; + + /** + * The constant INSERT_LOCK_SQL_ORACLE. + */ + public static final String INSERT_LOCK_SQL_ORACLE = "insert into " + LOCK_TABLE_PLACEHOLD + "("+ ALL_COLUMNS +")" + + "values (?, ?, ?, ?, ?, ?, ?, sysdate, sysdate)"; + + /** + * The constant DELETE_LOCK_SQL. + */ + public static final String DELETE_LOCK_SQL = "delete from " + LOCK_TABLE_PLACEHOLD + " where row_key = ? and xid = ?"; + + /** + * The constant BATCH_DELETE_LOCK_SQL. + */ + public static final String BATCH_DELETE_LOCK_SQL = "delete from " + LOCK_TABLE_PLACEHOLD + " where xid = ? and row_key in ("+ IN_PARAMS_PLACEHOLD +") "; + + /** + * The constant QUERY_LOCK_SQL. + */ + public static final String QUERY_LOCK_SQL = "select " + ALL_COLUMNS + " from " + LOCK_TABLE_PLACEHOLD + + " where row_key = ? "; + + /** + * The constant CHECK_LOCK_SQL. + */ + public static final String CHECK_LOCK_SQL = "select " + ALL_COLUMNS + " from " + LOCK_TABLE_PLACEHOLD + + " where row_key in ("+ IN_PARAMS_PLACEHOLD +")" ; + + /** + * Get insert lock sql string. + * + * @param lockTable the lock table + * @param dbType the db type + * @return the string + */ + public static String getInsertLockSQL(String lockTable, String dbType){ + if(DBType.MYSQL.name().equalsIgnoreCase(dbType) + || DBType.OCEANBASE.name().equalsIgnoreCase(dbType) + || DBType.H2.name().equalsIgnoreCase(dbType)){ + return INSERT_LOCK_SQL_MYSQL.replace(LOCK_TABLE_PLACEHOLD, lockTable); + }else if(DBType.ORACLE.name().equalsIgnoreCase(dbType)){ + return INSERT_LOCK_SQL_ORACLE.replace(LOCK_TABLE_PLACEHOLD, lockTable); + }else{ + throw new NotSupportYetException("unknown dbType:" + dbType); + } + } + + /** + * Get delete lock sql string. + * + * @param lockTable the lock table + * @param dbType the db type + * @return the string + */ + public static String getDeleteLockSql(String lockTable, String dbType){ + return DELETE_LOCK_SQL.replace(LOCK_TABLE_PLACEHOLD, lockTable); + } + + /** + * Get batch delete lock sql string. + * + * @param lockTable the lock table + * @param paramPlaceHold the param place hold + * @param dbType the db type + * @return the string + */ + public static String getBatchDeleteLockSql(String lockTable, String paramPlaceHold, String dbType){ + return BATCH_DELETE_LOCK_SQL.replace(LOCK_TABLE_PLACEHOLD, lockTable).replace(IN_PARAMS_PLACEHOLD, paramPlaceHold); + } + + /** + * Get query lock sql string. + * + * @param lockTable the lock table + * @param dbType the db type + * @return the string + */ + public static String getQueryLockSql(String lockTable, String dbType){ + return QUERY_LOCK_SQL.replace(LOCK_TABLE_PLACEHOLD, lockTable); + } + + /** + * Get check lock sql string. + * + * @param lockTable the lock table + * @param paramPlaceHold the param place hold + * @param dbType the db type + * @return the string + */ + public static String getCheckLockableSql(String lockTable, String paramPlaceHold, String dbType){ + return CHECK_LOCK_SQL.replace(LOCK_TABLE_PLACEHOLD, lockTable).replace(IN_PARAMS_PLACEHOLD, paramPlaceHold); + } + +} diff --git a/core/src/main/java/io/seata/core/store/db/LogStoreDataBaseDAO.java b/core/src/main/java/io/seata/core/store/db/LogStoreDataBaseDAO.java new file mode 100644 index 0000000000000000000000000000000000000000..4248ce45929b891bf5f3656f147b1e428555c5df --- /dev/null +++ b/core/src/main/java/io/seata/core/store/db/LogStoreDataBaseDAO.java @@ -0,0 +1,533 @@ +/* + * 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.store.db; + +import io.seata.common.exception.StoreException; +import io.seata.common.executor.Initialize; +import io.seata.common.loader.LoadLevel; +import io.seata.common.util.StringUtils; +import io.seata.config.Configuration; +import io.seata.config.ConfigurationFactory; +import io.seata.core.constants.ConfigurationKeys; +import io.seata.core.store.BranchTransactionDO; +import io.seata.core.store.GlobalTransactionDO; +import io.seata.core.store.LogStore; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +/** + * The type Log store data base dao. + * + * @author zhangsen + * @data 2019 /4/2 + */ +@LoadLevel(name = "db") +public class LogStoreDataBaseDAO implements LogStore, Initialize { + + /** + * The constant CONFIG. + */ + protected static final Configuration CONFIG = ConfigurationFactory.getInstance(); + + /** + * The Log store data source. + */ + protected DataSource logStoreDataSource = null; + + /** + * The Global table. + */ + protected String globalTable; + + /** + * The Brach table. + */ + protected String brachTable; + + private String dbType; + + /** + * Instantiates a new Log store data base dao. + */ + public LogStoreDataBaseDAO(){ + } + + /** + * Instantiates a new Log store data base dao. + * + * @param logStoreDataSource the log store data source + */ + public LogStoreDataBaseDAO(DataSource logStoreDataSource) { + this.logStoreDataSource = logStoreDataSource; + } + + @Override + public void init() { + globalTable = CONFIG.getConfig(ConfigurationKeys.STORE_DB_GLOBAL_TABLE, ConfigurationKeys.STORE_DB_GLOBAL_DEFAULT_TABLE); + brachTable = CONFIG.getConfig(ConfigurationKeys.STORE_DB_BRANCH_TABLE, ConfigurationKeys.STORE_DB_BRANCH_DEFAULT_TABLE); + dbType = CONFIG.getConfig(ConfigurationKeys.STORE_DB_TYPE); + if(StringUtils.isBlank(dbType)){ + throw new StoreException("there must be db type."); + } + if(logStoreDataSource == null){ + throw new StoreException("there must be logStoreDataSource."); + } + } + + @Override + public GlobalTransactionDO queryGlobalTransactionDO(String xid) { + String sql = LogStoreSqls.getQueryGlobalTransactionSQL(globalTable, dbType); + Connection conn = null; + PreparedStatement ps = null; + ResultSet rs = null; + try { + conn = logStoreDataSource.getConnection(); + conn.setAutoCommit(true); + ps = conn.prepareStatement(sql); + ps.setString(1, xid); + rs = ps.executeQuery(); + if(rs.next()){ + return convertGlobalTransactionDO(rs); + }else { + return null; + } + }catch (SQLException e){ + throw new StoreException(e); + }finally { + if(rs != null){ + try { + rs.close(); + } catch (SQLException e) { + } + } + if(ps != null){ + try { + ps.close(); + } catch (SQLException e) { + } + } + if(conn != null){ + try { + conn.close(); + } catch (SQLException e) { + } + } + } + } + + + @Override + public GlobalTransactionDO queryGlobalTransactionDO(long transactionId){ + String sql = LogStoreSqls.getQueryGlobalTransactionSQLByTransactionId(globalTable, dbType); + Connection conn = null; + PreparedStatement ps = null; + ResultSet rs = null; + try { + conn = logStoreDataSource.getConnection(); + conn.setAutoCommit(true); + ps = conn.prepareStatement(sql); + ps.setLong(1, transactionId); + rs = ps.executeQuery(); + if(rs.next()){ + return convertGlobalTransactionDO(rs); + }else { + return null; + } + }catch (SQLException e){ + throw new StoreException(e); + }finally { + if(rs != null){ + try { + rs.close(); + } catch (SQLException e) { + } + } + if(ps != null){ + try { + ps.close(); + } catch (SQLException e) { + } + } + if(conn != null){ + try { + conn.close(); + } catch (SQLException e) { + } + } + } + } + + @Override + public List<GlobalTransactionDO> queryGlobalTransactionDO(int[] statuses, int limit) { + List<GlobalTransactionDO> ret = new ArrayList<GlobalTransactionDO>(); + Connection conn = null; + PreparedStatement ps = null; + ResultSet rs = null; + try { + conn = logStoreDataSource.getConnection(); + conn.setAutoCommit(true); + + StringBuilder sb = new StringBuilder(); + for(int i = 0; i < statuses.length; i ++){ + sb.append("?"); + if(i != (statuses.length -1)){ + sb.append(", "); + } + } + + String sql = LogStoreSqls.getQueryGlobalTransactionSQLByStatus(globalTable, dbType, sb.toString()); + ps = conn.prepareStatement(sql); + for(int i = 0; i < statuses.length; i ++){ + int status = statuses[i]; + ps.setInt(i+1, status); + } + ps.setInt(statuses.length +1, limit); + rs = ps.executeQuery(); + while(rs.next()){ + ret.add(convertGlobalTransactionDO(rs)); + } + return ret; + }catch (SQLException e){ + throw new StoreException(e); + }finally { + if(rs != null){ + try { + rs.close(); + } catch (SQLException e) { + } + } + if(ps != null){ + try { + ps.close(); + } catch (SQLException e) { + } + } + if(conn != null){ + try { + conn.close(); + } catch (SQLException e) { + } + } + } + } + + @Override + public boolean insertGlobalTransactionDO(GlobalTransactionDO globalTransactionDO) { + String sql = LogStoreSqls.getInsertGlobalTransactionSQL(globalTable, dbType); + Connection conn = null; + PreparedStatement ps = null; + try{ + conn = logStoreDataSource.getConnection(); + conn.setAutoCommit(true); + ps = conn.prepareStatement(sql); + ps.setString(1, globalTransactionDO.getXid()); + ps.setLong(2, globalTransactionDO.getTransactionId()); + ps.setInt(3, globalTransactionDO.getStatus()); + ps.setString(4, globalTransactionDO.getApplicationId()); + ps.setString(5, globalTransactionDO.getTransactionServiceGroup()); + ps.setString(6, globalTransactionDO.getTransactionName()); + ps.setInt(7, globalTransactionDO.getTimeout()); + ps.setLong(8, globalTransactionDO.getBeginTime()); + ps.setString(9, globalTransactionDO.getApplicationData()); + return ps.executeUpdate() > 0; + }catch (SQLException e){ + throw new StoreException(e); + }finally { + if(ps != null){ + try { + ps.close(); + } catch (SQLException e) { + } + } + if(conn != null){ + try { + conn.close(); + } catch (SQLException e) { + } + } + } + } + + @Override + public boolean updateGlobalTransactionDO(GlobalTransactionDO globalTransactionDO) { + String sql = LogStoreSqls.getUpdateGlobalTransactionStatusSQL(globalTable, dbType); + Connection conn = null; + PreparedStatement ps = null; + try{ + conn = logStoreDataSource.getConnection(); + conn.setAutoCommit(true); + ps = conn.prepareStatement(sql); + ps.setInt(1, globalTransactionDO.getStatus()); + ps.setString(2, globalTransactionDO.getXid()); + return ps.executeUpdate() > 0; + }catch (SQLException e){ + throw new StoreException(e); + }finally { + if(ps != null){ + try { + ps.close(); + } catch (SQLException e) { + } + } + if(conn != null){ + try { + conn.close(); + } catch (SQLException e) { + } + } + } + } + + @Override + public boolean deleteGlobalTransactionDO(GlobalTransactionDO globalTransactionDO) { + String sql = LogStoreSqls.getDeleteGlobalTransactionSQL(globalTable, dbType); + Connection conn = null; + PreparedStatement ps = null; + try{ + conn = logStoreDataSource.getConnection(); + conn.setAutoCommit(true); + ps = conn.prepareStatement(sql); + ps.setString(1, globalTransactionDO.getXid()); + return ps.executeUpdate() > 0; + }catch (SQLException e){ + throw new StoreException(e); + }finally { + if(ps != null){ + try { + ps.close(); + } catch (SQLException e) { + } + } + if(conn != null){ + try { + conn.close(); + } catch (SQLException e) { + } + } + } + } + + @Override + public List<BranchTransactionDO> queryBranchTransactionDO(String xid) { + List<BranchTransactionDO> rets = new ArrayList<>(); + String sql = LogStoreSqls.getQureyBranchTransaction(brachTable, dbType); + Connection conn = null; + PreparedStatement ps = null; + try { + conn = logStoreDataSource.getConnection(); + conn.setAutoCommit(true); + + ps = conn.prepareStatement(sql); + ps.setString(1, xid); + + ResultSet rs = ps.executeQuery(); + while(rs.next()){ + rets.add(convertBranchTransactionDO(rs)); + } + return rets; + } catch (SQLException e) { + throw new StoreException(e); + } finally { + if (ps != null) { + try { + ps.close(); + } catch (SQLException e) { + } + } + if (conn != null) { + try { + conn.close(); + } catch (SQLException e) { + } + } + } + } + + @Override + public boolean insertBranchTransactionDO(BranchTransactionDO branchTransactionDO) { + String sql = LogStoreSqls.getInsertBranchTransactionSQL(brachTable, dbType); + Connection conn = null; + PreparedStatement ps = null; + try { + conn = logStoreDataSource.getConnection(); + conn.setAutoCommit(true); + ps = conn.prepareStatement(sql); + ps.setString(1, branchTransactionDO.getXid()); + ps.setLong(2, branchTransactionDO.getTransactionId()); + ps.setLong(3, branchTransactionDO.getBranchId()); + ps.setString(4, branchTransactionDO.getResourceGroupId()); + ps.setString(5, branchTransactionDO.getResourceId()); + ps.setString(6, branchTransactionDO.getLockKey()); + ps.setString(7, branchTransactionDO.getBranchType()); + ps.setInt(8, branchTransactionDO.getStatus()); + ps.setString(9, branchTransactionDO.getClientId()); + ps.setString(10, branchTransactionDO.getApplicationData()); + return ps.executeUpdate() > 0; + } catch (SQLException e) { + throw new StoreException(e); + } finally { + if (ps != null) { + try { + ps.close(); + } catch (SQLException e) { + } + } + if (conn != null) { + try { + conn.close(); + } catch (SQLException e) { + } + } + } + } + + @Override + public boolean updateBranchTransactionDO(BranchTransactionDO branchTransactionDO) { + String sql = LogStoreSqls.getUpdateBranchTransactionStatusSQL(brachTable, dbType); + Connection conn = null; + PreparedStatement ps = null; + try{ + conn = logStoreDataSource.getConnection(); + conn.setAutoCommit(true); + ps = conn.prepareStatement(sql); + ps.setInt(1, branchTransactionDO.getStatus()); + ps.setString(2, branchTransactionDO.getXid()); + ps.setLong(3, branchTransactionDO.getBranchId()); + return ps.executeUpdate() > 0; + }catch (SQLException e){ + throw new StoreException(e); + }finally { + if(ps != null){ + try { + ps.close(); + } catch (SQLException e) { + } + } + if(conn != null){ + try { + conn.close(); + } catch (SQLException e) { + } + } + } + } + + @Override + public boolean deleteBranchTransactionDO(BranchTransactionDO branchTransactionDO) { + String sql = LogStoreSqls.getDeleteBranchTransactionByBranchIdSQL(brachTable, dbType); + Connection conn = null; + PreparedStatement ps = null; + try{ + conn = logStoreDataSource.getConnection(); + conn.setAutoCommit(true); + ps = conn.prepareStatement(sql); + ps.setString(1, branchTransactionDO.getXid()); + ps.setLong(2, branchTransactionDO.getBranchId()); + return ps.executeUpdate() > 0; + }catch (SQLException e){ + throw new StoreException(e); + }finally { + if(ps != null){ + try { + ps.close(); + } catch (SQLException e) { + } + } + if(conn != null){ + try { + conn.close(); + } catch (SQLException e) { + } + } + } + } + + + private GlobalTransactionDO convertGlobalTransactionDO(ResultSet rs) throws SQLException { + GlobalTransactionDO globalTransactionDO = new GlobalTransactionDO(); + globalTransactionDO.setXid(rs.getString("xid")); + globalTransactionDO.setStatus(rs.getInt("status")); + globalTransactionDO.setApplicationId(rs.getString("application_id")); + globalTransactionDO.setBeginTime(rs.getLong("begin_time")); + globalTransactionDO.setTimeout(rs.getInt("timeout")); + globalTransactionDO.setTransactionId(rs.getLong("transaction_id")); + globalTransactionDO.setTransactionName(rs.getString("transaction_name")); + globalTransactionDO.setTransactionServiceGroup(rs.getString("transaction_service_group")); + globalTransactionDO.setApplicationData(rs.getString("application_data")); + globalTransactionDO.setGmtCreate(rs.getTimestamp("gmt_create")); + globalTransactionDO.setGmtModified(rs.getTimestamp("gmt_modified")); + return globalTransactionDO; + } + + private BranchTransactionDO convertBranchTransactionDO(ResultSet rs) throws SQLException { + BranchTransactionDO branchTransactionDO = new BranchTransactionDO(); + branchTransactionDO.setResourceGroupId(rs.getString("resource_group_id")); + branchTransactionDO.setStatus(rs.getInt("status")); + branchTransactionDO.setApplicationData(rs.getString("application_data")); + branchTransactionDO.setClientId(rs.getString("client_id")); + branchTransactionDO.setLockKey(rs.getString("lock_key")); + branchTransactionDO.setXid(rs.getString("xid")); + branchTransactionDO.setResourceId(rs.getString("resource_id")); + branchTransactionDO.setBranchId(rs.getLong("branch_id")); + branchTransactionDO.setBranchType(rs.getString("branch_type")); + branchTransactionDO.setTransactionId(rs.getLong("transaction_id")); + branchTransactionDO.setGmtCreate(rs.getTimestamp("gmt_create")); + branchTransactionDO.setGmtModified(rs.getTimestamp("gmt_modified")); + return branchTransactionDO; + } + + /** + * Sets log store data source. + * + * @param logStoreDataSource the log store data source + */ + public void setLogStoreDataSource(DataSource logStoreDataSource) { + this.logStoreDataSource = logStoreDataSource; + } + + /** + * Sets global table. + * + * @param globalTable the global table + */ + public void setGlobalTable(String globalTable) { + this.globalTable = globalTable; + } + + /** + * Sets brach table. + * + * @param brachTable the brach table + */ + public void setBrachTable(String brachTable) { + this.brachTable = brachTable; + } + + /** + * Sets db type. + * + * @param dbType the db type + */ + public void setDbType(String dbType) { + this.dbType = dbType; + } +} \ No newline at end of file diff --git a/core/src/main/java/io/seata/core/store/db/LogStoreSqls.java b/core/src/main/java/io/seata/core/store/db/LogStoreSqls.java new file mode 100644 index 0000000000000000000000000000000000000000..69308324d633a11b49c875384d0128744ac6ff10 --- /dev/null +++ b/core/src/main/java/io/seata/core/store/db/LogStoreSqls.java @@ -0,0 +1,323 @@ +/* + * 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.store.db; + + +import io.seata.common.exception.NotSupportYetException; +import io.seata.core.constants.DBType; + +/** + * database log store SQLs + * + * @author zhangsen + * @data 2019 /4/2 + */ +public class LogStoreSqls { + + /** + * The constant GLOBAL_TABLE_PLACEHOLD. + */ + public static final String GLOBAL_TABLE_PLACEHOLD = " #global_table# "; + + /** + * The constant BRANCH_TABLE_PLACEHOLD. + */ + public static final String BRANCH_TABLE_PLACEHOLD = " #branch_table# "; + + + /** + * The constant PRAMETER_PLACEHOLD. + */ + public static final String PRAMETER_PLACEHOLD = " #PRAMETER_PLACEHOLD# "; + + /** + * The constant ALL_GLOBAL_COLUMNS. + */ + public static final String ALL_GLOBAL_COLUMNS = "xid, transaction_id, status, application_id, transaction_service_group, transaction_name, timeout, begin_time, application_data, gmt_create, gmt_modified "; + + /** + * The constant ALL_BRANCH_COLUMNS. + */ + protected static final String ALL_BRANCH_COLUMNS = "xid, transaction_id, branch_id, resource_group_id, resource_id, lock_key, branch_type, status, client_id, application_data, gmt_create, gmt_modified "; + + /** + * The constant INSERT_GLOBAL_TRANSACTION_MYSQL. + */ + public static final String INSERT_GLOBAL_TRANSACTION_MYSQL = "insert into " + GLOBAL_TABLE_PLACEHOLD + "("+ ALL_GLOBAL_COLUMNS +")" + + "values(?, ?, ?, ?, ?, ?, ?, ?, ?, now(), now()) "; + + /** + * The constant INSERT_GLOBAL_TRANSACTION_ORACLE. + */ + public static final String INSERT_GLOBAL_TRANSACTION_ORACLE = "insert into " + GLOBAL_TABLE_PLACEHOLD + "("+ ALL_GLOBAL_COLUMNS +")" + + "values(?, ?, ?, ?, ?, ?, ?, ?, ?, sysdate, sysdate) "; + + /** + * The constant UPDATE_GLOBAL_TRANSACTION_STATUS_MYSQL. + */ + public static final String UPDATE_GLOBAL_TRANSACTION_STATUS_MYSQL = "update "+ GLOBAL_TABLE_PLACEHOLD + " set status = ?, gmt_modified = now() where xid = ?"; + + /** + * The constant UPDATE_GLOBAL_TRANSACTION_STATUS_ORACLE. + */ + public static final String UPDATE_GLOBAL_TRANSACTION_STATUS_ORACLE = "update "+ GLOBAL_TABLE_PLACEHOLD + " set status = ?, gmt_modified = sysdate where xid = ?"; + + /** + * The constant DELETE_GLOBAL_TRANSACTION. + */ + public static final String DELETE_GLOBAL_TRANSACTION = "delete from " + GLOBAL_TABLE_PLACEHOLD + " where xid = ?"; + + /** + * The constant QUERY_GLOBAL_TRANSACTION. + */ + public static final String QUERY_GLOBAL_TRANSACTION = "select "+ALL_GLOBAL_COLUMNS+" from " + GLOBAL_TABLE_PLACEHOLD + " where xid = ?"; + + + /** + * The constant QUERY_GLOBAL_TRANSACTION_ID. + */ + public static final String QUERY_GLOBAL_TRANSACTION_BY_ID = "select "+ALL_GLOBAL_COLUMNS+" from " + GLOBAL_TABLE_PLACEHOLD + " where transaction_id = ?"; + + + /** + * The constant QUERY_GLOBAL_TRANSACTION_BY_STATUS. + */ + public static final String QUERY_GLOBAL_TRANSACTION_BY_STATUS = "select "+ALL_GLOBAL_COLUMNS+" from " + GLOBAL_TABLE_PLACEHOLD + + " where status in (" + PRAMETER_PLACEHOLD + ") order by gmt_modified limit ?"; + /** + * The constant QUERY_GLOBAL_TRANSACTION_FOR_RECOVERY_MYSQL. + */ + public static final String QUERY_GLOBAL_TRANSACTION_FOR_RECOVERY_MYSQL = "select "+ALL_GLOBAL_COLUMNS+" from " + GLOBAL_TABLE_PLACEHOLD + " where status in (" + + "0, 2, 3, 4, 5, 6, 7, 8, 10 ,12, 14) order by gmt_modified limit ?"; + + /** + * The constant QUERY_GLOBAL_TRANSACTION_FOR_RECOVERY_ORACLE. + */ + public static final String QUERY_GLOBAL_TRANSACTION_FOR_RECOVERY_ORACLE = "select A.* from ( select "+ALL_GLOBAL_COLUMNS+" from " + GLOBAL_TABLE_PLACEHOLD + " where status in (" + + "0, 2, 3, 4, 5, 6, 7, 8, 10 ,12, 14) order by gmt_modified ) A where ROWNUM <= ?" ; + + + /** + * The constant INSERT_BRANCH_TRANSACTION_MYSQL. + */ + public static final String INSERT_BRANCH_TRANSACTION_MYSQL = "insert into " + BRANCH_TABLE_PLACEHOLD + "("+ ALL_BRANCH_COLUMNS +")" + + "values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, now(), now())"; + + /** + * The constant INSERT_BRANCH_TRANSACTION_ORACLE. + */ + public static final String INSERT_BRANCH_TRANSACTION_ORACLE = "insert into " + BRANCH_TABLE_PLACEHOLD + "("+ ALL_BRANCH_COLUMNS +")" + + "values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, sysdate, sysdate)"; + + /** + * The constant UPDATE_BRANCH_TRANSACTION_STATUS_MYSQL. + */ + public static final String UPDATE_BRANCH_TRANSACTION_STATUS_MYSQL = "update "+ BRANCH_TABLE_PLACEHOLD + " set status = ?, gmt_modified = now() where xid = ? and branch_id = ?"; + + /** + * The constant UPDATE_BRANCH_TRANSACTION_STATUS_ORACLE. + */ + public static final String UPDATE_BRANCH_TRANSACTION_STATUS_ORACLE = "update "+ BRANCH_TABLE_PLACEHOLD + " set status = ?, gmt_modified = sysdate where xid = ? and branch_id = ?" ; + + /** + * The constant DELETE_BRANCH_TRANSACTION_BY_BRANCH_ID. + */ + public static final String DELETE_BRANCH_TRANSACTION_BY_BRANCH_ID = "delete from " + BRANCH_TABLE_PLACEHOLD + " where xid = ? and branch_id = ?"; + + /** + * The constant DELETE_BRANCH_TRANSACTION_BY_XID. + */ + public static final String DELETE_BRANCH_TRANSACTION_BY_XID = "delete from " + BRANCH_TABLE_PLACEHOLD + " where xid = ?"; + + /** + * The constant QUREY_BRANCH_TRANSACTION. + */ + public static final String QUREY_BRANCH_TRANSACTION = "select "+ALL_BRANCH_COLUMNS+" from " + BRANCH_TABLE_PLACEHOLD + " where xid = ?"; + + /** + * Get insert global transaction sql string. + * + * @param globalTable the global table + * @param dbType the db type + * @return the string + */ + public static String getInsertGlobalTransactionSQL(String globalTable, String dbType){ + if(DBType.MYSQL.name().equalsIgnoreCase(dbType) + || DBType.OCEANBASE.name().equalsIgnoreCase(dbType) + || DBType.H2.name().equalsIgnoreCase(dbType)){ + return INSERT_GLOBAL_TRANSACTION_MYSQL.replace(GLOBAL_TABLE_PLACEHOLD, globalTable); + }else if(DBType.ORACLE.name().equalsIgnoreCase(dbType)){ + return INSERT_GLOBAL_TRANSACTION_ORACLE.replace(GLOBAL_TABLE_PLACEHOLD, globalTable); + }else{ + throw new NotSupportYetException("unknown dbType:" + dbType); + } + } + + /** + * Get update global transaction status sql string. + * + * @param globalTable the global table + * @param dbType the db type + * @return the string + */ + public static String getUpdateGlobalTransactionStatusSQL(String globalTable, String dbType){ + if(DBType.MYSQL.name().equalsIgnoreCase(dbType) + || DBType.OCEANBASE.name().equalsIgnoreCase(dbType) + || DBType.H2.name().equalsIgnoreCase(dbType)){ + return UPDATE_GLOBAL_TRANSACTION_STATUS_MYSQL.replace(GLOBAL_TABLE_PLACEHOLD, globalTable); + }else if(DBType.ORACLE.name().equalsIgnoreCase(dbType)){ + return UPDATE_GLOBAL_TRANSACTION_STATUS_ORACLE.replace(GLOBAL_TABLE_PLACEHOLD, globalTable); + }else{ + throw new NotSupportYetException("unknown dbType:" + dbType); + } + } + + /** + * Get delete global transaction sql string. + * + * @param globalTable the global table + * @param dbType the db type + * @return the string + */ + public static String getDeleteGlobalTransactionSQL(String globalTable, String dbType){ + return DELETE_GLOBAL_TRANSACTION.replace(GLOBAL_TABLE_PLACEHOLD, globalTable ); + } + + /** + * Get query global transaction sql string. + * + * @param globalTable the global table + * @param dbType the db type + * @return the string + */ + public static String getQueryGlobalTransactionSQL(String globalTable, String dbType){ + return QUERY_GLOBAL_TRANSACTION.replace(GLOBAL_TABLE_PLACEHOLD, globalTable); + } + + /** + * Get query global transaction sql by transaction id string. + * + * @param globalTable the global table + * @param dbType the db type + * @return the string + */ + public static String getQueryGlobalTransactionSQLByTransactionId(String globalTable, String dbType){ + return QUERY_GLOBAL_TRANSACTION_BY_ID.replace(GLOBAL_TABLE_PLACEHOLD, globalTable); + } + + + /** + * Get query global transaction sql by status string. + * + * @param globalTable the global table + * @param dbType the db type + * @param paramsPlaceHolder the params place holder + * @return the string + */ + public static String getQueryGlobalTransactionSQLByStatus(String globalTable, String dbType, String paramsPlaceHolder){ + return QUERY_GLOBAL_TRANSACTION_BY_STATUS.replace(GLOBAL_TABLE_PLACEHOLD, globalTable).replace(PRAMETER_PLACEHOLD, paramsPlaceHolder); + } + + /** + * Get query global transaction for recovery sql string. + * + * @param globalTable the global table + * @param dbType the db type + * @return the string + */ + public static String getQueryGlobalTransactionForRecoverySQL(String globalTable, String dbType){ + if(DBType.MYSQL.name().equalsIgnoreCase(dbType) + || DBType.OCEANBASE.name().equalsIgnoreCase(dbType) + || DBType.H2.name().equalsIgnoreCase(dbType)){ + return QUERY_GLOBAL_TRANSACTION_FOR_RECOVERY_MYSQL.replace(GLOBAL_TABLE_PLACEHOLD, globalTable); + }else if(DBType.ORACLE.name().equalsIgnoreCase(dbType)){ + return QUERY_GLOBAL_TRANSACTION_FOR_RECOVERY_ORACLE.replace(GLOBAL_TABLE_PLACEHOLD, globalTable); + }else{ + throw new NotSupportYetException("unknown dbType:" + dbType); + } + } + + /** + * Get insert branch transaction sql string. + * + * @param branchTable the branch table + * @param dbType the db type + * @return the string + */ + public static String getInsertBranchTransactionSQL(String branchTable, String dbType){ + if(DBType.MYSQL.name().equalsIgnoreCase(dbType) + || DBType.OCEANBASE.name().equalsIgnoreCase(dbType) + || DBType.H2.name().equalsIgnoreCase(dbType)){ + return INSERT_BRANCH_TRANSACTION_MYSQL.replace(BRANCH_TABLE_PLACEHOLD, branchTable); + }else if(DBType.ORACLE.name().equalsIgnoreCase(dbType)){ + return INSERT_BRANCH_TRANSACTION_ORACLE.replace(BRANCH_TABLE_PLACEHOLD, branchTable); + }else{ + throw new NotSupportYetException("unknown dbType:" + dbType); + } + } + + /** + * Get update branch transaction status sql string. + * + * @param branchTable the branch table + * @param dbType the db type + * @return the string + */ + public static String getUpdateBranchTransactionStatusSQL(String branchTable, String dbType){ + if(DBType.MYSQL.name().equalsIgnoreCase(dbType) + || DBType.OCEANBASE.name().equalsIgnoreCase(dbType) + || DBType.H2.name().equalsIgnoreCase(dbType)){ + return UPDATE_BRANCH_TRANSACTION_STATUS_MYSQL.replace(BRANCH_TABLE_PLACEHOLD, branchTable); + }else if(DBType.ORACLE.name().equalsIgnoreCase(dbType)){ + return UPDATE_BRANCH_TRANSACTION_STATUS_ORACLE.replace(BRANCH_TABLE_PLACEHOLD, branchTable); + }else{ + throw new NotSupportYetException("unknown dbType:" + dbType); + } + } + + /** + * Get delete branch transaction by branch id sql string. + * + * @param branchTable the branch table + * @param dbType the db type + * @return the string + */ + public static String getDeleteBranchTransactionByBranchIdSQL(String branchTable, String dbType){ + return DELETE_BRANCH_TRANSACTION_BY_BRANCH_ID.replace(BRANCH_TABLE_PLACEHOLD, branchTable); + } + + /** + * Get delete branch transaction by x id string. + * + * @param branchTable the branch table + * @param dbType the db type + * @return the string + */ + public static String getDeleteBranchTransactionByXId(String branchTable, String dbType){ + return DELETE_BRANCH_TRANSACTION_BY_XID.replace(BRANCH_TABLE_PLACEHOLD, branchTable); + } + + /** + * Get qurey branch transaction string. + * + * @param branchTable the branch table + * @param dbType the db type + * @return the string + */ + public static String getQureyBranchTransaction(String branchTable, String dbType){ + return QUREY_BRANCH_TRANSACTION.replace(BRANCH_TABLE_PLACEHOLD, branchTable); + } +} \ No newline at end of file diff --git a/core/src/main/resources/META-INF/services/io.seata.core.store.LockStore b/core/src/main/resources/META-INF/services/io.seata.core.store.LockStore new file mode 100644 index 0000000000000000000000000000000000000000..bd6ff3d994a20720bb3bdad8c301924d8e99cd38 --- /dev/null +++ b/core/src/main/resources/META-INF/services/io.seata.core.store.LockStore @@ -0,0 +1 @@ +io.seata.core.store.db.LockStoreDataBaseDAO \ No newline at end of file diff --git a/core/src/main/resources/META-INF/services/io.seata.core.store.LogStore b/core/src/main/resources/META-INF/services/io.seata.core.store.LogStore new file mode 100644 index 0000000000000000000000000000000000000000..f95309566f9bbb2a56d0de1e67ac9e265dd3524e --- /dev/null +++ b/core/src/main/resources/META-INF/services/io.seata.core.store.LogStore @@ -0,0 +1 @@ +io.seata.core.store.db.LogStoreDataBaseDAO \ No newline at end of file diff --git a/core/src/test/java/io/seata/core/protocol/RegisterTMRequestTest.java b/core/src/test/java/io/seata/core/protocol/RegisterTMRequestTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d76a6c79dc47e8822849b119e70de30be98f51fa --- /dev/null +++ b/core/src/test/java/io/seata/core/protocol/RegisterTMRequestTest.java @@ -0,0 +1,192 @@ +/* + * 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.protocol; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import io.netty.buffer.ByteBuf; +import static io.netty.buffer.Unpooled.buffer; + +/** + * RegisterTMRequest Test + * + * @author kaitithoma + * @author Danaykap + * + * @date 2019/05/13 + * + */ + +public class RegisterTMRequestTest { + + private static RegisterTMRequest registerTMRequest; + private static AbstractIdentifyRequest air; + private static final String APP_ID = "applicationId"; + private static final String TSG = "transactionServiceGroup"; + private static final String ED = "extraData"; + private static final short TYPE_CODE = 101; + private static final ByteBuf BB = buffer(128); + + /** Constructor without arguments **/ + @BeforeAll + public static void setupNull() { + registerTMRequest = new RegisterTMRequest(); + air = Mockito.mock( + AbstractIdentifyRequest.class, + Mockito.CALLS_REAL_METHODS); + } + + /** Constructor with arguments **/ + @BeforeAll + public static void setupWithValues() { + registerTMRequest = new RegisterTMRequest(APP_ID, TSG, ED); + air = Mockito.mock( + AbstractIdentifyRequest.class, + Mockito.CALLS_REAL_METHODS); + } + + /** Test get type code **/ + @Test + public void testGetTypeCode() { + Assertions.assertEquals(TYPE_CODE, registerTMRequest.getTypeCode()); + } + + /** + * Test toString having all the parameters initialized to null + */ + @Test + public void testToStringNullValues() { + Assertions.assertEquals("RegisterTMRequest{" + "applicationId='" + null + '\'' + ", transactionServiceGroup='" + + null + '\'' + '}', registerTMRequest.toString()); + } + + /** + * Test decode method with empty parameter + */ + @Test + public void testDecodeEmpty() { + BB.clear(); + Assertions.assertFalse(air.decode(BB)); + } + + /** + * Test decode method with initialized parameter + */ + @Test + public void testDecodeLessThanTwo() { + BB.clear(); + for (int i = 0; i < 2; i++) { + BB.writeShort(i); + } + Assertions.assertFalse(air.decode(BB)); + } + + /** + * Test decode method with initialized parameter + */ + @Test + public void testDecodeMoreThanThree() { + BB.clear(); + for (int i = 0; i < 3; i++) { + BB.writeShort(i); + } + Assertions.assertFalse(air.decode(BB)); + } + + /** + * Test decode method with initialized parameter + */ + @Test + public void testDecodeLessThanFour() { + BB.clear(); + for (int i = 0; i < 4; i++) { + BB.writeShort(i); + } + Assertions.assertFalse(air.decode(BB)); + } + + /** + * Test decode method with initialized parameter + */ + @Test + public void testDecodeMoreLessThanOne() { + BB.clear(); + for (int i = 0; i < 1; i++) { + BB.writeShort(i); + } + Assertions.assertFalse(air.decode(BB)); + } + + /** + * Test decode method with initialized parameter + */ + @Test + public void testDecodeMoreLessThanFourWithZero() { + BB.clear(); + for (int i = 0; i < 4; i++) { + BB.writeZero(i); + } + Assertions.assertFalse(air.decode(BB)); + } + + /** + * Test decode method with initialized parameter + */ + @Test + public void testDecodeFalseLessThanTwoWithDifferentReadable() { + BB.clear(); + for (int i = 0; i < 1; i++) { + BB.writeZero(i); + } + for (int i = 1; i < 2; i++) { + BB.writeShort(i); + } + Assertions.assertFalse(air.decode(BB)); + } + + /** + * Test decode method with initialized parameter + */ + @Test + public void testDecodeTrueLessThanFive() { + BB.clear(); + for (int i = 0; i < 4; i++) { + BB.writeZero(i); + } + for (int i = 4; i < 5; i++) { + BB.writeShort(i); + } + Assertions.assertTrue(air.decode(BB)); + } + + /** + * Test decode method with initialized parameter + */ + @Test + public void testDecodeTrueLessThanSixteen() { + BB.clear(); + for (int i = 0; i < 15; i++) { + BB.writeZero(i); + } + for (int i = 15; i < 16; i++) { + BB.writeShort(i); + } + Assertions.assertTrue(air.decode(BB)); + } + +} diff --git a/core/src/test/java/io/seata/core/store/db/DataBaseLockStoreDAOTest.java b/core/src/test/java/io/seata/core/store/db/DataBaseLockStoreDAOTest.java new file mode 100644 index 0000000000000000000000000000000000000000..eee248eb8cdf8244dc1567a496caeda1481c26c0 --- /dev/null +++ b/core/src/test/java/io/seata/core/store/db/DataBaseLockStoreDAOTest.java @@ -0,0 +1,300 @@ +/* + * 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.store.db; + +import io.seata.core.store.LockDO; +import org.apache.commons.dbcp.BasicDataSource; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Assertions; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +/** + * @author zhangsen + * @data 2019/4/26 + */ +public class DataBaseLockStoreDAOTest { + + static LockStoreDataBaseDAO dataBaseLockStoreDAO = null; + + static BasicDataSource dataSource = null; + + @BeforeAll + public static void start(){ + dataSource = new BasicDataSource(); + dataSource.setDriverClassName("org.h2.Driver"); + dataSource.setUrl("jdbc:h2:./db_store/lock"); + dataSource.setUsername("sa"); + dataSource.setPassword(""); + + dataBaseLockStoreDAO = new LockStoreDataBaseDAO(dataSource); + dataBaseLockStoreDAO.setDbType("h2"); + dataBaseLockStoreDAO.setLockTable("lock_table"); + + prepareTable(dataSource); + } + + private static void prepareTable(BasicDataSource dataSource) { + Connection conn = null; + try { + conn = dataSource.getConnection(); + Statement s = conn.createStatement(); + try { + s.execute("drop table lock_table"); + } catch (Exception e) { + } + s.execute("CREATE TABLE lock_table ( xid varchar(96) , transaction_id long , branch_id long, resource_id varchar(32) ,table_name varchar(32) ,pk varchar(32) , row_key varchar(128) primary key not null, gmt_create TIMESTAMP(6) ,gmt_modified TIMESTAMP(6) ) "); + System.out.println("create table lock_table success."); + + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (conn != null) { + try { + conn.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + } + } + + @Test + public void test_acquireLocks() throws SQLException { + List<LockDO> lockDOs = new ArrayList<>(); + for(int i = 0; i < 3; i++){ + LockDO lock = new LockDO(); + lock.setResourceId("abc"); + lock.setXid("abc-123:123"); + lock.setTransactionId(123L); + lock.setBranchId((long) i); + lock.setRowKey("abc-"+i); + lock.setPk(String.valueOf(i)); + lock.setTableName("t"); + lockDOs.add(lock); + } + + boolean ret = dataBaseLockStoreDAO.acquireLock(lockDOs); + Assertions.assertTrue(ret); + + String sql = "select * from lock_table where xid = 'abc-123:123' and table_name = 't' and row_key in ('abc-0','abc-1','abc-2')" ; + Connection conn = null; + try{ + conn = dataSource.getConnection(); + ResultSet rs = conn.createStatement().executeQuery(sql); + if(rs.next()){ + Assertions.assertTrue(true); + }else { + Assertions.assertTrue(false); + } + } finally { + if(conn != null){ + try { + conn.close(); + } catch (SQLException e) { + } + } + } + + Assertions.assertTrue(dataBaseLockStoreDAO.unLock(lockDOs)); + + } + + + @Test + public void test_re_acquireLocks() throws SQLException { + List<LockDO> lockDOs = new ArrayList<>(); + for(int i = 0; i < 3; i++){ + LockDO lock = new LockDO(); + lock.setResourceId("abc"); + lock.setXid("abc-123:123"); + lock.setTransactionId(123L); + lock.setBranchId((long) i); + lock.setRowKey("abc-"+i); + lock.setPk(String.valueOf(i)); + lock.setTableName("t"); + lockDOs.add(lock); + } + + boolean ret = dataBaseLockStoreDAO.acquireLock(lockDOs); + Assertions.assertTrue(ret); + + String sql = "select * from lock_table where xid = 'abc-123:123' and table_name = 't' and row_key in ('abc-0','abc-1','abc-2')" ; + Connection conn = null; + try{ + conn = dataSource.getConnection(); + ResultSet rs = conn.createStatement().executeQuery(sql); + if(rs.next()){ + Assertions.assertTrue(true); + }else { + Assertions.assertTrue(false); + } + } finally { + if(conn != null){ + try { + conn.close(); + } catch (SQLException e) { + } + } + } + + //lock again + Assertions.assertTrue(dataBaseLockStoreDAO.acquireLock(lockDOs)); + + Assertions.assertTrue(dataBaseLockStoreDAO.unLock(lockDOs)); + + } + + @Test + public void tes_unLocks() throws SQLException { + List<LockDO> lockDOs = new ArrayList<>(); + for(int i = 0; i < 3; i++){ + LockDO lock = new LockDO(); + lock.setResourceId("abc"); + lock.setXid("abc-456:123"); + lock.setTransactionId(123L); + lock.setBranchId((long) i); + lock.setRowKey("abc-"+i); + lock.setPk(String.valueOf(i)); + lock.setTableName("t"); + lockDOs.add(lock); + } + + boolean ret = dataBaseLockStoreDAO.acquireLock(lockDOs); + Assertions.assertTrue(ret); + + String sql = "select * from lock_table where xid = 'abc-456:123' and table_name = 't' and row_key in ('abc-0','abc-1','abc-2')" ; + Connection conn = null; + try{ + conn = dataSource.getConnection(); + ResultSet rs = conn.createStatement().executeQuery(sql); + if(rs.next()){ + Assertions.assertTrue(true); + }else { + Assertions.assertTrue(false); + } + rs.close(); + + //unlock + Assertions.assertTrue(dataBaseLockStoreDAO.unLock(lockDOs)); + + rs = conn.createStatement().executeQuery(sql); + if(rs.next()){ + Assertions.assertTrue(false); + }else { + Assertions.assertTrue(true); + } + + } finally { + if(conn != null){ + try { + conn.close(); + } catch (SQLException e) { + } + } + } + + + } + + + @Test + public void test_isLockable_can(){ + List<LockDO> lockDOs = new ArrayList<>(); + for(int i = 0; i < 3; i++){ + LockDO lock = new LockDO(); + lock.setResourceId("abc"); + lock.setXid("abc-678:123"); + lock.setTransactionId(123L); + lock.setBranchId((long) i); + lock.setRowKey("abc-"+i); + lock.setPk(String.valueOf(i)); + lock.setTableName("t"); + lockDOs.add(lock); + } + + boolean ret = dataBaseLockStoreDAO.acquireLock(lockDOs); + Assertions.assertTrue(ret); + + //unlock + Assertions.assertTrue(dataBaseLockStoreDAO.unLock(lockDOs)); + } + + @Test + public void test_isLockable_cannot() throws SQLException { + List<LockDO> lockDOs = new ArrayList<>(); + for(int i = 0; i < 3; i++){ + LockDO lock = new LockDO(); + lock.setResourceId("abc"); + lock.setXid("abc-123:222"); + lock.setTransactionId(222L); + lock.setBranchId((long) i); + lock.setRowKey("abc-"+i); + lock.setPk(String.valueOf(i)); + lock.setTableName("t"); + lockDOs.add(lock); + } + + boolean ret = dataBaseLockStoreDAO.acquireLock(lockDOs); + Assertions.assertTrue(ret); + + String sql = "select * from lock_table where xid = 'abc-123:222' and table_name = 't' and row_key in ('abc-0','abc-1','abc-2')" ; + Connection conn = null; + try{ + conn = dataSource.getConnection(); + ResultSet rs = conn.createStatement().executeQuery(sql); + if(rs.next()){ + Assertions.assertTrue(true); + }else { + Assertions.assertTrue(false); + } + } finally { + if(conn != null){ + try { + conn.close(); + } catch (SQLException e) { + } + } + } + + List<LockDO> lockDOs_2 = new ArrayList<>(); + for(int i = 0; i < 3; i++){ + LockDO lock = new LockDO(); + lock.setResourceId("abc"); + lock.setXid("abc-123:333"); + lock.setTransactionId(333L); + lock.setBranchId((long) i); + lock.setRowKey("abc-"+i); + lock.setPk(String.valueOf(i)); + lock.setTableName("t"); + lockDOs_2.add(lock); + } + + boolean ret2 = dataBaseLockStoreDAO.acquireLock(lockDOs_2); + Assertions.assertTrue(!ret2); + + } + + + +} \ No newline at end of file diff --git a/core/src/test/java/io/seata/core/store/db/LogStoreDataBaseDAOTest.java b/core/src/test/java/io/seata/core/store/db/LogStoreDataBaseDAOTest.java new file mode 100644 index 0000000000000000000000000000000000000000..12aaf5259ea700c61b503a93aca3ac9435c93758 --- /dev/null +++ b/core/src/test/java/io/seata/core/store/db/LogStoreDataBaseDAOTest.java @@ -0,0 +1,659 @@ +/* + * 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.store.db; + +import io.seata.common.util.CollectionUtils; +import io.seata.core.store.BranchTransactionDO; +import io.seata.core.store.GlobalTransactionDO; +import org.apache.commons.dbcp.BasicDataSource; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Assertions; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.List; + + +/** + * @author zhangsen + * @data 2019/4/26 + */ +public class LogStoreDataBaseDAOTest { + + static LogStoreDataBaseDAO logStoreDataBaseDAO = null; + + static BasicDataSource dataSource = null; + + @BeforeAll + public static void start(){ + dataSource = new BasicDataSource(); + dataSource.setDriverClassName("org.h2.Driver"); + dataSource.setUrl("jdbc:h2:./db_store/log"); + dataSource.setUsername("sa"); + dataSource.setPassword(""); + + logStoreDataBaseDAO = new LogStoreDataBaseDAO(dataSource); + logStoreDataBaseDAO.setDbType("h2"); + logStoreDataBaseDAO.setGlobalTable("global_table"); + logStoreDataBaseDAO.setBrachTable("branch_table"); + + prepareTable(dataSource); + } + + private static void prepareTable(BasicDataSource dataSource) { + Connection conn = null; + try { + conn = dataSource.getConnection(); + Statement s = conn.createStatement(); + try { + s.execute("drop table global_table"); + } catch (Exception e) { + } +// xid, transaction_id, status, application_id, transaction_service_group, transaction_name, timeout, begin_time, application_data, gmt_create, gmt_modified + s.execute("CREATE TABLE global_table ( xid varchar(96) primary key, transaction_id long , STATUS int, application_id varchar(32), transaction_service_group varchar(32) ,transaction_name varchar(32) ,timeout int, begin_time long, application_data varchar(500), gmt_create TIMESTAMP(6) ,gmt_modified TIMESTAMP(6) ) "); + System.out.println("create table global_table success."); + + try { + s.execute("drop table branch_table"); + } catch (Exception e) { + } +// xid, transaction_id, branch_id, resource_group_id, resource_id, lock_key, branch_type, status, client_id, application_data, gmt_create, gmt_modified + s.execute("CREATE TABLE branch_table ( xid varchar(96), transaction_id long , branch_id long primary key, resource_group_id varchar(32), resource_id varchar(32) ,lock_key varchar(64) ,branch_type varchar(32) , status int , client_id varchar(128), application_data varchar(500), gmt_create TIMESTAMP(6) ,gmt_modified TIMESTAMP(6) ) "); + System.out.println("create table branch_table success."); + + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (conn != null) { + try { + conn.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + } + } + + @Test + public void queryGlobalTransactionDO_by_xid() throws SQLException { + GlobalTransactionDO globalTransactionDO = new GlobalTransactionDO(); + globalTransactionDO.setXid("abc-123:978786"); + globalTransactionDO.setApplicationData("abc=87867978"); + globalTransactionDO.setTransactionServiceGroup("abc"); + globalTransactionDO.setTransactionName("test"); + globalTransactionDO.setTransactionId(143546567); + globalTransactionDO.setTimeout(20); + globalTransactionDO.setBeginTime(System.currentTimeMillis()); + globalTransactionDO.setApplicationId("test"); + globalTransactionDO.setStatus(1); + + boolean ret = logStoreDataBaseDAO.insertGlobalTransactionDO(globalTransactionDO); + Assertions.assertTrue(ret); + + + GlobalTransactionDO globalTransactionDO_db = logStoreDataBaseDAO.queryGlobalTransactionDO("abc-123:978786"); + Assertions.assertNotNull(globalTransactionDO_db); + + Assertions.assertEquals(globalTransactionDO_db.getBeginTime(), globalTransactionDO_db.getBeginTime()); + Assertions.assertEquals(globalTransactionDO_db.getTransactionName(), globalTransactionDO_db.getTransactionName()); + Assertions.assertEquals(globalTransactionDO_db.getTransactionId(), globalTransactionDO_db.getTransactionId()); + Assertions.assertEquals(globalTransactionDO_db.getStatus(), globalTransactionDO_db.getStatus()); + Assertions.assertEquals(globalTransactionDO_db.getTimeout(), globalTransactionDO_db.getTimeout()); + Assertions.assertEquals(globalTransactionDO_db.getTransactionServiceGroup(), globalTransactionDO_db.getTransactionServiceGroup()); + Assertions.assertEquals(globalTransactionDO_db.getApplicationId(), globalTransactionDO_db.getApplicationId()); + Assertions.assertNotNull(globalTransactionDO_db.getGmtCreate()); + Assertions.assertNotNull(globalTransactionDO_db.getGmtModified()); + + + String delSql = "delete from global_table where xid= 'abc-123:978786'"; + Connection conn = null; + try{ + conn = dataSource.getConnection(); + //delete + conn.createStatement().execute(delSql); + }finally { + if(conn != null){ + conn.close(); + } + } + + } + + @Test + public void queryGlobalTransactionDO_by_transaction_id() throws SQLException { + GlobalTransactionDO globalTransactionDO = new GlobalTransactionDO(); + globalTransactionDO.setXid("abc-123:676787978"); + globalTransactionDO.setApplicationData("abc=234356"); + globalTransactionDO.setTransactionServiceGroup("abc"); + globalTransactionDO.setTransactionName("test"); + globalTransactionDO.setTransactionId(867978970); + globalTransactionDO.setTimeout(20); + globalTransactionDO.setBeginTime(System.currentTimeMillis()); + globalTransactionDO.setApplicationId("test"); + globalTransactionDO.setStatus(1); + + boolean ret = logStoreDataBaseDAO.insertGlobalTransactionDO(globalTransactionDO); + Assertions.assertTrue(ret); + + GlobalTransactionDO globalTransactionDO_db = logStoreDataBaseDAO.queryGlobalTransactionDO(867978970L); + Assertions.assertNotNull(globalTransactionDO_db); + + Assertions.assertEquals(globalTransactionDO_db.getXid(), globalTransactionDO_db.getXid()); + Assertions.assertEquals(globalTransactionDO_db.getBeginTime(), globalTransactionDO_db.getBeginTime()); + Assertions.assertEquals(globalTransactionDO_db.getTransactionName(), globalTransactionDO_db.getTransactionName()); + Assertions.assertEquals(globalTransactionDO_db.getTransactionId(), globalTransactionDO_db.getTransactionId()); + Assertions.assertEquals(globalTransactionDO_db.getStatus(), globalTransactionDO_db.getStatus()); + Assertions.assertEquals(globalTransactionDO_db.getTimeout(), globalTransactionDO_db.getTimeout()); + Assertions.assertEquals(globalTransactionDO_db.getTransactionServiceGroup(), globalTransactionDO_db.getTransactionServiceGroup()); + Assertions.assertEquals(globalTransactionDO_db.getApplicationId(), globalTransactionDO_db.getApplicationId()); + Assertions.assertNotNull(globalTransactionDO_db.getGmtCreate()); + Assertions.assertNotNull(globalTransactionDO_db.getGmtModified()); + + String delSql = "delete from global_table where xid= 'abc-123:978786'"; + Connection conn = null; + try{ + conn = dataSource.getConnection(); + //delete + conn.createStatement().execute(delSql); + }finally { + if(conn != null){ + conn.close(); + } + } + + } + + @Test + public void queryGlobalTransactionDO_by_statuses() throws SQLException { + { + GlobalTransactionDO globalTransactionDO = new GlobalTransactionDO(); + globalTransactionDO.setXid("abc-123:1267"); + globalTransactionDO.setApplicationData("abc=234356"); + globalTransactionDO.setTransactionServiceGroup("abc"); + globalTransactionDO.setTransactionName("test"); + globalTransactionDO.setTransactionId(867978970); + globalTransactionDO.setTimeout(20); + globalTransactionDO.setBeginTime(System.currentTimeMillis()); + globalTransactionDO.setApplicationId("test"); + globalTransactionDO.setStatus(1); + + Assertions.assertTrue(logStoreDataBaseDAO.insertGlobalTransactionDO(globalTransactionDO)); + } + { + GlobalTransactionDO globalTransactionDO = new GlobalTransactionDO(); + globalTransactionDO.setXid("abc-123:6978"); + globalTransactionDO.setApplicationData("abc=87867978"); + globalTransactionDO.setTransactionServiceGroup("abc"); + globalTransactionDO.setTransactionName("test"); + globalTransactionDO.setTransactionId(143546567); + globalTransactionDO.setTimeout(20); + globalTransactionDO.setBeginTime(System.currentTimeMillis()); + globalTransactionDO.setApplicationId("test"); + globalTransactionDO.setStatus(2); + + boolean ret = logStoreDataBaseDAO.insertGlobalTransactionDO(globalTransactionDO); + Assertions.assertTrue(ret); + } + { + GlobalTransactionDO globalTransactionDO = new GlobalTransactionDO(); + globalTransactionDO.setXid("abc-123:5657"); + globalTransactionDO.setApplicationData("abc=5454"); + globalTransactionDO.setTransactionServiceGroup("abc"); + globalTransactionDO.setTransactionName("test"); + globalTransactionDO.setTransactionId(12345); + globalTransactionDO.setTimeout(20); + globalTransactionDO.setBeginTime(System.currentTimeMillis()); + globalTransactionDO.setApplicationId("test"); + globalTransactionDO.setStatus(1); + + boolean ret = logStoreDataBaseDAO.insertGlobalTransactionDO(globalTransactionDO); + Assertions.assertTrue(ret); + } + + List<GlobalTransactionDO> globalTransactionDOs = logStoreDataBaseDAO.queryGlobalTransactionDO(new int[]{1}, 10); + Assertions.assertNotNull(globalTransactionDOs); + Assertions.assertEquals(2, globalTransactionDOs.size()); + + if("abc-123:5657".equals(globalTransactionDOs.get(0).getXid()) && "abc-123:1267".equals(globalTransactionDOs.get(1).getXid())){ + Assertions.assertTrue(true); + }else if("abc-123:5657".equals(globalTransactionDOs.get(1).getXid()) && "abc-123:1267".equals(globalTransactionDOs.get(0).getXid())){ + Assertions.assertTrue(true); + }else { + Assertions.assertTrue(false); + } + + String delSql = "delete from global_table where xid in ('abc-123:1267', 'abc-123:6978', 'abc-123:5657')"; + Connection conn = null; + try{ + conn = dataSource.getConnection(); + conn.createStatement().execute(delSql); + }finally { + if(conn != null){ + conn.close(); + } + } + } + + @Test + public void queryGlobalTransactionDO_by_statuses_limit() throws SQLException { + { + GlobalTransactionDO globalTransactionDO = new GlobalTransactionDO(); + globalTransactionDO.setXid("abc-123:1267"); + globalTransactionDO.setApplicationData("abc=234356"); + globalTransactionDO.setTransactionServiceGroup("abc"); + globalTransactionDO.setTransactionName("test"); + globalTransactionDO.setTransactionId(867978970); + globalTransactionDO.setTimeout(20); + globalTransactionDO.setBeginTime(System.currentTimeMillis()); + globalTransactionDO.setApplicationId("test"); + globalTransactionDO.setStatus(1); + + Assertions.assertTrue(logStoreDataBaseDAO.insertGlobalTransactionDO(globalTransactionDO)); + } + { + GlobalTransactionDO globalTransactionDO = new GlobalTransactionDO(); + globalTransactionDO.setXid("abc-123:6978"); + globalTransactionDO.setApplicationData("abc=87867978"); + globalTransactionDO.setTransactionServiceGroup("abc"); + globalTransactionDO.setTransactionName("test"); + globalTransactionDO.setTransactionId(143546567); + globalTransactionDO.setTimeout(20); + globalTransactionDO.setBeginTime(System.currentTimeMillis()); + globalTransactionDO.setApplicationId("test"); + globalTransactionDO.setStatus(2); + + boolean ret = logStoreDataBaseDAO.insertGlobalTransactionDO(globalTransactionDO); + Assertions.assertTrue(ret); + } + { + GlobalTransactionDO globalTransactionDO = new GlobalTransactionDO(); + globalTransactionDO.setXid("abc-123:5657"); + globalTransactionDO.setApplicationData("abc=5454"); + globalTransactionDO.setTransactionServiceGroup("abc"); + globalTransactionDO.setTransactionName("test"); + globalTransactionDO.setTransactionId(12345); + globalTransactionDO.setTimeout(20); + globalTransactionDO.setBeginTime(System.currentTimeMillis()); + globalTransactionDO.setApplicationId("test"); + globalTransactionDO.setStatus(1); + + boolean ret = logStoreDataBaseDAO.insertGlobalTransactionDO(globalTransactionDO); + Assertions.assertTrue(ret); + } + + List<GlobalTransactionDO> globalTransactionDOs = logStoreDataBaseDAO.queryGlobalTransactionDO(new int[]{1}, 1); + Assertions.assertNotNull(globalTransactionDOs); + Assertions.assertEquals(1, globalTransactionDOs.size()); + + if("abc-123:1267".equals(globalTransactionDOs.get(0).getXid())){ + Assertions.assertTrue(true); + }else { + Assertions.assertTrue(false); + } + + String delSql = "delete from global_table where xid in ('abc-123:1267', 'abc-123:6978', 'abc-123:5657')"; + Connection conn = null; + try{ + conn = dataSource.getConnection(); + conn.createStatement().execute(delSql); + }finally { + if(conn != null){ + conn.close(); + } + } + + } + + @Test + public void insertGlobalTransactionDO() throws SQLException { + GlobalTransactionDO globalTransactionDO = new GlobalTransactionDO(); + globalTransactionDO.setXid("abc-123:333"); + globalTransactionDO.setApplicationData("abc=5454"); + globalTransactionDO.setTransactionServiceGroup("abc"); + globalTransactionDO.setTransactionName("test"); + globalTransactionDO.setTransactionId(12345); + globalTransactionDO.setTimeout(20); + globalTransactionDO.setBeginTime(System.currentTimeMillis()); + globalTransactionDO.setApplicationId("test"); + globalTransactionDO.setStatus(1); + + boolean ret = logStoreDataBaseDAO.insertGlobalTransactionDO(globalTransactionDO); + Assertions.assertTrue(ret); + + String sql = "select * from global_table where xid= 'abc-123:333'"; + String delSql = "delete from global_table where xid= 'abc-123:333'"; + Connection conn = null; + try{ + conn = dataSource.getConnection(); + ResultSet rs = conn.createStatement().executeQuery(sql); + if(rs.next()){ + Assertions.assertTrue(true); + }else{ + Assertions.assertTrue(false); + } + + conn.createStatement().execute(delSql); + + }finally { + if(conn != null){ + conn.close(); + } + } + } + + @Test + public void updateGlobalTransactionDO() throws SQLException { + GlobalTransactionDO globalTransactionDO = new GlobalTransactionDO(); + globalTransactionDO.setXid("abc-123:222"); + globalTransactionDO.setApplicationData("abc=5454"); + globalTransactionDO.setTransactionServiceGroup("abc"); + globalTransactionDO.setTransactionName("test"); + globalTransactionDO.setTransactionId(12345); + globalTransactionDO.setTimeout(20); + globalTransactionDO.setBeginTime(System.currentTimeMillis()); + globalTransactionDO.setApplicationId("test"); + globalTransactionDO.setStatus(1); + + boolean ret = logStoreDataBaseDAO.insertGlobalTransactionDO(globalTransactionDO); + Assertions.assertTrue(ret); + + String sql = "select * from global_table where xid= 'abc-123:222'"; + String delSql = "delete from global_table where xid= 'abc-123:222'"; + Connection conn = null; + try{ + conn = dataSource.getConnection(); + ResultSet rs = conn.createStatement().executeQuery(sql); + if(rs.next()){ + Assertions.assertTrue(true); + Assertions.assertEquals(1, rs.getInt("status")); + }else{ + Assertions.assertTrue(false); + } + rs.close(); + + //update + globalTransactionDO.setStatus(2); + Assertions.assertTrue(logStoreDataBaseDAO.updateGlobalTransactionDO(globalTransactionDO)); + + rs = conn.createStatement().executeQuery(sql); + if(rs.next()){ + Assertions.assertTrue(true); + Assertions.assertEquals(2, rs.getInt("status")); + }else{ + Assertions.assertTrue(false); + } + rs.close(); + + //delete + conn.createStatement().execute(delSql); + + }finally { + if(conn != null){ + conn.close(); + } + } + + } + + @Test + public void deleteGlobalTransactionDO() throws SQLException { + GlobalTransactionDO globalTransactionDO = new GlobalTransactionDO(); + globalTransactionDO.setXid("abc-123:555"); + globalTransactionDO.setApplicationData("abc=5454"); + globalTransactionDO.setTransactionServiceGroup("abc"); + globalTransactionDO.setTransactionName("test"); + globalTransactionDO.setTransactionId(12345); + globalTransactionDO.setTimeout(20); + globalTransactionDO.setBeginTime(System.currentTimeMillis()); + globalTransactionDO.setApplicationId("test"); + globalTransactionDO.setStatus(1); + + boolean ret = logStoreDataBaseDAO.insertGlobalTransactionDO(globalTransactionDO); + Assertions.assertTrue(ret); + + //delete + Assertions.assertTrue(logStoreDataBaseDAO.deleteGlobalTransactionDO(globalTransactionDO)); + + //check + + String sql = "select * from global_table where xid= 'abc-123:555'"; + Connection conn = null; + try{ + conn = dataSource.getConnection(); + ResultSet rs = conn.createStatement().executeQuery(sql); + if(rs.next()){ + Assertions.assertTrue(false); + }else{ + Assertions.assertTrue(true); + } + rs.close(); + }finally { + if(conn != null){ + conn.close(); + } + } + } + + @Test + public void queryBranchTransactionDO() throws SQLException { + { + //creata data for test + BranchTransactionDO branchTransactionDO = new BranchTransactionDO(); + branchTransactionDO.setResourceId("qqqq"); + branchTransactionDO.setXid("abc-123:6789"); + branchTransactionDO.setTransactionId(24234235); + branchTransactionDO.setBranchId(345465676); + branchTransactionDO.setBranchType("TCC"); + branchTransactionDO.setResourceGroupId("abc"); + branchTransactionDO.setLockKey("t:1,2,3;t2,4,5,6"); + branchTransactionDO.setResourceGroupId("a"); + branchTransactionDO.setClientId("1.1.1.1"); + branchTransactionDO.setStatus(1); + branchTransactionDO.setApplicationData("abc=123"); + branchTransactionDO.setResourceGroupId("test"); + + boolean ret = logStoreDataBaseDAO.insertBranchTransactionDO(branchTransactionDO); + Assertions.assertTrue(ret); + } + { + //creata data for test + BranchTransactionDO branchTransactionDO = new BranchTransactionDO(); + branchTransactionDO.setResourceId("qqqq"); + branchTransactionDO.setXid("abc-123:6789"); + branchTransactionDO.setTransactionId(24234235); + branchTransactionDO.setBranchId(78563453); + branchTransactionDO.setBranchType("TCC"); + branchTransactionDO.setResourceGroupId("abc"); + branchTransactionDO.setLockKey("t:6;t2:7"); + branchTransactionDO.setResourceGroupId("a"); + branchTransactionDO.setClientId("1.1.1.1"); + branchTransactionDO.setStatus(1); + branchTransactionDO.setApplicationData("abc=123"); + branchTransactionDO.setResourceGroupId("test"); + + boolean ret = logStoreDataBaseDAO.insertBranchTransactionDO(branchTransactionDO); + Assertions.assertTrue(ret); + } + + List<BranchTransactionDO> rets = logStoreDataBaseDAO.queryBranchTransactionDO("abc-123:6789"); + Assertions.assertTrue(CollectionUtils.isNotEmpty(rets)); + Assertions.assertEquals(2, rets.size()); + + if(78563453 == rets.get(0).getBranchId() && 345465676 == rets.get(1).getBranchId()){ + Assertions.assertTrue(true); + }else if(78563453 == rets.get(1).getBranchId() && 345465676 == rets.get(0).getBranchId()){ + Assertions.assertTrue(true); + }else { + Assertions.assertTrue(false); + } + + String delSql = "delete from branch_table where xid= 'abc-123:6789' "; + Connection conn = null; + try{ + conn = dataSource.getConnection(); + + conn.createStatement().execute(delSql); + + }finally { + if(conn != null){ + conn.close(); + } + } + } + + @Test + public void insertBranchTransactionDO() throws SQLException { + BranchTransactionDO branchTransactionDO = new BranchTransactionDO(); + branchTransactionDO.setResourceId("qqqq"); + branchTransactionDO.setXid("abc-123:7777"); + branchTransactionDO.setTransactionId(1285343); + branchTransactionDO.setBranchId(1234508); + branchTransactionDO.setBranchType("TCC"); + branchTransactionDO.setResourceGroupId("abc"); + branchTransactionDO.setLockKey("t:1,2,3;t2,4,5,6"); + branchTransactionDO.setResourceGroupId("a"); + branchTransactionDO.setClientId("1.1.1.1"); + branchTransactionDO.setStatus(1); + branchTransactionDO.setApplicationData("abc=123"); + branchTransactionDO.setResourceGroupId("test"); + + boolean ret = logStoreDataBaseDAO.insertBranchTransactionDO(branchTransactionDO); + Assertions.assertTrue(ret); + + + String sql = "select * from branch_table where xid= 'abc-123:7777' and branch_id = 1234508"; + String delSql = "delete from branch_table where xid= 'abc-123:7777' and branch_id = 1234508"; + Connection conn = null; + try{ + conn = dataSource.getConnection(); + + ResultSet rs = conn.createStatement().executeQuery(sql); + if(rs.next()){ + Assertions.assertTrue(true); + }else { + Assertions.assertTrue(false); + } + + conn.createStatement().execute(delSql); + }finally { + if(conn != null){ + conn.close(); + } + } + + } + + @Test + public void updateBranchTransactionDO() throws SQLException { + BranchTransactionDO branchTransactionDO = new BranchTransactionDO(); + branchTransactionDO.setResourceId("qqqq"); + branchTransactionDO.setXid("abc-123:8888"); + branchTransactionDO.setTransactionId(1285343); + branchTransactionDO.setBranchId(343434318); + branchTransactionDO.setBranchType("TCC"); + branchTransactionDO.setResourceGroupId("abc"); + branchTransactionDO.setLockKey("t:1,2,3;t2,4,5,6"); + branchTransactionDO.setResourceGroupId("a"); + branchTransactionDO.setClientId("1.1.1.1"); + branchTransactionDO.setStatus(1); + branchTransactionDO.setApplicationData("abc=123"); + branchTransactionDO.setResourceGroupId("test"); + + boolean ret = logStoreDataBaseDAO.insertBranchTransactionDO(branchTransactionDO); + Assertions.assertTrue(ret); + + branchTransactionDO.setStatus(3); + Assertions.assertTrue(logStoreDataBaseDAO.updateBranchTransactionDO(branchTransactionDO)); + + String sql = "select * from branch_table where xid= 'abc-123:8888' and branch_id = 343434318"; + String delSql = "delete from branch_table where xid= 'abc-123:8888' and branch_id = 343434318"; + Connection conn = null; + try{ + conn = dataSource.getConnection(); + + ResultSet rs = conn.createStatement().executeQuery(sql); + if(rs.next()){ + Assertions.assertTrue(true); + Assertions.assertEquals(3, rs.getInt("status")); + }else { + Assertions.assertTrue(false); + } + + conn.createStatement().execute(delSql); + }finally { + if(conn != null){ + conn.close(); + } + } + } + + @Test + public void deleteBranchTransactionDO() throws SQLException { + BranchTransactionDO branchTransactionDO = new BranchTransactionDO(); + branchTransactionDO.setResourceId("qqqq"); + branchTransactionDO.setXid("abc-123:9999"); + branchTransactionDO.setTransactionId(1285343); + branchTransactionDO.setBranchId(34567798); + branchTransactionDO.setBranchType("TCC"); + branchTransactionDO.setResourceGroupId("abc"); + branchTransactionDO.setLockKey("t:1,2,3;t2,4,5,6"); + branchTransactionDO.setResourceGroupId("a"); + branchTransactionDO.setClientId("1.1.1.1"); + branchTransactionDO.setStatus(1); + branchTransactionDO.setApplicationData("abc=123"); + branchTransactionDO.setResourceGroupId("test"); + + boolean ret = logStoreDataBaseDAO.insertBranchTransactionDO(branchTransactionDO); + Assertions.assertTrue(ret); + + + String sql = "select * from branch_table where xid= 'abc-123:9999' and branch_id = 34567798"; + String delSql = "delete from branch_table where xid= 'abc-123:9999' and branch_id = 34567798"; + Connection conn = null; + try{ + conn = dataSource.getConnection(); + + ResultSet rs = conn.createStatement().executeQuery(sql); + if(rs.next()){ + Assertions.assertTrue(true); + }else { + Assertions.assertTrue(false); + } + rs.close(); + + //delete + logStoreDataBaseDAO.deleteBranchTransactionDO(branchTransactionDO); + + rs = conn.createStatement().executeQuery(sql); + if(rs.next()){ + Assertions.assertTrue(false); + }else { + Assertions.assertTrue(true); + } + rs.close(); + + }finally { + if(conn != null){ + conn.close(); + } + } + } + +} \ No newline at end of file diff --git a/discovery/seata-discovery-core/src/test/java/io/seata/config/ConfigurationFactoryTest.java b/discovery/seata-discovery-core/src/test/java/io/seata/config/ConfigurationFactoryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..663234beea118b4070074e7e240639fb45b8fbdb --- /dev/null +++ b/discovery/seata-discovery-core/src/test/java/io/seata/config/ConfigurationFactoryTest.java @@ -0,0 +1,32 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.config; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * @author Geng Zhang + */ +class ConfigurationFactoryTest { + + @Test + void getInstance() { + Configuration configuration = ConfigurationFactory.getInstance(); + // check singleton + Assertions.assertEquals(configuration, ConfigurationFactory.getInstance()); + } +} \ No newline at end of file diff --git a/discovery/seata-discovery-eureka/src/main/java/io/seata/discovery/registry/eureka/EurekaRegistryProvider.java b/discovery/seata-discovery-eureka/src/main/java/io/seata/discovery/registry/eureka/EurekaRegistryProvider.java index 8687246b90f4326691946d9c9711d77a28b102d4..ceedb26bd643e360f0eb05336f850d46cbf27af0 100644 --- a/discovery/seata-discovery-eureka/src/main/java/io/seata/discovery/registry/eureka/EurekaRegistryProvider.java +++ b/discovery/seata-discovery-eureka/src/main/java/io/seata/discovery/registry/eureka/EurekaRegistryProvider.java @@ -16,7 +16,6 @@ package io.seata.discovery.registry.eureka; import io.seata.common.loader.LoadLevel; -import io.seata.discovery.registry.RegistryProvider; import io.seata.discovery.registry.RegistryService; import io.seata.discovery.registry.RegistryProvider; diff --git a/discovery/seata-discovery-nacos/src/main/java/io/seata/discovery/registry/nacos/NacosRegistryProvider.java b/discovery/seata-discovery-nacos/src/main/java/io/seata/discovery/registry/nacos/NacosRegistryProvider.java index 1acd0db7f7e5884331f8fc45d2bf4ef833cf06ee..d201ce29c22ecfa048187227808a61c485127834 100644 --- a/discovery/seata-discovery-nacos/src/main/java/io/seata/discovery/registry/nacos/NacosRegistryProvider.java +++ b/discovery/seata-discovery-nacos/src/main/java/io/seata/discovery/registry/nacos/NacosRegistryProvider.java @@ -16,7 +16,6 @@ package io.seata.discovery.registry.nacos; import io.seata.common.loader.LoadLevel; -import io.seata.discovery.registry.RegistryProvider; import io.seata.discovery.registry.RegistryService; import io.seata.discovery.registry.RegistryProvider; diff --git a/discovery/seata-discovery-redis/src/main/java/io/seata/discovery/registry/redis/RedisRegistryProvider.java b/discovery/seata-discovery-redis/src/main/java/io/seata/discovery/registry/redis/RedisRegistryProvider.java index 4585a45b09288cdb25a6450e4b0505f42e380a29..10bd29dcae9b36779f98a56456484048b1c491b3 100644 --- a/discovery/seata-discovery-redis/src/main/java/io/seata/discovery/registry/redis/RedisRegistryProvider.java +++ b/discovery/seata-discovery-redis/src/main/java/io/seata/discovery/registry/redis/RedisRegistryProvider.java @@ -18,8 +18,6 @@ package io.seata.discovery.registry.redis; import io.seata.common.loader.LoadLevel; import io.seata.discovery.registry.RegistryProvider; import io.seata.discovery.registry.RegistryService; -import io.seata.discovery.registry.RegistryProvider; -import io.seata.discovery.registry.RegistryService; /** * @author xingfudeshi@gmail.com diff --git a/discovery/seata-discovery-redis/src/main/java/io/seata/discovery/registry/redis/RedisRegistryServiceImpl.java b/discovery/seata-discovery-redis/src/main/java/io/seata/discovery/registry/redis/RedisRegistryServiceImpl.java index 9c1d87a8810d048ffd9bc9bd6990f221ebe27120..1ea76541b20a846be149f11de61613d00eb1685e 100644 --- a/discovery/seata-discovery-redis/src/main/java/io/seata/discovery/registry/redis/RedisRegistryServiceImpl.java +++ b/discovery/seata-discovery-redis/src/main/java/io/seata/discovery/registry/redis/RedisRegistryServiceImpl.java @@ -34,7 +34,6 @@ import io.seata.common.util.StringUtils; import io.seata.config.Configuration; import io.seata.config.ConfigurationFactory; -import io.seata.discovery.registry.RegistryService; import io.seata.discovery.registry.RegistryService; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.slf4j.Logger; diff --git a/pom.xml b/pom.xml index e3628dda5353c2e1ae04bd8d33249e61d80f6352..d657ed812a4a9f7d79fa3b6be4bf121b1e61dc65 100644 --- a/pom.xml +++ b/pom.xml @@ -82,7 +82,7 @@ <properties> <!-- seata version --> - <revision>0.5.2</revision> + <revision>0.6.0</revision> <!-- Compiler settings properties --> <maven.compiler.source>1.8</maven.compiler.source> @@ -107,6 +107,7 @@ <mockito-core.version>2.23.4</mockito-core.version> <assertj-core.version>3.12.2</assertj-core.version> <junit-platform-launcher.version>1.4.2</junit-platform-launcher.version> + </properties> <!--test--> @@ -142,7 +143,6 @@ <scope>test</scope> </dependency> </dependencies> - <dependencyManagement> <dependencies> <dependency> diff --git a/rm-datasource/pom.xml b/rm-datasource/pom.xml index 8ab21440bb40ac1e7b263efc4cf52e62eadbf24c..3b1a0435f453e409a57e89783e30dbe59943b88d 100644 --- a/rm-datasource/pom.xml +++ b/rm-datasource/pom.xml @@ -48,6 +48,17 @@ <artifactId>caffeine</artifactId> </dependency> + <dependency> + <groupId>commons-dbcp</groupId> + <artifactId>commons-dbcp</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.h2database</groupId> + <artifactId>h2</artifactId> + <scope>test</scope> + </dependency> + </dependencies> diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/AbstractPreparedStatementProxy.java b/rm-datasource/src/main/java/io/seata/rm/datasource/AbstractPreparedStatementProxy.java index b835dc354abd3e0ef3178aa93cd0562f69d0ce89..40adc924fa65e190a72eec5b9e1acc8b9a2a8fab 100644 --- a/rm-datasource/src/main/java/io/seata/rm/datasource/AbstractPreparedStatementProxy.java +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/AbstractPreparedStatementProxy.java @@ -37,7 +37,6 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.List; -import io.seata.rm.datasource.sql.struct.Null; import io.seata.rm.datasource.sql.struct.Null; /** diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/AsyncWorker.java b/rm-datasource/src/main/java/io/seata/rm/datasource/AsyncWorker.java index f6076f882042d4e9c68a8e314bd3295da489e18a..dd5f1baa2c301e1d4af3b7f9f94fccdc1f05d4fb 100644 --- a/rm-datasource/src/main/java/io/seata/rm/datasource/AsyncWorker.java +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/AsyncWorker.java @@ -178,7 +178,7 @@ public class AsyncWorker implements ResourceManagerInbound { int maxSize = xids.size() > branchIds.size() ? xids.size() : branchIds.size(); if(maxSize == UNDOLOG_DELETE_LIMIT_SIZE){ try { - UndoLogManager.batchDeleteUndoLog(xids, branchIds, UNDOLOG_DELETE_LIMIT_SIZE, conn); + UndoLogManager.batchDeleteUndoLog(xids, branchIds, conn); } catch (Exception ex) { LOGGER.warn("Failed to batch delete undo log [" + branchIds + "/" + xids + "]", ex); } @@ -192,7 +192,7 @@ public class AsyncWorker implements ResourceManagerInbound { } try { - UndoLogManager.batchDeleteUndoLog(xids, branchIds, UNDOLOG_DELETE_LIMIT_SIZE, conn); + UndoLogManager.batchDeleteUndoLog(xids, branchIds, conn); }catch (Exception ex) { LOGGER.warn("Failed to batch delete undo log [" + branchIds + "/" + xids + "]", ex); } diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/PreparedStatementProxy.java b/rm-datasource/src/main/java/io/seata/rm/datasource/PreparedStatementProxy.java index 5279ad772d763909db796a70bc0c4914cf60c2b3..492d59be5f2e29035eb4a32614df94c9cb383de8 100644 --- a/rm-datasource/src/main/java/io/seata/rm/datasource/PreparedStatementProxy.java +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/PreparedStatementProxy.java @@ -20,8 +20,6 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; -import io.seata.rm.datasource.exec.ExecuteTemplate; -import io.seata.rm.datasource.exec.StatementCallback; import io.seata.rm.datasource.exec.ExecuteTemplate; import io.seata.rm.datasource.exec.StatementCallback; diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/StatementProxy.java b/rm-datasource/src/main/java/io/seata/rm/datasource/StatementProxy.java index e0b1eb7ab681ed8b75d2c46ef2fcbbce50708337..955fc81c05899967d9356587bc0e42dac97b0878 100644 --- a/rm-datasource/src/main/java/io/seata/rm/datasource/StatementProxy.java +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/StatementProxy.java @@ -19,8 +19,6 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import io.seata.rm.datasource.exec.ExecuteTemplate; -import io.seata.rm.datasource.exec.StatementCallback; import io.seata.rm.datasource.exec.ExecuteTemplate; import io.seata.rm.datasource.exec.StatementCallback; diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/exec/DeleteExecutor.java b/rm-datasource/src/main/java/io/seata/rm/datasource/exec/DeleteExecutor.java index 6a3f3f848b557a7acbf926180e24c2731bb11519..26507eb74f349c13f00972747bbb97ada16a0014 100644 --- a/rm-datasource/src/main/java/io/seata/rm/datasource/exec/DeleteExecutor.java +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/exec/DeleteExecutor.java @@ -29,8 +29,6 @@ import io.seata.rm.datasource.sql.SQLDeleteRecognizer; import io.seata.rm.datasource.sql.SQLRecognizer; import io.seata.rm.datasource.sql.struct.TableMeta; import io.seata.rm.datasource.sql.struct.TableRecords; -import io.seata.rm.datasource.undo.KeywordChecker; -import io.seata.rm.datasource.undo.KeywordCheckerFactory; import io.seata.rm.datasource.undo.KeywordChecker; import io.seata.rm.datasource.undo.KeywordCheckerFactory; diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/sql/WhereRecognizer.java b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/WhereRecognizer.java index be6537bfe12793ab43e21fc383814b391c755967..26d87cd1d389bdcee4181b2ef07fdb92cce1dd64 100644 --- a/rm-datasource/src/main/java/io/seata/rm/datasource/sql/WhereRecognizer.java +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/sql/WhereRecognizer.java @@ -17,7 +17,6 @@ package io.seata.rm.datasource.sql; import java.util.ArrayList; -import io.seata.rm.datasource.ParametersHolder; import io.seata.rm.datasource.ParametersHolder; /** diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/undo/AbstractUndoExecutor.java b/rm-datasource/src/main/java/io/seata/rm/datasource/undo/AbstractUndoExecutor.java index 779da87c8d18ce3ef524c158af330c1167cfe475..36f14f9720393487f4daa9a83da0fc2fbd84d167 100644 --- a/rm-datasource/src/main/java/io/seata/rm/datasource/undo/AbstractUndoExecutor.java +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/undo/AbstractUndoExecutor.java @@ -15,16 +15,25 @@ */ package io.seata.rm.datasource.undo; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.util.ArrayList; - +import com.alibaba.fastjson.JSON; +import io.seata.common.util.StringUtils; +import io.seata.config.ConfigurationFactory; +import io.seata.core.constants.ConfigurationKeys; import io.seata.rm.datasource.DataCompareUtils; import io.seata.rm.datasource.sql.struct.Field; import io.seata.rm.datasource.sql.struct.KeyType; import io.seata.rm.datasource.sql.struct.Row; +import io.seata.rm.datasource.sql.struct.TableMeta; import io.seata.rm.datasource.sql.struct.TableRecords; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; /** * The type Abstract undo executor. @@ -34,6 +43,24 @@ import io.seata.rm.datasource.sql.struct.TableRecords; */ public abstract class AbstractUndoExecutor { + /** + * Logger for AbstractUndoExecutor + **/ + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractUndoExecutor.class); + + /** + * template of check sql + * + * TODO support multiple primary key + */ + private static final String CHECK_SQL_TEMPLATE = "SELECT * FROM %s WHERE %s in (%s)"; + + /** + * Switch of undo data validation + */ + public static final boolean IS_UNDO_DATA_VALIDATION_ENABLE = ConfigurationFactory.getInstance() + .getBoolean(ConfigurationKeys.TRANSACTION_UNOD_DATA_VALIDATION, true); + /** * The Sql undo log. */ @@ -72,11 +99,10 @@ public abstract class AbstractUndoExecutor { */ public void executeOn(Connection conn) throws SQLException { - // no need undo if the before data snapshot is equivalent to the after data snapshot. - if (DataCompareUtils.isRecordsEquals(sqlUndoLog.getBeforeImage(), sqlUndoLog.getAfterImage())) { + if (IS_UNDO_DATA_VALIDATION_ENABLE && !dataValidationAndGoOn(conn)) { return; } - dataValidation(conn); + try { String undoSQL = buildUndoSQL(); @@ -145,9 +171,125 @@ public abstract class AbstractUndoExecutor { * Data validation. * * @param conn the conn - * @throws SQLException the sql exception + * @return return true if data validation is ok and need continue undo, and return false if no need continue undo. + * @throws SQLException the sql exception such as has dirty data */ - protected void dataValidation(Connection conn) throws SQLException { + protected boolean dataValidationAndGoOn(Connection conn) throws SQLException { + + TableRecords beforeRecords = sqlUndoLog.getBeforeImage(); + TableRecords afterRecords = sqlUndoLog.getAfterImage(); + + // Compare current data with before data + // No need undo if the before data snapshot is equivalent to the after data snapshot. + if (DataCompareUtils.isRecordsEquals(beforeRecords, afterRecords)) { + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Stop rollback because there is no data change " + + "between the before data snapshot and the after data snapshot."); + } + // no need continue undo. + return false; + } + // Validate if data is dirty. + TableRecords currentRecords = queryCurrentRecords(conn); + // compare with current data and after image. + if (!DataCompareUtils.isRecordsEquals(afterRecords, currentRecords)) { + + // If current data is not equivalent to the after data, then compare the current data with the before + // data, too. No need continue to undo if current data is equivalent to the before data snapshot + if (DataCompareUtils.isRecordsEquals(beforeRecords, currentRecords)) { + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Stop rollback because there is no data change " + + "between the before data snapshot and the current data snapshot."); + } + // no need continue undo. + return false; + } else { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("check dirty datas failed, old and new data are not equal," + + "tableName:[" + sqlUndoLog.getTableName() + "]," + + "oldRows:[" + JSON.toJSONString(afterRecords.getRows()) + "]," + + "newRows:[" + JSON.toJSONString(currentRecords.getRows()) + "]."); + } + throw new SQLException("Has dirty records when undo."); + } + } + return true; + } + + /** + * Query current records. + * + * @param conn the conn + * @return the table records + * @throws SQLException the sql exception + */ + protected TableRecords queryCurrentRecords(Connection conn) throws SQLException { + TableRecords undoRecords = getUndoRows(); + TableMeta tableMeta = undoRecords.getTableMeta(); + String pkName = tableMeta.getPkName(); + int pkType = tableMeta.getColumnMeta(pkName).getDataType(); + + // pares pk values + Object[] pkValues = parsePkValues(getUndoRows()); + if (pkValues.length == 0) { + return TableRecords.empty(tableMeta); + } + StringBuffer replace = new StringBuffer(); + for (int i = 0; i < pkValues.length; i++) { + replace.append("?,"); + } + // build check sql + String checkSQL = String.format(CHECK_SQL_TEMPLATE, sqlUndoLog.getTableName(), pkName, + replace.substring(0, replace.length() - 1)); + + PreparedStatement statement = null; + ResultSet checkSet = null; + TableRecords currentRecords; + try { + statement = conn.prepareStatement(checkSQL); + for (int i = 1; i <= pkValues.length; i++) { + statement.setObject(i, pkValues[i - 1], pkType); + } + checkSet = statement.executeQuery(); + currentRecords = TableRecords.buildRecords(tableMeta, checkSet); + } finally { + if (checkSet != null) { + try { + checkSet.close(); + } catch (Exception e) { + } + } + if (statement != null) { + try { + statement.close(); + } catch (Exception e) { + } + } + } + return currentRecords; + } + + /** + * Parse pk values object [ ]. + * + * @param records the records + * @return the object [ ] + */ + protected Object[] parsePkValues(TableRecords records) { + String pkName = records.getTableMeta().getPkName(); + List<Row> undoRows = records.getRows(); + Object[] pkValues = new Object[undoRows.size()]; + for (int i = 0; i < undoRows.size(); i++) { + List<Field> fields = undoRows.get(i).getFields(); + if (fields != null) { + for (Field field : fields) { + if (StringUtils.equalsIgnoreCase(pkName, field.getName())) { + pkValues[i] = field.getValue(); + } + } + } + } + return pkValues; } } diff --git a/rm-datasource/src/main/java/io/seata/rm/datasource/undo/UndoLogManager.java b/rm-datasource/src/main/java/io/seata/rm/datasource/undo/UndoLogManager.java index ab278f2e71f7450447bd440c55e9d02642f76b3f..deadf563778274db3f17e47c74e118aa964a7122 100644 --- a/rm-datasource/src/main/java/io/seata/rm/datasource/undo/UndoLogManager.java +++ b/rm-datasource/src/main/java/io/seata/rm/datasource/undo/UndoLogManager.java @@ -18,7 +18,6 @@ package io.seata.rm.datasource.undo; import com.alibaba.druid.util.JdbcConstants; import io.seata.common.exception.NotSupportYetException; import io.seata.common.util.BlobUtils; -import io.seata.common.util.StringUtils; import io.seata.core.exception.TransactionException; import io.seata.rm.datasource.ConnectionContext; import io.seata.rm.datasource.ConnectionProxy; @@ -161,7 +160,7 @@ public final class UndoLogManager { } Blob b = rs.getBlob("rollback_info"); - String rollbackInfo = StringUtils.blob2string(b); + String rollbackInfo = BlobUtils.blob2string(b); BranchUndoLog branchUndoLog = UndoLogParserFactory.getInstance().decode(rollbackInfo); for (SQLUndoLog sqlUndoLog : branchUndoLog.getSqlUndoLogs()) { @@ -234,13 +233,12 @@ public final class UndoLogManager { * * @param xids * @param branchIds - * @param limitSize * @param conn */ - public static void batchDeleteUndoLog(Set<String> xids, Set<Long> branchIds, int limitSize, Connection conn) throws SQLException { + public static void batchDeleteUndoLog(Set<String> xids, Set<Long> branchIds, Connection conn) throws SQLException { int xidSize = xids.size(); int branchIdSize = branchIds.size(); - String batchDeleteSql = toBatchDeleteUndoLogSql(xidSize, branchIdSize,limitSize); + String batchDeleteSql = toBatchDeleteUndoLogSql(xidSize, branchIdSize); PreparedStatement deletePST = null; try { deletePST = conn.prepareStatement(batchDeleteSql); @@ -268,15 +266,14 @@ public final class UndoLogManager { } - protected static String toBatchDeleteUndoLogSql(int xidSize, int branchIdSize,int limitSize) { + protected static String toBatchDeleteUndoLogSql(int xidSize, int branchIdSize) { StringBuilder sqlBuilder = new StringBuilder(); sqlBuilder.append("DELETE FROM ") .append(UNDO_LOG_TABLE_NAME) .append(" WHERE branch_id IN "); - appendInParam(xidSize, sqlBuilder); - sqlBuilder.append(" AND xid IN "); appendInParam(branchIdSize, sqlBuilder); - sqlBuilder.append(" LIMIT ").append(limitSize); + sqlBuilder.append(" AND xid IN "); + appendInParam(xidSize, sqlBuilder); return sqlBuilder.toString(); } diff --git a/rm-datasource/src/test/java/io/seata/rm/datasource/undo/AbstractUndoExecutorTest.java b/rm-datasource/src/test/java/io/seata/rm/datasource/undo/AbstractUndoExecutorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..0d79fa178d00835eb2d884d33ebec799944fab62 --- /dev/null +++ b/rm-datasource/src/test/java/io/seata/rm/datasource/undo/AbstractUndoExecutorTest.java @@ -0,0 +1,316 @@ +/* + * 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.rm.datasource.undo; + +import io.seata.rm.datasource.sql.SQLType; +import io.seata.rm.datasource.sql.struct.ColumnMeta; +import io.seata.rm.datasource.sql.struct.Field; +import io.seata.rm.datasource.sql.struct.Row; +import io.seata.rm.datasource.sql.struct.TableMeta; +import io.seata.rm.datasource.sql.struct.TableRecords; +import org.apache.commons.dbcp.BasicDataSource; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Types; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Geng Zhang + */ +public class AbstractUndoExecutorTest extends BaseExecutorTest { + + static BasicDataSource dataSource = null; + + static Connection connection = null; + + static TableMeta tableMeta = null; + + @BeforeAll + public static void start() throws SQLException { + dataSource = new BasicDataSource(); + dataSource.setDriverClassName("org.h2.Driver"); + dataSource.setUrl("jdbc:h2:./db_store/test_undo"); + dataSource.setUsername("sa"); + dataSource.setPassword(""); + + connection = dataSource.getConnection(); + + tableMeta = mockTableMeta(); + } + + @AfterAll + public static void stop() { + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { + } + } + if (dataSource != null) { + try { + dataSource.close(); + } catch (SQLException e) { + } + } + } + + @BeforeEach + private void prepareTable() { + execSQL("DROP TABLE table_name"); + execSQL("CREATE TABLE table_name ( `id` int(8), `name` varchar(64), PRIMARY KEY (`id`))"); + } + + @Test + public void dataValidationUpdate() throws SQLException { + execSQL("INSERT INTO table_name(id, name) VALUES (12345,'aaa');"); + execSQL("INSERT INTO table_name(id, name) VALUES (12346,'aaa');"); + + TableRecords beforeImage = execQuery(tableMeta, "SELECT * FROM table_name WHERE id IN (12345, 12346);"); + + execSQL("update table_name set name = 'xxx' where id in (12345, 12346);"); + + TableRecords afterImage = execQuery(tableMeta, "SELECT * FROM table_name WHERE id IN (12345, 12346);"); + + SQLUndoLog sqlUndoLog = new SQLUndoLog(); + sqlUndoLog.setSqlType(SQLType.UPDATE); + sqlUndoLog.setTableMeta(tableMeta); + sqlUndoLog.setTableName("table_name"); + sqlUndoLog.setBeforeImage(beforeImage); + sqlUndoLog.setAfterImage(afterImage); + + TestUndoExecutor spy = new TestUndoExecutor(sqlUndoLog, false); + + // case1: normal case before:aaa -> after:xxx -> current:xxx + Assertions.assertTrue(spy.dataValidationAndGoOn(connection)); + + // case2: dirty data before:aaa -> after:xxx -> current:yyy + execSQL("update table_name set name = 'yyy' where id in (12345, 12346);"); + try { + spy.dataValidationAndGoOn(connection); + Assertions.fail(); + } catch (Exception e) { + Assertions.assertTrue(e instanceof SQLException); + } + + // case 3: before == current before:aaa -> after:xxx -> current:aaa + execSQL("update table_name set name = 'aaa' where id in (12345, 12346);"); + Assertions.assertFalse(spy.dataValidationAndGoOn(connection)); + + // case 4: before == after before:aaa -> after:aaa + afterImage = execQuery(tableMeta, "SELECT * FROM table_name WHERE id IN (12345, 12346);"); + sqlUndoLog.setAfterImage(afterImage); + Assertions.assertFalse(spy.dataValidationAndGoOn(connection)); + } + + @Test + public void dataValidationInsert() throws SQLException { + TableRecords beforeImage = execQuery(tableMeta, "SELECT * FROM table_name WHERE id IN (12345, 12346);"); + + execSQL("INSERT INTO table_name(id, name) VALUES (12345,'aaa');"); + execSQL("INSERT INTO table_name(id, name) VALUES (12346,'aaa');"); + + TableRecords afterImage = execQuery(tableMeta, "SELECT * FROM table_name WHERE id IN (12345, 12346);"); + + SQLUndoLog sqlUndoLog = new SQLUndoLog(); + sqlUndoLog.setSqlType(SQLType.INSERT); + sqlUndoLog.setTableMeta(tableMeta); + sqlUndoLog.setTableName("table_name"); + sqlUndoLog.setBeforeImage(beforeImage); + sqlUndoLog.setAfterImage(afterImage); + + TestUndoExecutor spy = new TestUndoExecutor(sqlUndoLog, false); + + // case1: normal case before:0 -> after:2 -> current:2 + Assertions.assertTrue(spy.dataValidationAndGoOn(connection)); + + // case2: dirty data before:0 -> after:2 -> current:2' + execSQL("update table_name set name = 'yyy' where id in (12345, 12346);"); + try { + Assertions.assertTrue(spy.dataValidationAndGoOn(connection)); + Assertions.fail(); + } catch (Exception e) { + Assertions.assertTrue(e instanceof SQLException); + } + + // case3: before == current before:0 -> after:2 -> current:0 + execSQL("delete from table_name where id in (12345, 12346);"); + Assertions.assertFalse(spy.dataValidationAndGoOn(connection)); + + // case 4: before == after before:0 -> after:0 + afterImage = execQuery(tableMeta, "SELECT * FROM table_name WHERE id IN (12345, 12346);"); + sqlUndoLog.setAfterImage(afterImage); + Assertions.assertFalse(spy.dataValidationAndGoOn(connection)); + } + + @Test + public void dataValidationDelete() throws SQLException { + execSQL("INSERT INTO table_name(id, name) VALUES (12345,'aaa');"); + execSQL("INSERT INTO table_name(id, name) VALUES (12346,'aaa');"); + + TableRecords beforeImage = execQuery(tableMeta, "SELECT * FROM table_name WHERE id IN (12345, 12346);"); + + execSQL("delete from table_name where id in (12345, 12346);"); + + TableRecords afterImage = execQuery(tableMeta, "SELECT * FROM table_name WHERE id IN (12345, 12346);"); + + SQLUndoLog sqlUndoLog = new SQLUndoLog(); + sqlUndoLog.setSqlType(SQLType.INSERT); + sqlUndoLog.setTableMeta(tableMeta); + sqlUndoLog.setTableName("table_name"); + sqlUndoLog.setBeforeImage(beforeImage); + sqlUndoLog.setAfterImage(afterImage); + + TestUndoExecutor spy = new TestUndoExecutor(sqlUndoLog, true); + + // case1: normal case before:2 -> after:0 -> current:0 + Assertions.assertTrue(spy.dataValidationAndGoOn(connection)); + + // case2: dirty data before:2 -> after:0 -> current:1 + execSQL("INSERT INTO table_name(id, name) VALUES (12345,'aaa');"); + try { + Assertions.assertTrue(spy.dataValidationAndGoOn(connection)); + Assertions.fail(); + } catch (Exception e) { + Assertions.assertTrue(e instanceof SQLException); + } + + // case3: before == current before:2 -> after:0 -> current:2 + execSQL("INSERT INTO table_name(id, name) VALUES (12346,'aaa');"); + Assertions.assertFalse(spy.dataValidationAndGoOn(connection)); + + // case 4: before == after before:2 -> after:2 + afterImage = execQuery(tableMeta, "SELECT * FROM table_name WHERE id IN (12345, 12346);"); + sqlUndoLog.setAfterImage(afterImage); + Assertions.assertFalse(spy.dataValidationAndGoOn(connection)); + } + + @Test + public void testParsePK() { + TableMeta tableMeta = Mockito.mock(TableMeta.class); + Mockito.when(tableMeta.getPkName()).thenReturn("id"); + Mockito.when(tableMeta.getTableName()).thenReturn("table_name"); + + TableRecords beforeImage = new TableRecords(); + beforeImage.setTableName("table_name"); + beforeImage.setTableMeta(tableMeta); + + List<Row> beforeRows = new ArrayList<>(); + Row row0 = new Row(); + Field field01 = addField(row0, "id", 1, "12345"); + Field field02 = addField(row0, "age", 1, "2"); + beforeRows.add(row0); + Row row1 = new Row(); + Field field11 = addField(row1, "id", 1, "12346"); + Field field12 = addField(row1, "age", 1, "2"); + beforeRows.add(row1); + beforeImage.setRows(beforeRows); + + SQLUndoLog sqlUndoLog = new SQLUndoLog(); + sqlUndoLog.setSqlType(SQLType.UPDATE); + sqlUndoLog.setTableMeta(tableMeta); + sqlUndoLog.setTableName("table_name"); + sqlUndoLog.setBeforeImage(beforeImage); + sqlUndoLog.setAfterImage(null); + + TestUndoExecutor executor = new TestUndoExecutor(sqlUndoLog, true); + Object[] pkValues = executor.parsePkValues(beforeImage); + Assertions.assertEquals(2, pkValues.length); + } + + + private static TableMeta mockTableMeta() { + TableMeta tableMeta = Mockito.mock(TableMeta.class); + Mockito.when(tableMeta.getPkName()).thenReturn("ID"); + Mockito.when(tableMeta.getTableName()).thenReturn("table_name"); + ColumnMeta meta0 = Mockito.mock(ColumnMeta.class); + Mockito.when(meta0.getDataType()).thenReturn(Types.INTEGER); + Mockito.when(meta0.getColumnName()).thenReturn("ID"); + Mockito.when(tableMeta.getColumnMeta("ID")).thenReturn(meta0); + ColumnMeta meta1 = Mockito.mock(ColumnMeta.class); + Mockito.when(meta1.getDataType()).thenReturn(Types.VARCHAR); + Mockito.when(meta1.getColumnName()).thenReturn("NAME"); + Mockito.when(tableMeta.getColumnMeta("NAME")).thenReturn(meta1); + return tableMeta; + } + + private void execSQL(String sql) { + Statement s = null; + try { + s = connection.createStatement(); + s.execute(sql); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (s != null) { + try { + s.close(); + } catch (SQLException e) { + } + } + } + } + + private TableRecords execQuery(TableMeta tableMeta, String sql) throws SQLException { + Statement s = null; + ResultSet set = null; + try { + s = connection.createStatement(); + set = s.executeQuery(sql); + return TableRecords.buildRecords(tableMeta, set); + } finally { + if (set != null) { + try { + set.close(); + } catch (Exception e) { + } + } + if (s != null) { + try { + s.close(); + } catch (SQLException e) { + } + } + } + } +} + +class TestUndoExecutor extends AbstractUndoExecutor { + private boolean isDelete; + public TestUndoExecutor(SQLUndoLog sqlUndoLog, boolean isDelete) { + super(sqlUndoLog); + this.isDelete = isDelete; + } + + @Override + protected String buildUndoSQL() { + return null; + } + + @Override + protected TableRecords getUndoRows() { + return isDelete ? sqlUndoLog.getBeforeImage() : sqlUndoLog.getAfterImage(); + } +} diff --git a/rm-datasource/src/test/java/io/seata/rm/datasource/undo/UndoExecutorTest.java b/rm-datasource/src/test/java/io/seata/rm/datasource/undo/UndoExecutorTest.java index eb7a0324bb26c6ddf99164c6fd544ea82d4ca17a..ae1174c8c5c77255cdeed702ce64ef86939fb7b9 100644 --- a/rm-datasource/src/test/java/io/seata/rm/datasource/undo/UndoExecutorTest.java +++ b/rm-datasource/src/test/java/io/seata/rm/datasource/undo/UndoExecutorTest.java @@ -59,6 +59,7 @@ import io.seata.rm.datasource.sql.struct.TableMeta; import io.seata.rm.datasource.sql.struct.TableRecords; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; /** * The type Undo executor test. @@ -88,7 +89,7 @@ public class UndoExecutorTest { * Test update. */ @Test - public void testUpdate() { + public void testUpdate() throws SQLException { SQLUndoLog SQLUndoLog = new SQLUndoLog(); SQLUndoLog.setTableName("my_test_table"); SQLUndoLog.setSqlType(SQLType.UPDATE); @@ -147,19 +148,18 @@ public class UndoExecutorTest { SQLUndoLog.setAfterImage(afterImage); AbstractUndoExecutor executor = UndoExecutorFactory.getUndoExecutor(JdbcConstants.MYSQL, SQLUndoLog); - - try { - executor.executeOn(new MockConnection()); - } catch (SQLException e) { - e.printStackTrace(); - } + MockConnection connection = new MockConnection(); + AbstractUndoExecutor spy = Mockito.spy(executor); + // skip data validation + Mockito.doReturn(true).when(spy).dataValidationAndGoOn(connection); + spy.executeOn(connection); } /** * Test insert. */ @Test - public void testInsert() { + public void testInsert() throws SQLException { SQLUndoLog SQLUndoLog = new SQLUndoLog(); SQLUndoLog.setTableName("my_test_table"); SQLUndoLog.setSqlType(SQLType.INSERT); @@ -217,19 +217,18 @@ public class UndoExecutorTest { SQLUndoLog.setAfterImage(afterImage); AbstractUndoExecutor executor = UndoExecutorFactory.getUndoExecutor(JdbcConstants.MYSQL, SQLUndoLog); - - try { - executor.executeOn(new MockConnection()); - } catch (SQLException e) { - e.printStackTrace(); - } + MockConnection connection = new MockConnection(); + AbstractUndoExecutor spy = Mockito.spy(executor); + // skip data validation + Mockito.doReturn(true).when(spy).dataValidationAndGoOn(connection); + spy.executeOn(connection); } /** * Test delete. */ @Test - public void testDelete() { + public void testDelete() throws SQLException { SQLUndoLog SQLUndoLog = new SQLUndoLog(); SQLUndoLog.setTableName("my_test_table"); SQLUndoLog.setSqlType(SQLType.DELETE); @@ -287,12 +286,11 @@ public class UndoExecutorTest { SQLUndoLog.setBeforeImage(beforeImage); AbstractUndoExecutor executor = UndoExecutorFactory.getUndoExecutor(JdbcConstants.MYSQL, SQLUndoLog); - - try { - executor.executeOn(new MockConnection()); - } catch (SQLException e) { - e.printStackTrace(); - } + MockConnection connection = new MockConnection(); + AbstractUndoExecutor spy = Mockito.spy(executor); + // skip data validation + Mockito.doReturn(true).when(spy).dataValidationAndGoOn(connection); + spy.executeOn(connection); } /** diff --git a/rm-datasource/src/test/java/io/seata/rm/datasource/undo/UndoLogManagerTest.java b/rm-datasource/src/test/java/io/seata/rm/datasource/undo/UndoLogManagerTest.java index dcc8f5d4ddc3606f75d988fc9626f2aab8305ab9..e8fe5504dc9c67f710af88712bd032ca45d75dad 100644 --- a/rm-datasource/src/test/java/io/seata/rm/datasource/undo/UndoLogManagerTest.java +++ b/rm-datasource/src/test/java/io/seata/rm/datasource/undo/UndoLogManagerTest.java @@ -40,9 +40,10 @@ public class UndoLogManagerTest { private static final int APPEND_IN_SIZE = 10; - private static final int LIMIT_SIZE = 10; - private static final String THE_APPEND_IN_SIZE_PARAM_String = " (?,?,?,?,?,?,?,?,?,?) "; + private static final String THE_APPEND_IN_SIZE_PARAM_STRING = " (?,?,?,?,?,?,?,?,?,?) "; + + private static final String THE_DOUBLE_APPEND_IN_SIZE_PARAM_STRING = " (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) "; @Test public void testBatchDeleteUndoLog() throws Exception { @@ -57,7 +58,7 @@ public class UndoLogManagerTest { Connection connection = mock(Connection.class); PreparedStatement preparedStatement = mock(PreparedStatement.class); when(connection.prepareStatement(anyString())).thenReturn(preparedStatement); - UndoLogManager.batchDeleteUndoLog(xids, branchIds, LIMIT_SIZE, connection); + UndoLogManager.batchDeleteUndoLog(xids, branchIds, connection); //verify for (int i = 1;i <= APPEND_IN_SIZE;i++){ @@ -72,11 +73,10 @@ public class UndoLogManagerTest { @Test public void testToBatchDeleteUndoLogSql() { String expectedSqlString="DELETE FROM undo_log WHERE branch_id IN " + - THE_APPEND_IN_SIZE_PARAM_String + + THE_APPEND_IN_SIZE_PARAM_STRING + " AND xid IN " + - THE_APPEND_IN_SIZE_PARAM_String + - " LIMIT " + LIMIT_SIZE; - String batchDeleteUndoLogSql = UndoLogManager.toBatchDeleteUndoLogSql(APPEND_IN_SIZE, APPEND_IN_SIZE, LIMIT_SIZE); + THE_DOUBLE_APPEND_IN_SIZE_PARAM_STRING; + String batchDeleteUndoLogSql = UndoLogManager.toBatchDeleteUndoLogSql(APPEND_IN_SIZE * 2, APPEND_IN_SIZE); System.out.println(batchDeleteUndoLogSql); assertThat(batchDeleteUndoLogSql).isEqualTo(expectedSqlString); } @@ -85,7 +85,7 @@ public class UndoLogManagerTest { public void testAppendInParam() { StringBuilder sqlBuilder = new StringBuilder(); UndoLogManager.appendInParam(APPEND_IN_SIZE, sqlBuilder); - assertThat(sqlBuilder.toString()).isEqualTo(THE_APPEND_IN_SIZE_PARAM_String); + assertThat(sqlBuilder.toString()).isEqualTo(THE_APPEND_IN_SIZE_PARAM_STRING); } } diff --git a/rm-datasource/src/test/java/io/seata/rm/datasource/undo/mysql/keyword/MySQLKeywordCheckerTest.java b/rm-datasource/src/test/java/io/seata/rm/datasource/undo/mysql/keyword/MySQLKeywordCheckerTest.java index 879b77a88cdded38da225d3bf4418cd6c4042b5f..a3fd52afb1a3b2401660ba542b4b9c505e06a690 100644 --- a/rm-datasource/src/test/java/io/seata/rm/datasource/undo/mysql/keyword/MySQLKeywordCheckerTest.java +++ b/rm-datasource/src/test/java/io/seata/rm/datasource/undo/mysql/keyword/MySQLKeywordCheckerTest.java @@ -26,7 +26,6 @@ import io.seata.rm.datasource.sql.struct.TableRecords; import io.seata.rm.datasource.undo.KeywordChecker; import io.seata.rm.datasource.undo.KeywordCheckerFactory; import io.seata.rm.datasource.undo.SQLUndoLog; -import io.seata.rm.datasource.undo.UndoExecutorTest; import io.seata.rm.datasource.undo.mysql.MySQLUndoDeleteExecutor; import io.seata.rm.datasource.undo.mysql.MySQLUndoInsertExecutor; import io.seata.rm.datasource.undo.mysql.MySQLUndoUpdateExecutor; diff --git a/server/pom.xml b/server/pom.xml index 55290bec0ee9cdfd89784ff3d71097cf4bbac865..870f8187e7a02253f64d37acbc851a3df2f067a7 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -45,6 +45,24 @@ <version>${project.version}</version> </dependency> + <!-- for database --> + <dependency> + <groupId>com.alibaba</groupId> + <artifactId>druid</artifactId> + </dependency> + <dependency> + <groupId>commons-dbcp</groupId> + <artifactId>commons-dbcp</artifactId> + </dependency> + <dependency> + <groupId>com.h2database</groupId> + <artifactId>h2</artifactId> + </dependency> + <dependency> + <groupId>mysql</groupId> + <artifactId>mysql-connector-java</artifactId> + </dependency> + </dependencies> <build> diff --git a/server/src/main/java/io/seata/server/AbstractTCInboundHandler.java b/server/src/main/java/io/seata/server/AbstractTCInboundHandler.java index d6655196eeee371631db759df2e063a174c861c1..89fcbe94740f56d7a42802f87df788a6105c68c3 100644 --- a/server/src/main/java/io/seata/server/AbstractTCInboundHandler.java +++ b/server/src/main/java/io/seata/server/AbstractTCInboundHandler.java @@ -15,7 +15,6 @@ */ package io.seata.server; -import io.seata.common.XID; import io.seata.core.exception.AbstractExceptionHandler; import io.seata.core.exception.TransactionException; import io.seata.core.model.GlobalStatus; @@ -106,8 +105,7 @@ public abstract class AbstractTCInboundHandler extends AbstractExceptionHandler public void onTransactionException(GlobalRollbackRequest request, GlobalRollbackResponse response, TransactionException tex) { super.onTransactionException(request, response, tex); - GlobalSession globalSession = SessionHolder.findGlobalSession( - XID.getTransactionId(request.getXid())); + GlobalSession globalSession = SessionHolder.findGlobalSession(request.getXid()); if (globalSession != null) { response.setGlobalStatus(globalSession.getStatus()); } else { @@ -118,8 +116,7 @@ public abstract class AbstractTCInboundHandler extends AbstractExceptionHandler @Override public void onException(GlobalRollbackRequest request, GlobalRollbackResponse response, Exception rex) { super.onException(request, response, rex); - GlobalSession globalSession = SessionHolder.findGlobalSession( - XID.getTransactionId(request.getXid())); + GlobalSession globalSession = SessionHolder.findGlobalSession(request.getXid()); if (globalSession != null) { response.setGlobalStatus(globalSession.getStatus()); } else { diff --git a/server/src/main/java/io/seata/server/Server.java b/server/src/main/java/io/seata/server/Server.java index 92273a3e68fed718f276b95df19220e88994da68..0cec9d18b8713a6c006597b82ec0cd5db1c653b5 100644 --- a/server/src/main/java/io/seata/server/Server.java +++ b/server/src/main/java/io/seata/server/Server.java @@ -42,7 +42,7 @@ public class Server { private static final int SERVER_DEFAULT_PORT = 8091; private static final ThreadPoolExecutor WORKING_THREADS = new ThreadPoolExecutor(MIN_SERVER_POOL_SIZE, MAX_SERVER_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS, - new LinkedBlockingQueue(MAX_TASK_QUEUE_SIZE), + new LinkedBlockingQueue<>(MAX_TASK_QUEUE_SIZE), new NamedThreadFactory("ServerHandlerThread", MAX_SERVER_POOL_SIZE), new ThreadPoolExecutor.CallerRunsPolicy()); /** diff --git a/server/src/main/java/io/seata/server/coordinator/DefaultCoordinator.java b/server/src/main/java/io/seata/server/coordinator/DefaultCoordinator.java index 15063da2cb22238481f6596ce9a22523fe84d368..18c1e80395fa9604463f5e10c9056cc2fadb1252 100644 --- a/server/src/main/java/io/seata/server/coordinator/DefaultCoordinator.java +++ b/server/src/main/java/io/seata/server/coordinator/DefaultCoordinator.java @@ -16,13 +16,17 @@ package io.seata.server.coordinator; import java.io.IOException; +import java.time.Duration; import java.util.Collection; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import io.seata.common.XID; import io.seata.common.thread.NamedThreadFactory; +import io.seata.common.util.CollectionUtils; +import io.seata.core.constants.ConfigurationKeys; +import io.seata.common.util.DurationUtil; +import io.seata.config.ConfigurationFactory; import io.seata.core.exception.TransactionException; import io.seata.core.model.BranchStatus; import io.seata.core.model.BranchType; @@ -78,6 +82,44 @@ public class DefaultCoordinator extends AbstractTCInboundHandler private static final int TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS = 5000; + /** + * The Committing retry delay. + */ + protected int committingRetryDelay = CONFIG.getInt(ConfigurationKeys.COMMITING_RETRY_DELAY, 5); + + /** + * The Asyn committing retry delay. + */ + protected int asynCommittingRetryDelay = CONFIG.getInt(ConfigurationKeys.ASYN_COMMITING_RETRY_DELAY, 5); + + /** + * The Rollbacking retry delay. + */ + protected int rollbackingRetryDelay = CONFIG.getInt(ConfigurationKeys.ROLLBACKING_RETRY_DELAY, 5); + + /** + * The Timeout retry delay. + */ + protected int timeoutRetryDelay = CONFIG.getInt(ConfigurationKeys.TIMEOUT_RETRY_DELAY, 5); + + private static final int ALWAYS_RETRY_BOUNDARY = 0; + + private static final Duration MAX_COMMIT_RETRY_TIMEOUT = ConfigurationFactory.getInstance().getDuration(ConfigurationKeys.SERVICE_PREFIX + "max.commit.retry.timeout", DurationUtil.DEFAULT_DURATION, 100); + + private static final Duration MAX_ROLLBACK_RETRY_TIMEOUT = ConfigurationFactory.getInstance().getDuration(ConfigurationKeys.SERVICE_PREFIX + "max.rollback.retry.timeout", DurationUtil.DEFAULT_DURATION, 100); + + private ScheduledThreadPoolExecutor retryRollbacking = new ScheduledThreadPoolExecutor(1, + new NamedThreadFactory("RetryRollbacking", 1)); + + private ScheduledThreadPoolExecutor retryCommitting = new ScheduledThreadPoolExecutor(1, + new NamedThreadFactory("RetryCommitting", 1)); + + private ScheduledThreadPoolExecutor asyncCommitting = new ScheduledThreadPoolExecutor(1, + new NamedThreadFactory("AsyncCommitting", 1)); + + private ScheduledThreadPoolExecutor timeoutCheck = new ScheduledThreadPoolExecutor(1, + new NamedThreadFactory("TxTimeoutCheck", 1)); + private ServerMessageSender messageSender; private Core core = CoreFactory.get(); @@ -156,7 +198,7 @@ public class DefaultCoordinator extends AbstractTCInboundHandler request.setApplicationData(applicationData); request.setBranchType(branchType); - GlobalSession globalSession = SessionHolder.findGlobalSession(XID.getTransactionId(xid)); + GlobalSession globalSession = SessionHolder.findGlobalSession(xid); if(globalSession == null){ return BranchStatus.PhaseTwo_Committed; } @@ -165,9 +207,7 @@ public class DefaultCoordinator extends AbstractTCInboundHandler BranchCommitResponse response = (BranchCommitResponse)messageSender.sendSyncRequest(resourceId, branchSession.getClientId(), request); return response.getBranchStatus(); - } catch (IOException e) { - throw new TransactionException(FailedToSendBranchCommitRequest, branchId + "/" + xid, e); - } catch (TimeoutException e) { + } catch (IOException | TimeoutException e) { throw new TransactionException(FailedToSendBranchCommitRequest, branchId + "/" + xid, e); } } @@ -185,7 +225,7 @@ public class DefaultCoordinator extends AbstractTCInboundHandler request.setApplicationData(applicationData); request.setBranchType(branchType); - GlobalSession globalSession = SessionHolder.findGlobalSession(XID.getTransactionId(xid)); + GlobalSession globalSession = SessionHolder.findGlobalSession(xid); if(globalSession == null){ return BranchStatus.PhaseTwo_Rollbacked; } @@ -194,27 +234,34 @@ public class DefaultCoordinator extends AbstractTCInboundHandler BranchRollbackResponse response = (BranchRollbackResponse)messageSender.sendSyncRequest(resourceId, branchSession.getClientId(), request); return response.getBranchStatus(); - } catch (IOException e) { - throw new TransactionException(FailedToSendBranchRollbackRequest, branchId + "/" + xid, e); - } catch (TimeoutException e) { + } catch (IOException | TimeoutException e) { throw new TransactionException(FailedToSendBranchRollbackRequest, branchId + "/" + xid, e); } } - private void timeoutCheck() throws TransactionException { + /** + * Timeout check. + * + * @throws TransactionException the transaction exception + */ + protected void timeoutCheck() throws TransactionException { Collection<GlobalSession> allSessions = SessionHolder.getRootSessionManager().allSessions(); + if(CollectionUtils.isEmpty(allSessions)){ + return; + } if (allSessions.size() > 0 && LOGGER.isDebugEnabled()) { LOGGER.debug("Transaction Timeout Check Begin: " + allSessions.size()); } for (GlobalSession globalSession : allSessions) { if (LOGGER.isDebugEnabled()) { - LOGGER.debug(globalSession.getTransactionId() + " " + globalSession.getStatus() + " " + + LOGGER.debug(globalSession.getXid() + " " + globalSession.getStatus() + " " + globalSession.getBeginTime() + " " + globalSession.getTimeout()); } boolean shouldTimeout = globalSession.lockAndExcute(() -> { if (globalSession.getStatus() != GlobalStatus.Begin || !globalSession.isTimeout()) { return false; } + globalSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); globalSession.close(); globalSession.changeStatus(GlobalStatus.TimeoutRollbacking); return true; @@ -223,7 +270,7 @@ public class DefaultCoordinator extends AbstractTCInboundHandler continue; } LOGGER.info( - "Global transaction[" + globalSession.getTransactionId() + "] is timeout and will be rolled back."); + "Global transaction[" + globalSession.getXid() + "] is timeout and will be rolled back."); globalSession.addSessionLifecycleListener(SessionHolder.getRetryRollbackingSessionManager()); SessionHolder.getRetryRollbackingSessionManager().addGlobalSession(globalSession); @@ -235,107 +282,129 @@ public class DefaultCoordinator extends AbstractTCInboundHandler } - private void handleRetryRollbacking() { + /** + * Handle retry rollbacking. + */ + protected void handleRetryRollbacking() { Collection<GlobalSession> rollbackingSessions = SessionHolder.getRetryRollbackingSessionManager().allSessions(); + if(CollectionUtils.isEmpty(rollbackingSessions)){ + return; + } + long now = System.currentTimeMillis(); for (GlobalSession rollbackingSession : rollbackingSessions) { try { + if(isRetryTimeout(now, MAX_ROLLBACK_RETRY_TIMEOUT.toMillis(), rollbackingSession.getBeginTime())){ + /** + * Prevent thread safety issues + */ + SessionHolder.getRetryCommittingSessionManager().removeGlobalSession(rollbackingSession); + LOGGER.error("GlobalSession rollback retry timeout [{}]", rollbackingSession.getTransactionId()); + continue; + } + rollbackingSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); core.doGlobalRollback(rollbackingSession, true); } catch (TransactionException ex) { LOGGER.info("Failed to retry rollbacking [{}] {} {}", - rollbackingSession.getTransactionId(), ex.getCode(), ex.getMessage()); + rollbackingSession.getXid(), ex.getCode(), ex.getMessage()); } } } - private void handleRetryCommitting() { + /** + * Handle retry committing. + */ + protected void handleRetryCommitting() { Collection<GlobalSession> committingSessions = SessionHolder.getRetryCommittingSessionManager().allSessions(); + if(CollectionUtils.isEmpty(committingSessions)){ + return; + } + long now = System.currentTimeMillis(); for (GlobalSession committingSession : committingSessions) { try { + if(isRetryTimeout(now, MAX_COMMIT_RETRY_TIMEOUT.toMillis(), committingSession.getBeginTime())){ + /** + * Prevent thread safety issues + */ + SessionHolder.getRetryCommittingSessionManager().removeGlobalSession(committingSession); + LOGGER.error("GlobalSession commit retry timeout [{}]", committingSession.getTransactionId()); + continue; + } + committingSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); core.doGlobalCommit(committingSession, true); } catch (TransactionException ex) { LOGGER.info("Failed to retry committing [{}] {} {}", - committingSession.getTransactionId(), ex.getCode(), ex.getMessage()); + committingSession.getXid(), ex.getCode(), ex.getMessage()); } } } - private void handleAsyncCommitting() { + private boolean isRetryTimeout(long now, long timeout, long beginTime){ + /** + * Start timing when the session begin + */ + if(timeout >= ALWAYS_RETRY_BOUNDARY && + now - beginTime > timeout){ + return true; + } + return false; + } + + /** + * Handle async committing. + */ + protected void handleAsyncCommitting() { Collection<GlobalSession> asyncCommittingSessions = SessionHolder.getAsyncCommittingSessionManager() .allSessions(); + if(CollectionUtils.isEmpty(asyncCommittingSessions)){ + return; + } for (GlobalSession asyncCommittingSession : asyncCommittingSessions) { try { + asyncCommittingSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); core.doGlobalCommit(asyncCommittingSession, true); } catch (TransactionException ex) { LOGGER.info("Failed to async committing [{}] {} {}", - asyncCommittingSession.getTransactionId(), ex.getCode(), ex.getMessage()); + asyncCommittingSession.getXid(), ex.getCode(), ex.getMessage()); } } } - private ScheduledThreadPoolExecutor retryRollbacking = new ScheduledThreadPoolExecutor(1, - new NamedThreadFactory("RetryRollbacking", 1)); - - private ScheduledThreadPoolExecutor retryCommitting = new ScheduledThreadPoolExecutor(1, - new NamedThreadFactory("RetryCommitting", 1)); - - private ScheduledThreadPoolExecutor asyncCommitting = new ScheduledThreadPoolExecutor(1, - new NamedThreadFactory("AsyncCommitting", 1)); - - private ScheduledThreadPoolExecutor timeoutCheck = new ScheduledThreadPoolExecutor(1, - new NamedThreadFactory("TxTimeoutCheck", 1)); /** * Init. */ public void init() { - retryRollbacking.scheduleAtFixedRate(new Runnable() { - - @Override - public void run() { - try { - handleRetryRollbacking(); - } catch (Exception e) { - LOGGER.info("Exception retry rollbacking ... ", e); - } - + retryRollbacking.scheduleAtFixedRate(() -> { + try { + handleRetryRollbacking(); + } catch (Exception e) { + LOGGER.info("Exception retry rollbacking ... ", e); } - }, 0, 5, TimeUnit.MILLISECONDS); - - retryCommitting.scheduleAtFixedRate(new Runnable() { - @Override - public void run() { - try { - handleRetryCommitting(); - } catch (Exception e) { - LOGGER.info("Exception retry committing ... ", e); - } + }, 0, rollbackingRetryDelay, TimeUnit.SECONDS); + retryCommitting.scheduleAtFixedRate(() -> { + try { + handleRetryCommitting(); + } catch (Exception e) { + LOGGER.info("Exception retry committing ... ", e); } - }, 0, 5, TimeUnit.MILLISECONDS); - - asyncCommitting.scheduleAtFixedRate(new Runnable() { - @Override - public void run() { - try { - handleAsyncCommitting(); - } catch (Exception e) { - LOGGER.info("Exception async committing ... ", e); - } + }, 0, committingRetryDelay, TimeUnit.SECONDS); + asyncCommitting.scheduleAtFixedRate(() -> { + try { + handleAsyncCommitting(); + } catch (Exception e) { + LOGGER.info("Exception async committing ... ", e); } - }, 0, 10, TimeUnit.MILLISECONDS); - - timeoutCheck.scheduleAtFixedRate(new Runnable() { - @Override - public void run() { - try { - timeoutCheck(); - } catch (Exception e) { - LOGGER.info("Exception timeout checking ... ", e); - } + }, 0, asynCommittingRetryDelay, TimeUnit.SECONDS); + timeoutCheck.scheduleAtFixedRate(() -> { + try { + timeoutCheck(); + } catch (Exception e) { + LOGGER.info("Exception timeout checking ... ", e); } - }, 0, 2, TimeUnit.MILLISECONDS); + }, 0, timeoutRetryDelay, TimeUnit.SECONDS); } @Override @@ -369,14 +438,14 @@ public class DefaultCoordinator extends AbstractTCInboundHandler retryCommitting.awaitTermination(TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS, TimeUnit.MILLISECONDS); asyncCommitting.awaitTermination(TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS, TimeUnit.MILLISECONDS); timeoutCheck.awaitTermination(TIMED_TASK_SHUTDOWN_MAX_WAIT_MILLS, TimeUnit.MILLISECONDS); - } catch (InterruptedException ingore) { + } catch (InterruptedException ignore) { } - // 2. sencond close netty flow + // 2. second close netty flow if (messageSender instanceof RpcServer){ ((RpcServer) messageSender).destroy(); } - // 3. last destory SessionHolder + // 3. last destroy SessionHolder SessionHolder.destory(); } } diff --git a/server/src/main/java/io/seata/server/coordinator/DefaultCore.java b/server/src/main/java/io/seata/server/coordinator/DefaultCore.java index 1ba36763f1f056f21eacd7bb8a65b7ba471f96ad..cd10d4281badee62d4a4df6fdd45d443554ca383 100644 --- a/server/src/main/java/io/seata/server/coordinator/DefaultCore.java +++ b/server/src/main/java/io/seata/server/coordinator/DefaultCore.java @@ -23,7 +23,7 @@ import io.seata.core.model.BranchType; import io.seata.core.model.GlobalStatus; import io.seata.core.model.ResourceManagerInbound; import io.seata.server.lock.LockManager; -import io.seata.server.lock.LockManagerFactory; +import io.seata.server.lock.LockerFactory; import io.seata.server.session.BranchSession; import io.seata.server.session.GlobalSession; import io.seata.server.session.SessionHelper; @@ -46,7 +46,7 @@ public class DefaultCore implements Core { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultCore.class); - private LockManager lockManager = LockManagerFactory.get(); + private LockManager lockManager = LockerFactory.getLockManager(); private ResourceManagerInbound resourceManagerInbound; @@ -58,7 +58,7 @@ public class DefaultCore implements Core { @Override public Long branchRegister(BranchType branchType, String resourceId, String clientId, String xid, String applicationData, String lockKeys) throws TransactionException { - GlobalSession globalSession = assertGlobalSessionNotNull(XID.getTransactionId(xid)); + GlobalSession globalSession = assertGlobalSessionNotNull(xid); return globalSession.lockAndExcute(() -> { if (!globalSession.isActive()) { throw new TransactionException(GlobalTransactionNotActive, "Current Status: " + globalSession.getStatus()); @@ -67,6 +67,7 @@ public class DefaultCore implements Core { throw new TransactionException(GlobalTransactionStatusInvalid, globalSession.getStatus() + " while expecting " + GlobalStatus.Begin); } + globalSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); BranchSession branchSession = SessionHelper.newBranchByGlobal(globalSession, branchType, resourceId, applicationData, lockKeys, clientId); if (!branchSession.lock()) { @@ -81,10 +82,10 @@ public class DefaultCore implements Core { }); } - private GlobalSession assertGlobalSessionNotNull(long transactionId) throws TransactionException { - GlobalSession globalSession = SessionHolder.findGlobalSession(transactionId); + private GlobalSession assertGlobalSessionNotNull(String xid) throws TransactionException { + GlobalSession globalSession = SessionHolder.findGlobalSession(xid); if (globalSession == null) { - throw new TransactionException(TransactionExceptionCode.GlobalTransactionNotExist, "" + transactionId + ""); + throw new TransactionException(TransactionExceptionCode.GlobalTransactionNotExist, "" + xid + ""); } return globalSession; } @@ -93,11 +94,12 @@ public class DefaultCore implements Core { @Override public void branchReport(BranchType branchType, String xid, long branchId, BranchStatus status, String applicationData) throws TransactionException { - GlobalSession globalSession = assertGlobalSessionNotNull(XID.getTransactionId(xid)); + GlobalSession globalSession = assertGlobalSessionNotNull(xid); BranchSession branchSession = globalSession.getBranch(branchId); if (branchSession == null) { throw new TransactionException(BranchTransactionNotExist); } + globalSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); globalSession.changeBranchStatus(branchSession, status); } @@ -105,7 +107,7 @@ public class DefaultCore implements Core { public boolean lockQuery(BranchType branchType, String resourceId, String xid, String lockKeys) throws TransactionException { if (branchType == BranchType.AT) { - return lockManager.isLockable(XID.getTransactionId(xid), resourceId, lockKeys); + return lockManager.isLockable(xid, resourceId, lockKeys); } else { return true; } @@ -121,17 +123,19 @@ public class DefaultCore implements Core { session.begin(); - return XID.generateXID(session.getTransactionId()); + return session.getXid(); } @Override public GlobalStatus commit(String xid) throws TransactionException { - GlobalSession globalSession = SessionHolder.findGlobalSession(XID.getTransactionId(xid)); + GlobalSession globalSession = SessionHolder.findGlobalSession(xid); if (globalSession == null) { return GlobalStatus.Finished; } + globalSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); // just lock changeStatus boolean shouldCommit = globalSession.lockAndExcute(() -> { + //the lock should release after branch commit globalSession.closeAndClean(); // Highlight: Firstly, close the session, then no more branch can be registered. if (globalSession.getStatus() == GlobalStatus.Begin) { globalSession.changeStatus(GlobalStatus.Committing); @@ -175,7 +179,7 @@ public class DefaultCore implements Core { } else { SessionHelper.endCommitFailed(globalSession); LOGGER.error("Finally, failed to commit global[{}] since branch[{}] commit failed", - globalSession.getTransactionId(), branchSession.getBranchId()); + globalSession.getXid(), branchSession.getBranchId()); return; } default: @@ -189,7 +193,7 @@ public class DefaultCore implements Core { } else { LOGGER.error( "Failed to commit global[{}] since branch[{}] commit failed, will retry later.", - globalSession.getTransactionId(), branchSession.getBranchId()); + globalSession.getXid(), branchSession.getBranchId()); return; } @@ -206,11 +210,11 @@ public class DefaultCore implements Core { } if (globalSession.hasBranch()) { - LOGGER.info("Global[{}] committing is NOT done.", globalSession.getTransactionId()); + LOGGER.info("Global[{}] committing is NOT done.", globalSession.getXid()); return; } SessionHelper.endCommitted(globalSession); - LOGGER.info("Global[{}] committing is successfully done.", globalSession.getTransactionId()); + LOGGER.info("Global[{}] committing is successfully done.", globalSession.getXid()); } private void asyncCommit(GlobalSession globalSession) throws TransactionException { @@ -238,10 +242,11 @@ public class DefaultCore implements Core { @Override public GlobalStatus rollback(String xid) throws TransactionException { - GlobalSession globalSession = SessionHolder.findGlobalSession(XID.getTransactionId(xid)); + GlobalSession globalSession = SessionHolder.findGlobalSession(xid); if (globalSession == null) { return GlobalStatus.Finished; } + globalSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); // just lock changeStatus boolean shouldRollBack = globalSession.lockAndExcute(() -> { globalSession.close(); // Highlight: Firstly, close the session, then no more branch can be registered. @@ -279,7 +284,7 @@ public class DefaultCore implements Core { continue; case PhaseTwo_RollbackFailed_Unretryable: SessionHelper.endRollbackFailed(globalSession); - LOGGER.error("Failed to rollback global[" + globalSession.getTransactionId() + "] since branch[" + LOGGER.error("Failed to rollback global[" + globalSession.getXid() + "] since branch[" + branchSession.getBranchId() + "] rollback failed"); return; default: @@ -304,7 +309,7 @@ public class DefaultCore implements Core { @Override public GlobalStatus getStatus(String xid) throws TransactionException { - GlobalSession globalSession = SessionHolder.findGlobalSession(XID.getTransactionId(xid)); + GlobalSession globalSession = SessionHolder.findGlobalSession(xid); return globalSession.getStatus(); } } diff --git a/server/src/main/java/io/seata/server/lock/AbstractLockManager.java b/server/src/main/java/io/seata/server/lock/AbstractLockManager.java new file mode 100644 index 0000000000000000000000000000000000000000..a8db3a54d9214c10a7e4eb5638532003f25fa717 --- /dev/null +++ b/server/src/main/java/io/seata/server/lock/AbstractLockManager.java @@ -0,0 +1,117 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.lock; + +import io.seata.common.XID; +import io.seata.common.util.StringUtils; +import io.seata.core.lock.RowLock; +import io.seata.server.session.BranchSession; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +/** + * The type Abstract lock manager. + * + * @author zhangsen + * @data 2019 /4/25 + */ +public abstract class AbstractLockManager implements LockManager { + + /** + * The constant LOGGER. + */ + protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractLockManager.class); + + /** + * Collect row locks list.` + * + * @param branchSession the branch session + * @return the list + */ + protected List<RowLock> collectRowLocks(BranchSession branchSession){ + List<RowLock> locks = new ArrayList<>(); + if(branchSession == null || StringUtils.isBlank(branchSession.getLockKey())){ + return locks; + } + String xid = branchSession.getXid(); + String resourceId = branchSession.getResourceId(); + long transactionId = branchSession.getTransactionId(); + + String lockKey = branchSession.getLockKey(); + + return collectRowLocks(lockKey, resourceId, xid, transactionId, branchSession.getBranchId()); + } + + /** + * Collect row locks list. + * + * @param lockKey the lock key + * @param resourceId the resource id + * @param xid the xid + * @return the list + */ + protected List<RowLock> collectRowLocks(String lockKey, String resourceId, String xid) { + return collectRowLocks(lockKey, resourceId, xid, XID.getTransactionId(xid), null); + } + + /** + * Collect row locks list. + * + * @param lockKey the lock key + * @param resourceId the resource id + * @param xid the xid + * @param transactionId the transaction id + * @param branchID the branch id + * @return the list + */ + protected List<RowLock> collectRowLocks(String lockKey, String resourceId, String xid, Long transactionId, Long branchID){ + List<RowLock> locks = new ArrayList<RowLock>(); + + String[] tableGroupedLockKeys = lockKey.split(";"); + for (String tableGroupedLockKey : tableGroupedLockKeys) { + int idx = tableGroupedLockKey.indexOf(":"); + if (idx < 0) { + return locks; + } + String tableName = tableGroupedLockKey.substring(0, idx); + String mergedPKs = tableGroupedLockKey.substring(idx + 1); + if(StringUtils.isBlank(mergedPKs)){ + return locks; + } + String[] pks = mergedPKs.split(","); + if(pks == null || pks.length == 0){ + return locks; + } + for(String pk : pks){ + if(StringUtils.isNotBlank(pk)){ + RowLock rowLock = new RowLock(); + rowLock.setXid(xid); + rowLock.setTransactionId(transactionId); + rowLock.setBranchId(branchID); + rowLock.setTableName(tableName); + rowLock.setPk(pk); + rowLock.setResourceId(resourceId); + locks.add(rowLock); + } + } + } + return locks; + } + +} diff --git a/server/src/main/java/io/seata/server/lock/DefaultLockManager.java b/server/src/main/java/io/seata/server/lock/DefaultLockManager.java new file mode 100644 index 0000000000000000000000000000000000000000..dd66e979987d4c4ee7d5bf900b5c94862b966230 --- /dev/null +++ b/server/src/main/java/io/seata/server/lock/DefaultLockManager.java @@ -0,0 +1,103 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.lock; + +import io.seata.common.util.CollectionUtils; +import io.seata.common.util.StringUtils; +import io.seata.core.exception.TransactionException; +import io.seata.core.lock.Locker; +import io.seata.core.lock.RowLock; +import io.seata.server.session.BranchSession; + +import java.util.List; + +/** + * The type Default lock manager. + * + * @author zhangsen + * @data 2019 -05-15 + */ +public class DefaultLockManager extends AbstractLockManager { + + private static Locker locker = null; + + @Override + public boolean acquireLock(BranchSession branchSession) throws TransactionException { + String lockKey = branchSession.getLockKey(); + if (StringUtils.isNullOrEmpty(lockKey)) { + //no lock + return true; + } + //get locks of branch + List<RowLock> locks = collectRowLocks(branchSession); + if(CollectionUtils.isEmpty(locks)){ + //no lock + return true; + } + return getLocker(branchSession).acquireLock(locks); + } + + @Override + public boolean releaseLock(BranchSession branchSession) throws TransactionException { + List<RowLock> locks = collectRowLocks(branchSession); + if(CollectionUtils.isEmpty(locks)){ + //no lock + return true; + } + try{ + return getLocker(branchSession).releaseLock(locks); + }catch(Exception t){ + LOGGER.error("unLock error, branchSession:" + branchSession, t); + return false; + } + } + + @Override + public boolean isLockable(String xid, String resourceId, String lockKey) throws TransactionException { + List<RowLock> locks = collectRowLocks(lockKey, resourceId, xid); + try{ + return getLocker().isLockable(locks); + }catch(Exception t){ + LOGGER.error("isLockable error, xid:" + xid + ", resourceId:"+resourceId + ", lockKey:"+lockKey, t); + return false; + } + } + + @Override + public void cleanAllLocks() throws TransactionException { + getLocker().cleanAllLocks(); + } + + /** + * Gets locker. + * + * @return the locker + */ + protected Locker getLocker() { + return getLocker(null); + } + + /** + * Gets locker. + * + * @param branchSession the branch session + * @return the locker + */ + protected Locker getLocker(BranchSession branchSession) { + return LockerFactory.get(branchSession); + } + +} diff --git a/server/src/main/java/io/seata/server/lock/DefaultLockManagerImpl.java b/server/src/main/java/io/seata/server/lock/DefaultLockManagerImpl.java deleted file mode 100644 index 14a68549987fbf913437cdef1265b84d1239113c..0000000000000000000000000000000000000000 --- a/server/src/main/java/io/seata/server/lock/DefaultLockManagerImpl.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright 1999-2019 Seata.io Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.seata.server.lock; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -import io.netty.util.internal.ConcurrentSet; -import io.seata.common.exception.ShouldNeverHappenException; -import io.seata.common.util.StringUtils; -import io.seata.core.exception.TransactionException; -import io.seata.server.session.BranchSession; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * The type Default lock manager. - * - * @author sharajava - */ -public class DefaultLockManagerImpl implements LockManager { - - private static final Logger LOGGER = LoggerFactory.getLogger(DefaultLockManagerImpl.class); - - private static final int BUCKET_PER_TABLE = 128; - - private static final - ConcurrentHashMap<String/* resourceId */, - ConcurrentHashMap<String/* tableName */, - ConcurrentHashMap<Integer/* bucketId */, - Map<String/* pk */, Long/* transactionId */>>>> - LOCK_MAP = new ConcurrentHashMap<String, ConcurrentHashMap<String, ConcurrentHashMap<Integer, Map<String, Long>>>>(); - - @Override - public boolean acquireLock(BranchSession branchSession) throws TransactionException { - String resourceId = branchSession.getResourceId(); - long transactionId = branchSession.getTransactionId(); - ConcurrentHashMap<String, ConcurrentHashMap<Integer, Map<String, Long>>> dbLockMap = LOCK_MAP.get(resourceId); - if (dbLockMap == null) { - LOCK_MAP.putIfAbsent(resourceId, - new ConcurrentHashMap<String, ConcurrentHashMap<Integer, Map<String, Long>>>()); - dbLockMap = LOCK_MAP.get(resourceId); - } - ConcurrentHashMap<Map<String, Long>, Set<String>> bucketHolder = branchSession.getLockHolder(); - - String lockKey = branchSession.getLockKey(); - if (StringUtils.isNullOrEmpty(lockKey)) { - return true; - } - - String[] tableGroupedLockKeys = lockKey.split(";"); - for (String tableGroupedLockKey : tableGroupedLockKeys) { - int idx = tableGroupedLockKey.indexOf(":"); - if (idx < 0) { - branchSession.unlock(); - throw new ShouldNeverHappenException("Wrong format of LOCK KEYS: " + branchSession.getLockKey()); - } - String tableName = tableGroupedLockKey.substring(0, idx); - String mergedPKs = tableGroupedLockKey.substring(idx + 1); - ConcurrentHashMap<Integer, Map<String, Long>> tableLockMap = dbLockMap.get(tableName); - if (tableLockMap == null) { - dbLockMap.putIfAbsent(tableName, new ConcurrentHashMap<Integer, Map<String, Long>>()); - tableLockMap = dbLockMap.get(tableName); - } - String[] pks = mergedPKs.split(","); - for (String pk : pks) { - int bucketId = pk.hashCode() % BUCKET_PER_TABLE; - Map<String, Long> bucketLockMap = tableLockMap.get(bucketId); - if (bucketLockMap == null) { - tableLockMap.putIfAbsent(bucketId, new HashMap<String, Long>()); - bucketLockMap = tableLockMap.get(bucketId); - } - synchronized (bucketLockMap) { - Long lockingTransactionId = bucketLockMap.get(pk); - if (lockingTransactionId == null) { - // No existing lock - bucketLockMap.put(pk, transactionId); - Set<String> keysInHolder = bucketHolder.get(bucketLockMap); - if (keysInHolder == null) { - bucketHolder.putIfAbsent(bucketLockMap, new ConcurrentSet<String>()); - keysInHolder = bucketHolder.get(bucketLockMap); - } - keysInHolder.add(pk); - - } else if (lockingTransactionId.longValue() == transactionId) { - // Locked by me - continue; - } else { - LOGGER.info( - "Global lock on [" + tableName + ":" + pk + "] is holding by " + lockingTransactionId); - branchSession.unlock(); // Release all acquired locks. - return false; - } - } - } - } - return true; - } - - @Override - public boolean isLockable(long transactionId, String resourceId, String lockKey) throws TransactionException { - ConcurrentHashMap<String, ConcurrentHashMap<Integer, Map<String, Long>>> dbLockMap = LOCK_MAP.get(resourceId); - if (dbLockMap == null) { - return true; - } - String[] tableGroupedLockKeys = lockKey.split(";"); - for (String tableGroupedLockKey : tableGroupedLockKeys) { - int idx = tableGroupedLockKey.indexOf(":"); - if (idx < 0) { - throw new ShouldNeverHappenException("Wrong format of LOCK KEYS: " + lockKey); - } - String tableName = tableGroupedLockKey.substring(0, idx); - String mergedPKs = tableGroupedLockKey.substring(idx + 1); - ConcurrentHashMap<Integer, Map<String, Long>> tableLockMap = dbLockMap.get(tableName); - if (tableLockMap == null) { - continue; - } - String[] pks = mergedPKs.split(","); - for (String pk : pks) { - int bucketId = pk.hashCode() % BUCKET_PER_TABLE; - Map<String, Long> bucketLockMap = tableLockMap.get(bucketId); - if (bucketLockMap == null) { - continue; - } - Long lockingTransactionId = bucketLockMap.get(pk); - if (lockingTransactionId == null || lockingTransactionId.longValue() == transactionId) { - // Locked by me - continue; - } else { - LOGGER.info("Global lock on [" + tableName + ":" + pk + "] is holding by " + lockingTransactionId); - return false; - } - } - } - return true; - } - - @Override - public void cleanAllLocks() throws TransactionException { - LOCK_MAP.clear(); - } - - @Override - public boolean releaseLock(BranchSession branchSession) throws TransactionException { - ConcurrentHashMap<Map<String, Long>, Set<String>> lockHolder = branchSession.getLockHolder(); - if (lockHolder.size() == 0) { - return true; - } - Iterator<Entry<Map<String, Long>, Set<String>>> it = lockHolder.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry<Map<String, Long>, Set<String>> entry = it.next(); - Map<String, Long> bucket = entry.getKey(); - Set<String> keys = entry.getValue(); - synchronized (bucket) { - for (String key : keys) { - Long v = bucket.get(key); - if (v == null) { - continue; - } - if (v.longValue() == branchSession.getTransactionId()) { - bucket.remove(key); - } - } - } - } - lockHolder.clear(); - return true; - } -} diff --git a/server/src/main/java/io/seata/server/lock/LockManager.java b/server/src/main/java/io/seata/server/lock/LockManager.java index 54bcef3c0b4e6c0edb376e128dabc64dec3192b5..64e7bd7317e9069f39b5f7c706370dff43e58fa1 100644 --- a/server/src/main/java/io/seata/server/lock/LockManager.java +++ b/server/src/main/java/io/seata/server/lock/LockManager.java @@ -35,29 +35,30 @@ public interface LockManager { boolean acquireLock(BranchSession branchSession) throws TransactionException; /** - * Is lockable boolean. + * Un lock boolean. * - * @param transactionId the transaction id - * @param resourceId the resource id - * @param lockKey the lock key + * @param branchSession the branch session * @return the boolean * @throws TransactionException the transaction exception */ - boolean isLockable(long transactionId, String resourceId, String lockKey) throws TransactionException; + boolean releaseLock(BranchSession branchSession) throws TransactionException; /** - * Clean all locks. + * Is lockable boolean. * + * @param xid the xid + * @param resourceId the resource id + * @param lockKey the lock key + * @return the boolean * @throws TransactionException the transaction exception */ - void cleanAllLocks() throws TransactionException; + boolean isLockable(String xid, String resourceId, String lockKey) throws TransactionException; /** - * Release lock boolean. + * Clean all locks. * - * @param branchSession the branch session - * @return the boolean * @throws TransactionException the transaction exception */ - boolean releaseLock(BranchSession branchSession) throws TransactionException; + void cleanAllLocks() throws TransactionException; + } diff --git a/server/src/main/java/io/seata/server/lock/LockerFactory.java b/server/src/main/java/io/seata/server/lock/LockerFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..6a402f0a447ffa4a311b5584d33b3b5dc0523291 --- /dev/null +++ b/server/src/main/java/io/seata/server/lock/LockerFactory.java @@ -0,0 +1,99 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.lock; + +import io.seata.common.loader.EnhancedServiceLoader; +import io.seata.config.Configuration; +import io.seata.config.ConfigurationFactory; +import io.seata.core.constants.ConfigurationKeys; +import io.seata.core.lock.LockMode; +import io.seata.core.lock.Locker; +import io.seata.core.store.db.DataSourceGenerator; +import io.seata.server.session.BranchSession; + +import javax.sql.DataSource; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * The type Lock manager factory. + * + * @author sharajava + */ +public class LockerFactory { + + /** + * The constant CONFIG. + */ + protected static final Configuration CONFIG = ConfigurationFactory.getInstance(); + + /** + * The constant locker. + */ + protected static Locker locker = null; + + /** + * The constant lockerMap. + */ + protected static Map<String, Locker> lockerMap = new ConcurrentHashMap<>(); + + /** + * The constant lockManager. + */ + protected static LockManager lockManager = new DefaultLockManager(); + + /** + * Get lock manager. + * + * @return the lock manager + */ + public static synchronized final LockManager getLockManager() { + return lockManager; + } + + /** + * Get lock manager. + * + * @param branchSession the branch session + * @return the lock manager + */ + public static synchronized final Locker get(BranchSession branchSession) { + String lockMode = CONFIG.getConfig(ConfigurationKeys.LOCK_MODE); + if(LockMode.DB.name().equalsIgnoreCase(lockMode)){ + if(lockerMap.get(lockMode) != null){ + return lockerMap.get(lockMode); + } + //init dataSource + String datasourceType = CONFIG.getConfig(ConfigurationKeys.STORE_DB_DATASOURCE_TYPE); + DataSourceGenerator dataSourceGenerator = EnhancedServiceLoader.load(DataSourceGenerator.class, datasourceType); + DataSource logStoreDataSource = dataSourceGenerator.generateDataSource(); + locker = EnhancedServiceLoader.load(Locker.class, lockMode, new Class[]{DataSource.class}, new Object[]{logStoreDataSource}); + lockerMap.put(lockMode, locker); + }else if(LockMode.MEMORY.name().equalsIgnoreCase(lockMode)){ + if(branchSession == null){ + throw new IllegalArgumentException("branchSession can be null for memory lockMode."); + } + locker = EnhancedServiceLoader.load(Locker.class, lockMode, + new Class[]{BranchSession.class}, new Object[]{branchSession} ); + }else { + //other locker + locker = EnhancedServiceLoader.load(Locker.class, lockMode); + } + return locker; + } + + +} diff --git a/server/src/main/java/io/seata/server/lock/db/DataBaseLocker.java b/server/src/main/java/io/seata/server/lock/db/DataBaseLocker.java new file mode 100644 index 0000000000000000000000000000000000000000..5721727fdc8478ce3f0067ea7946cedf3240971e --- /dev/null +++ b/server/src/main/java/io/seata/server/lock/db/DataBaseLocker.java @@ -0,0 +1,101 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.lock.db; + +import io.seata.common.loader.EnhancedServiceLoader; +import io.seata.common.loader.LoadLevel; +import io.seata.common.util.CollectionUtils; +import io.seata.core.lock.AbstractLocker; +import io.seata.core.lock.LockMode; +import io.seata.core.lock.RowLock; +import io.seata.core.store.LockStore; + +import javax.sql.DataSource; +import java.util.List; + +/** + * The type Data base locker. + * + * @author zhangsen + * @data 2019 -05-15 + */ +@LoadLevel(name = "db") +public class DataBaseLocker extends AbstractLocker { + + private LockStore lockStore; + + /** + * Instantiates a new Data base locker. + */ + public DataBaseLocker(){ + } + + /** + * Instantiates a new Data base locker. + * + * @param logStoreDataSource the log store data source + */ + public DataBaseLocker(DataSource logStoreDataSource){ + lockStore = EnhancedServiceLoader.load(LockStore.class, LockMode.DB.name(), new Class[]{DataSource.class}, new Object[]{logStoreDataSource}); + } + + @Override + public boolean acquireLock(List<RowLock> locks) { + if(CollectionUtils.isEmpty(locks)){ + //no lock + return true; + } + try{ + return lockStore.acquireLock(convertToLockDO(locks)); + }catch(Exception t){ + LOGGER.error("AcquireLock error, locks:" + CollectionUtils.toString(locks), t); + return false; + } + } + + @Override + public boolean releaseLock(List<RowLock> locks) { + if(CollectionUtils.isEmpty(locks)){ + //no lock + return true; + } + try{ + return lockStore.unLock(convertToLockDO(locks)); + }catch(Exception t){ + LOGGER.error("unLock error, locks:" + CollectionUtils.toString(locks), t); + return false; + } + } + + @Override + public boolean isLockable(List<RowLock> locks) { + try{ + return lockStore.isLockable(convertToLockDO(locks)); + }catch(Exception t){ + LOGGER.error("isLockable error, locks:" + CollectionUtils.toString(locks), t); + return false; + } + } + + /** + * Sets lock store. + * + * @param lockStore the lock store + */ + public void setLockStore(LockStore lockStore) { + this.lockStore = lockStore; + } +} diff --git a/server/src/main/java/io/seata/server/lock/memory/MemoryLocker.java b/server/src/main/java/io/seata/server/lock/memory/MemoryLocker.java new file mode 100644 index 0000000000000000000000000000000000000000..f4dbb9423e63a02dfb9dd86c0e3386ce34116012 --- /dev/null +++ b/server/src/main/java/io/seata/server/lock/memory/MemoryLocker.java @@ -0,0 +1,193 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.lock.memory; + +import io.netty.util.internal.ConcurrentSet; +import io.seata.common.exception.FrameworkException; +import io.seata.common.loader.LoadLevel; +import io.seata.common.util.CollectionUtils; +import io.seata.core.exception.TransactionException; +import io.seata.core.lock.AbstractLocker; +import io.seata.core.lock.RowLock; +import io.seata.server.session.BranchSession; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * The type Memory locker. + * + * @author zhangsen + * @data 2019 -05-15 + */ +@LoadLevel(name="memory") +public class MemoryLocker extends AbstractLocker { + + private static final int BUCKET_PER_TABLE = 128; + + private static final ConcurrentHashMap<String/* resourceId */, + ConcurrentHashMap<String/* tableName */, + ConcurrentHashMap<Integer/* bucketId */, + Map<String/* pk */, Long/* transactionId */>>>> + LOCK_MAP = new ConcurrentHashMap<String, ConcurrentHashMap<String, ConcurrentHashMap<Integer, Map<String, Long>>>>(); + + /** + * The Branch session. + */ + protected BranchSession branchSession = null; + + /** + * Instantiates a new Memory locker. + * + * @param branchSession the branch session + */ + public MemoryLocker(BranchSession branchSession){ + this.branchSession = branchSession; + } + + @Override + public boolean acquireLock(List<RowLock> rowLocks) { + if(CollectionUtils.isEmpty(rowLocks)){ + //no lock + return true; + } + String resourceId = branchSession.getResourceId(); + long transactionId = branchSession.getTransactionId(); + + ConcurrentHashMap<Map<String, Long>, Set<String>> bucketHolder = branchSession.getLockHolder(); + ConcurrentHashMap<String, ConcurrentHashMap<Integer, Map<String, Long>>> dbLockMap = LOCK_MAP.get(resourceId); + if (dbLockMap == null) { + LOCK_MAP.putIfAbsent(resourceId, new ConcurrentHashMap<String, ConcurrentHashMap<Integer, Map<String, Long>>>()); + dbLockMap = LOCK_MAP.get(resourceId); + } + + for(RowLock lock : rowLocks){ + String tableName = lock.getTableName(); + String pk = lock.getPk(); + ConcurrentHashMap<Integer, Map<String, Long>> tableLockMap = dbLockMap.get(tableName); + if (tableLockMap == null) { + dbLockMap.putIfAbsent(tableName, new ConcurrentHashMap<Integer, Map<String, Long>>()); + tableLockMap = dbLockMap.get(tableName); + } + int bucketId = pk.hashCode() % BUCKET_PER_TABLE; + Map<String, Long> bucketLockMap = tableLockMap.get(bucketId); + if (bucketLockMap == null) { + tableLockMap.putIfAbsent(bucketId, new ConcurrentHashMap<String, Long>()); + bucketLockMap = tableLockMap.get(bucketId); + } + synchronized (bucketLockMap) { + Long lockingTransactionId = bucketLockMap.get(pk); + if (lockingTransactionId == null) { + //No existing lock + bucketLockMap.put(pk, transactionId); + Set<String> keysInHolder = bucketHolder.get(bucketLockMap); + if (keysInHolder == null) { + bucketHolder.putIfAbsent(bucketLockMap, new ConcurrentSet<String>()); + keysInHolder = bucketHolder.get(bucketLockMap); + } + keysInHolder.add(pk); + + } else if (lockingTransactionId.longValue() == transactionId) { + // Locked by me + continue; + } else { + LOGGER.info("Global lock on [" + tableName + ":" + pk + "] is holding by " + lockingTransactionId); + try { + // Release all acquired locks. + branchSession.unlock(); + } catch (TransactionException e) { + throw new FrameworkException(e); + } + return false; + } + } + } + return true; + } + + @Override + public boolean releaseLock(List<RowLock> rowLock) { + ConcurrentHashMap<Map<String, Long>, Set<String>> lockHolder = branchSession.getLockHolder(); + if (lockHolder == null || lockHolder.size() == 0) { + return true; + } + Iterator<Map.Entry<Map<String, Long>, Set<String>>> it = lockHolder.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry<Map<String, Long>, Set<String>> entry = it.next(); + Map<String, Long> bucket = entry.getKey(); + Set<String> keys = entry.getValue(); + synchronized (bucket) { + for (String key : keys) { + Long v = bucket.get(key); + if (v == null) { + continue; + } + if (v.longValue() == branchSession.getTransactionId()) { + bucket.remove(key); + } + } + } + } + lockHolder.clear(); + return true; + } + + @Override + public boolean isLockable(List<RowLock> rowLocks) { + if(CollectionUtils.isEmpty(rowLocks)){ + //no lock + return true; + } + Long transactionId = rowLocks.get(0).getTransactionId(); + String resourceId = rowLocks.get(0).getResourceId(); + ConcurrentHashMap<String, ConcurrentHashMap<Integer, Map<String, Long>>> dbLockMap = LOCK_MAP.get(resourceId); + if (dbLockMap == null) { + return true; + } + for(RowLock rowLock : rowLocks){ + String xid = rowLock.getXid(); + String tableName = rowLock.getTableName(); + String pk = rowLock.getPk(); + + ConcurrentHashMap<Integer, Map<String, Long>> tableLockMap = dbLockMap.get(tableName); + if (tableLockMap == null) { + continue; + } + int bucketId = pk.hashCode() % BUCKET_PER_TABLE; + Map<String, Long> bucketLockMap = tableLockMap.get(bucketId); + if (bucketLockMap == null) { + continue; + } + Long lockingTransactionId = bucketLockMap.get(pk); + if (lockingTransactionId == null || lockingTransactionId.longValue() == transactionId) { + // Locked by me + continue; + } else { + LOGGER.info("Global lock on [" + tableName + ":" + pk + "] is holding by " + lockingTransactionId); + return false; + } + } + return true; + } + + @Override + public void cleanAllLocks() { + LOCK_MAP.clear(); + } +} diff --git a/server/src/main/java/io/seata/server/session/AbstractSessionManager.java b/server/src/main/java/io/seata/server/session/AbstractSessionManager.java index d58f8b71d5a55f17f5031bac96fd2f03f0727643..8cce5790b8fabe0272fe21bcefb89485be30ea18 100644 --- a/server/src/main/java/io/seata/server/session/AbstractSessionManager.java +++ b/server/src/main/java/io/seata/server/session/AbstractSessionManager.java @@ -25,16 +25,8 @@ import io.seata.server.store.TransactionStoreManager.LogOperation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - /** * The type Abstract session manager. - * - * @author sharajava */ public abstract class AbstractSessionManager implements SessionManager, SessionLifecycleListener { @@ -43,11 +35,6 @@ public abstract class AbstractSessionManager implements SessionManager, SessionL */ protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractSessionManager.class); - /** - * The Session map. - */ - protected Map<Long, GlobalSession> sessionMap = new ConcurrentHashMap<>(); - /** * The Transaction store manager. */ @@ -58,6 +45,12 @@ public abstract class AbstractSessionManager implements SessionManager, SessionL */ protected String name; + /** + * Instantiates a new Abstract session manager. + */ + public AbstractSessionManager() { + } + /** * Instantiates a new Abstract session manager. * @@ -73,15 +66,6 @@ public abstract class AbstractSessionManager implements SessionManager, SessionL LOGGER.debug("MANAGER[" + name + "] SESSION[" + session + "] " + LogOperation.GLOBAL_ADD); } writeSession(LogOperation.GLOBAL_ADD, session); - sessionMap.put(session.getTransactionId(), session); - - } - - - - @Override - public GlobalSession findGlobalSession(Long transactionId) { - return sessionMap.get(transactionId); } @Override @@ -98,8 +82,6 @@ public abstract class AbstractSessionManager implements SessionManager, SessionL LOGGER.debug("MANAGER[" + name + "] SESSION[" + session + "] " + LogOperation.GLOBAL_REMOVE); } writeSession(LogOperation.GLOBAL_REMOVE, session); - sessionMap.remove(session.getTransactionId()); - } @Override @@ -126,23 +108,6 @@ public abstract class AbstractSessionManager implements SessionManager, SessionL LOGGER.debug("MANAGER[" + name + "] SESSION[" + branchSession + "] " + LogOperation.GLOBAL_ADD); } writeSession(LogOperation.BRANCH_REMOVE, branchSession); - - } - - @Override - public Collection<GlobalSession> allSessions() { - return sessionMap.values(); - } - - @Override - public List<GlobalSession> findGlobalSessions(SessionCondition condition) { - List<GlobalSession> found = new ArrayList<>(); - for (GlobalSession globalSession : sessionMap.values()) { - if (System.currentTimeMillis() - globalSession.getBeginTime() > condition.getOverTimeAliveMills()) { - found.add(globalSession); - } - } - return found; } @Override @@ -174,18 +139,29 @@ public abstract class AbstractSessionManager implements SessionManager, SessionL @Override public void onClose(GlobalSession globalSession) throws TransactionException { globalSession.setActive(false); - } @Override public void onEnd(GlobalSession globalSession) throws TransactionException { removeGlobalSession(globalSession); - } - private void writeSession(LogOperation logOperation, SessionStorable sessionStorable) throws TransactionException{ + private void writeSession(LogOperation logOperation, SessionStorable sessionStorable) throws TransactionException { if (!transactionStoreManager.writeSession(logOperation, sessionStorable)) { throw new TransactionException(TransactionExceptionCode.FailedWriteSession); } } + + @Override + public void destroy() { + } + + /** + * Sets transaction store manager. + * + * @param transactionStoreManager the transaction store manager + */ + public void setTransactionStoreManager(TransactionStoreManager transactionStoreManager) { + this.transactionStoreManager = transactionStoreManager; + } } diff --git a/server/src/main/java/io/seata/server/session/BranchSession.java b/server/src/main/java/io/seata/server/session/BranchSession.java index 31e51961235b42913019c6fe07317d14baac1d57..7354a88ab07deab31cdf3f9be992c64b8136ff62 100644 --- a/server/src/main/java/io/seata/server/session/BranchSession.java +++ b/server/src/main/java/io/seata/server/session/BranchSession.java @@ -15,22 +15,22 @@ */ package io.seata.server.session; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - import io.seata.common.util.CompressUtil; import io.seata.core.exception.TransactionException; import io.seata.core.model.BranchStatus; import io.seata.core.model.BranchType; -import io.seata.server.lock.LockManagerFactory; +import io.seata.server.lock.LockerFactory; import io.seata.server.store.SessionStorable; import io.seata.server.store.StoreConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + /** * The type Branch session. * @@ -45,6 +45,8 @@ public class BranchSession implements Lockable, Comparable<BranchSession>, Sessi private static ThreadLocal<ByteBuffer> byteBufferThreadLocal = ThreadLocal.withInitial(() -> ByteBuffer.allocate( MAX_BRANCH_SESSION_SIZE)); + private String xid; + private long transactionId; private long branchId; @@ -64,7 +66,7 @@ public class BranchSession implements Lockable, Comparable<BranchSession>, Sessi private String applicationData; private ConcurrentHashMap<Map<String, Long>, Set<String>> lockHolder - = new ConcurrentHashMap<Map<String, Long>, Set<String>>(); + = new ConcurrentHashMap<>(); /** * Gets application data. @@ -188,7 +190,7 @@ public class BranchSession implements Lockable, Comparable<BranchSession>, Sessi * * @param status the status */ - void setStatus(BranchStatus status) { + public void setStatus(BranchStatus status) { this.status = status; } @@ -228,6 +230,24 @@ public class BranchSession implements Lockable, Comparable<BranchSession>, Sessi this.branchId = branchId; } + /** + * Gets xid. + * + * @return the xid + */ + public String getXid() { + return xid; + } + + /** + * Sets xid. + * + * @param xid the xid + */ + public void setXid(String xid) { + this.xid = xid; + } + @Override public String toString() { return "BR:" + branchId + "/" + transactionId; @@ -249,12 +269,12 @@ public class BranchSession implements Lockable, Comparable<BranchSession>, Sessi @Override public boolean lock() throws TransactionException { - return LockManagerFactory.get().acquireLock(this); + return LockerFactory.getLockManager().acquireLock(this); } @Override public boolean unlock() throws TransactionException { - return LockManagerFactory.get().releaseLock(this); + return LockerFactory.getLockManager().releaseLock(this); } @Override @@ -268,7 +288,9 @@ public class BranchSession implements Lockable, Comparable<BranchSession>, Sessi byte[] applicationDataBytes = applicationData != null ? applicationData.getBytes() : null; - int size = calBranchSessionSize(resourceIdBytes, lockKeyBytes, clientIdBytes, applicationDataBytes); + byte[] xidBytes = xid != null ? xid.getBytes() : null; + + int size = calBranchSessionSize(resourceIdBytes, lockKeyBytes, clientIdBytes, applicationDataBytes, xidBytes); if (size > MAX_BRANCH_SESSION_SIZE) { if (lockKeyBytes == null) { @@ -327,6 +349,13 @@ public class BranchSession implements Lockable, Comparable<BranchSession>, Sessi byteBuffer.putInt(0); } + if(xidBytes != null){ + byteBuffer.putInt(xidBytes.length); + byteBuffer.put(xidBytes); + }else { + byteBuffer.putInt(0); + } + byteBuffer.put((byte)status.getCode()); byteBuffer.flip(); byte[] result = new byte[byteBuffer.limit()]; @@ -335,18 +364,19 @@ public class BranchSession implements Lockable, Comparable<BranchSession>, Sessi } private int calBranchSessionSize(byte[] resourceIdBytes, byte[] lockKeyBytes, byte[] clientIdBytes, - byte[] applicationDataBytes) { + byte[] applicationDataBytes, byte[] xidBytes) { final int size = 8 // trascationId - + 8 // branchId - + 4 // resourceIdBytes.length - + 4 // lockKeyBytes.length - + 2 // clientIdBytes.length - + 4 // applicationDataBytes.length - + 1 // statusCode - + (resourceIdBytes == null ? 0 : resourceIdBytes.length) - + (lockKeyBytes == null ? 0 : lockKeyBytes.length) - + (clientIdBytes == null ? 0 : clientIdBytes.length) - + (applicationDataBytes == null ? 0 : applicationDataBytes.length); + + 8 // branchId + + 4 // resourceIdBytes.length + + 4 // lockKeyBytes.length + + 2 // clientIdBytes.length + + 4 // applicationDataBytes.length + + 1 // statusCode + + (resourceIdBytes == null ? 0 : resourceIdBytes.length) + + (lockKeyBytes == null ? 0 : lockKeyBytes.length) + + (clientIdBytes == null ? 0 : clientIdBytes.length) + + (applicationDataBytes == null ? 0 : applicationDataBytes.length) + + (xidBytes == null ? 0 : xidBytes.length); return size; } @@ -369,7 +399,7 @@ public class BranchSession implements Lockable, Comparable<BranchSession>, Sessi try { this.lockKey = new String(CompressUtil.uncompress(byLockKey)); } catch (IOException e) { - throw new RuntimeException("uncompress lockKey error", e); + throw new RuntimeException("decompress lockKey error", e); } } else { this.lockKey = new String(byLockKey); @@ -388,6 +418,12 @@ public class BranchSession implements Lockable, Comparable<BranchSession>, Sessi byteBuffer.get(byApplicationData); this.applicationData = new String(byApplicationData); } + int xidLen = byteBuffer.getInt(); + if (xidLen > 0) { + byte[] xidBytes = new byte[xidLen]; + byteBuffer.get(xidBytes); + this.xid = new String(xidBytes); + } this.status = BranchStatus.get(byteBuffer.get()); } diff --git a/server/src/main/java/io/seata/server/session/DefaultSessionManager.java b/server/src/main/java/io/seata/server/session/DefaultSessionManager.java index ff24f7f6bbacc3ebdffb8bb9c2ecd8f02ae3bea5..a9fcf0cae66e943119e47166d17cf655527aee2c 100644 --- a/server/src/main/java/io/seata/server/session/DefaultSessionManager.java +++ b/server/src/main/java/io/seata/server/session/DefaultSessionManager.java @@ -15,19 +15,30 @@ */ package io.seata.server.session; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import io.seata.common.loader.LoadLevel; +import io.seata.core.exception.TransactionException; +import io.seata.server.store.AbstractTransactionStoreManager; import io.seata.server.store.SessionStorable; -import io.seata.server.store.TransactionStoreManager; -import io.seata.server.store.TransactionWriteStore; /** - * The type Default session manager. + * The type Default session manager, store session data in memory. * * @author sharajava */ +@LoadLevel(name = "default") public class DefaultSessionManager extends AbstractSessionManager { + /** + * The Session map. + */ + protected Map<String, GlobalSession> sessionMap = new ConcurrentHashMap<String, GlobalSession>(); + /** * Instantiates a new Default session manager. * @@ -35,27 +46,45 @@ public class DefaultSessionManager extends AbstractSessionManager { */ public DefaultSessionManager(String name) { super(name); - transactionStoreManager = new TransactionStoreManager() { + transactionStoreManager = new AbstractTransactionStoreManager() { @Override public boolean writeSession(LogOperation logOperation, SessionStorable session) { return true; } + }; + } - @Override - public void shutdown() { + @Override + public void addGlobalSession(GlobalSession session) throws TransactionException { + super.addGlobalSession(session); + sessionMap.put(session.getXid(), session); + } - } + @Override + public GlobalSession findGlobalSession(String xid) { + return sessionMap.get(xid); + } - @Override - public List<TransactionWriteStore> readWriteStoreFromFile(int readSize, boolean isHistory) { - return null; - } + @Override + public void removeGlobalSession(GlobalSession session) throws TransactionException { + super.removeGlobalSession(session); + sessionMap.remove(session.getXid()); + } - @Override - public boolean hasRemaining(boolean isHistory) { - return false; + @Override + public Collection<GlobalSession> allSessions() { + return sessionMap.values(); + } + + @Override + public List<GlobalSession> findGlobalSessions(SessionCondition condition) { + List<GlobalSession> found = new ArrayList<>(); + for (GlobalSession globalSession : sessionMap.values()) { + if (System.currentTimeMillis() - globalSession.getBeginTime() > condition.getOverTimeAliveMills()) { + found.add(globalSession); } - }; + } + return found; } @Override diff --git a/server/src/main/java/io/seata/server/session/GlobalSession.java b/server/src/main/java/io/seata/server/session/GlobalSession.java index 1c9f31d5bda543df6cf57fc452d9fda330d275e8..c76481782231c5d03362e7ccc2945615f711686d 100644 --- a/server/src/main/java/io/seata/server/session/GlobalSession.java +++ b/server/src/main/java/io/seata/server/session/GlobalSession.java @@ -15,6 +15,7 @@ */ package io.seata.server.session; +import io.seata.common.XID; import io.seata.core.exception.TransactionException; import io.seata.core.exception.TransactionExceptionCode; import io.seata.core.model.BranchStatus; @@ -47,6 +48,8 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { private static ThreadLocal<ByteBuffer> byteBufferThreadLocal = ThreadLocal.withInitial(() -> ByteBuffer.allocate( MAX_GLOBAL_SESSION_SIZE)); + private String xid; + private long transactionId; private volatile GlobalStatus status; @@ -61,7 +64,9 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { private long beginTime; - private boolean active; + private String applicationData; + + private boolean active = true; private ArrayList<BranchSession> branchSessions = new ArrayList<>(); @@ -165,7 +170,7 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { } - private void clean() throws TransactionException { + public void clean() throws TransactionException { for (BranchSession branchSession : branchSessions) { branchSession.unlock(); } @@ -282,6 +287,7 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { this.transactionServiceGroup = transactionServiceGroup; this.transactionName = transactionName; this.timeout = timeout; + this.xid = XID.generateXID(transactionId); } /** @@ -293,6 +299,15 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { return transactionId; } + /** + * Sets transaction id. + * + * @param transactionId the transaction id + */ + public void setTransactionId(long transactionId) { + this.transactionId = transactionId; + } + /** * Gets status. * @@ -307,10 +322,28 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { * * @param status the status */ - void setStatus(GlobalStatus status) { + public void setStatus(GlobalStatus status) { this.status = status; } + /** + * Gets xid. + * + * @return the xid + */ + public String getXid() { + return xid; + } + + /** + * Sets xid. + * + * @param xid the xid + */ + public void setXid(String xid) { + this.xid = xid; + } + /** * Gets application id. * @@ -356,6 +389,32 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { return beginTime; } + /** + * Sets begin time. + * + * @param beginTime the begin time + */ + public void setBeginTime(long beginTime) { + this.beginTime = beginTime; + } + + /** + * Gets application data. + * + * @return the application data + */ + public String getApplicationData() { + return applicationData; + } + + /** + * Sets application data. + * + * @param applicationData the application data + */ + public void setApplicationData(String applicationData) { + this.applicationData = applicationData; + } /** * Create global session global session. @@ -390,7 +449,11 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { byte[] byTxNameBytes = transactionName != null ? transactionName.getBytes() : null; - int size = calGlobalSessionSize(byApplicationIdBytes, byServiceGroupBytes, byTxNameBytes); + byte[] xidBytes = xid != null ? xid.getBytes():null; + + byte[] applicationDataBytes = applicationData != null ? applicationData.getBytes():null; + + int size = calGlobalSessionSize(byApplicationIdBytes, byServiceGroupBytes, byTxNameBytes, xidBytes, applicationDataBytes); if (size > MAX_GLOBAL_SESSION_SIZE) { throw new RuntimeException("global session size exceeded, size : " + size + " maxBranchSessionSize : " + @@ -420,6 +483,19 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { } else { byteBuffer.putShort((short)0); } + if(xidBytes != null){ + byteBuffer.putInt(xidBytes.length); + byteBuffer.put(xidBytes); + }else { + byteBuffer.putInt(0); + } + if(applicationDataBytes != null){ + byteBuffer.putInt(applicationDataBytes.length); + byteBuffer.put(applicationDataBytes); + }else { + byteBuffer.putInt(0); + } + byteBuffer.putLong(beginTime); byteBuffer.put((byte)status.getCode()); byteBuffer.flip(); @@ -428,17 +504,20 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { return result; } - private int calGlobalSessionSize(byte[] byApplicationIdBytes, byte[] byServiceGroupBytes, byte[] byTxNameBytes) { + private int calGlobalSessionSize(byte[] byApplicationIdBytes, byte[] byServiceGroupBytes, byte[] byTxNameBytes, + byte[] xidBytes, byte[] applicationDataBytes) { final int size = 8 // trascationId - + 4 // timeout - + 2 // byApplicationIdBytes.length - + 2 // byServiceGroupBytes.length - + 2 // byTxNameBytes.length - + 8 // beginTime - + 1 // statusCode - + (byApplicationIdBytes == null ? 0 : byApplicationIdBytes.length) - + (byServiceGroupBytes == null ? 0 : byServiceGroupBytes.length) - + (byTxNameBytes == null ? 0 : byTxNameBytes.length); + + 4 // timeout + + 2 // byApplicationIdBytes.length + + 2 // byServiceGroupBytes.length + + 2 // byTxNameBytes.length + + 8 // beginTime + + 1 // statusCode + + (byApplicationIdBytes == null ? 0 : byApplicationIdBytes.length) + + (byServiceGroupBytes == null ? 0 : byServiceGroupBytes.length) + + (byTxNameBytes == null ? 0 : byTxNameBytes.length) + + (xidBytes ==null ? 0 : xidBytes.length) + + (applicationDataBytes == null ? 0 : applicationDataBytes.length); return size; } @@ -465,6 +544,19 @@ public class GlobalSession implements SessionLifecycle, SessionStorable { byteBuffer.get(byTxName); this.transactionName = new String(byTxName); } + int xidLen = byteBuffer.getInt(); + if(xidLen > 0 ){ + byte[] xidBytes = new byte[xidLen]; + byteBuffer.get(xidBytes); + this.xid = new String(xidBytes); + } + int applicationDataLen = byteBuffer.getInt(); + if(applicationDataLen > 0){ + byte[] applicationDataLenBytes = new byte[applicationDataLen]; + byteBuffer.get(applicationDataLenBytes); + this.applicationData = new String(applicationDataLenBytes); + } + this.beginTime = byteBuffer.getLong(); this.status = GlobalStatus.get(byteBuffer.get()); } diff --git a/server/src/main/java/io/seata/server/session/SessionCondition.java b/server/src/main/java/io/seata/server/session/SessionCondition.java index 4063d7aa3780e9070e0d758140cbd20578e0de2e..1b192bb0c859918c544bb5bee8b591d94abea7c0 100644 --- a/server/src/main/java/io/seata/server/session/SessionCondition.java +++ b/server/src/main/java/io/seata/server/session/SessionCondition.java @@ -15,6 +15,8 @@ */ package io.seata.server.session; +import io.seata.core.model.GlobalStatus; + /** * The type Session condition. * @@ -22,9 +24,46 @@ package io.seata.server.session; * @date 2018 /12/13 */ public class SessionCondition { - + private Long transactionId; + private String xid; + private GlobalStatus status; + private GlobalStatus[] statuses; private long overTimeAliveMills; + /** + * Instantiates a new Session condition. + */ + public SessionCondition() { + } + + /** + * Instantiates a new Session condition. + * + * @param xid the xid + */ + public SessionCondition(String xid) { + this.xid = xid; + } + + /** + * Instantiates a new Session condition. + * + * @param status the status + */ + public SessionCondition(GlobalStatus status) { + this.status = status; + statuses = new GlobalStatus[]{status}; + } + + /** + * Instantiates a new Session condition. + * + * @param statuses the statuses + */ + public SessionCondition(GlobalStatus[] statuses) { + this.statuses = statuses; + } + /** * Instantiates a new Session condition. * @@ -34,6 +73,23 @@ public class SessionCondition { this.overTimeAliveMills = overTimeAliveMills; } + /** + * Gets status. + * + * @return the status + */ + public GlobalStatus getStatus() { + return status; + } + + /** + * Sets status. + * + * @param status the status + */ + public void setStatus(GlobalStatus status) { + this.status = status; + } /** * Gets over time alive mills. @@ -52,4 +108,28 @@ public class SessionCondition { public void setOverTimeAliveMills(long overTimeAliveMills) { this.overTimeAliveMills = overTimeAliveMills; } + + public Long getTransactionId() { + return transactionId; + } + + public void setTransactionId(Long transactionId) { + this.transactionId = transactionId; + } + + public String getXid() { + return xid; + } + + public void setXid(String xid) { + this.xid = xid; + } + + public GlobalStatus[] getStatuses() { + return statuses; + } + + public void setStatuses(GlobalStatus[] statuses) { + this.statuses = statuses; + } } diff --git a/server/src/main/java/io/seata/server/session/SessionHelper.java b/server/src/main/java/io/seata/server/session/SessionHelper.java index b3295d24e8f6596a641744b975eb616c851e9c0c..fec302b1535bcd8c6d28536e92eefa58efaf0183 100644 --- a/server/src/main/java/io/seata/server/session/SessionHelper.java +++ b/server/src/main/java/io/seata/server/session/SessionHelper.java @@ -47,6 +47,7 @@ public class SessionHelper { String applicationData, String lockKeys, String clientId) { BranchSession branchSession = new BranchSession(); + branchSession.setXid(globalSession.getXid()); branchSession.setTransactionId(globalSession.getTransactionId()); branchSession.setBranchId(UUIDGenerator.generateUUID()); branchSession.setBranchType(branchType); diff --git a/server/src/main/java/io/seata/server/session/SessionHolder.java b/server/src/main/java/io/seata/server/session/SessionHolder.java index 30f4b4d00b3ba3fc5cd47f98dd545b3fe705f95a..316b6e11d998fd35d4b52e5be4195737cafd08d2 100644 --- a/server/src/main/java/io/seata/server/session/SessionHolder.java +++ b/server/src/main/java/io/seata/server/session/SessionHolder.java @@ -21,6 +21,7 @@ import java.util.Collection; import io.seata.common.exception.ShouldNeverHappenException; import io.seata.common.exception.StoreException; +import io.seata.common.loader.EnhancedServiceLoader; import io.seata.common.util.StringUtils; import io.seata.config.Configuration; import io.seata.config.ConfigurationFactory; @@ -46,6 +47,11 @@ public class SessionHolder { */ protected static final Configuration CONFIG = ConfigurationFactory.getInstance(); + /** + * The constant DEFAULT. + */ + public static final String DEFAULT = "default"; + /** * The constant ROOT_SESSION_MANAGER_NAME. */ @@ -82,23 +88,33 @@ public class SessionHolder { //the store mode StoreMode storeMode = StoreMode.valueof(mode); if (StoreMode.DB.equals(storeMode)) { - //TODO database store - + //database store + ROOT_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.DB.name()); + ASYNC_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.DB.name(), new Object[]{ASYNC_COMMITTING_SESSION_MANAGER_NAME});; + RETRY_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.DB.name(), new Object[]{RETRY_COMMITTING_SESSION_MANAGER_NAME});; + RETRY_ROLLBACKING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.DB.name(), new Object[]{RETRY_ROLLBACKING_SESSION_MANAGER_NAME});; } else if (StoreMode.FILE.equals(storeMode)) { //file store String sessionStorePath = CONFIG.getConfig(ConfigurationKeys.STORE_FILE_DIR); if (sessionStorePath == null) { throw new StoreException("the {store.file.dir} is empty."); } - ROOT_SESSION_MANAGER = new FileBasedSessionManager(ROOT_SESSION_MANAGER_NAME, sessionStorePath); - ASYNC_COMMITTING_SESSION_MANAGER = new DefaultSessionManager(ASYNC_COMMITTING_SESSION_MANAGER_NAME); - RETRY_COMMITTING_SESSION_MANAGER = new DefaultSessionManager(RETRY_COMMITTING_SESSION_MANAGER_NAME); - RETRY_ROLLBACKING_SESSION_MANAGER = new DefaultSessionManager(RETRY_ROLLBACKING_SESSION_MANAGER_NAME); + ROOT_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, StoreMode.FILE.name(), new Object[]{ROOT_SESSION_MANAGER_NAME, sessionStorePath}); + ASYNC_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, DEFAULT, new Object[]{ASYNC_COMMITTING_SESSION_MANAGER_NAME});; + RETRY_COMMITTING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, DEFAULT, new Object[]{RETRY_COMMITTING_SESSION_MANAGER_NAME});; + RETRY_ROLLBACKING_SESSION_MANAGER = EnhancedServiceLoader.load(SessionManager.class, DEFAULT, new Object[]{RETRY_ROLLBACKING_SESSION_MANAGER_NAME});; } else { //unknown store throw new IllegalArgumentException("unknown store mode:" + mode); } + //relaod + reload(); + } + /** + * Reload. + */ + protected static void reload(){ if (ROOT_SESSION_MANAGER instanceof Reloadable) { ((Reloadable) ROOT_SESSION_MANAGER).reload(); @@ -173,7 +189,6 @@ public class SessionHolder { }); } } - } /** @@ -227,11 +242,11 @@ public class SessionHolder { /** * Find global session. * - * @param transactionId the transaction id + * @param xid the xid * @return the global session */ - public static GlobalSession findGlobalSession(Long transactionId) { - return getRootSessionManager().findGlobalSession(transactionId); + public static GlobalSession findGlobalSession(String xid) { + return getRootSessionManager().findGlobalSession(xid); } public static void destory() { diff --git a/server/src/main/java/io/seata/server/session/SessionManager.java b/server/src/main/java/io/seata/server/session/SessionManager.java index 2883d440482d97d1f295b6c0acc8e7f72059203d..e7e94017191c71019e6b8ceb5f6264f853302070 100644 --- a/server/src/main/java/io/seata/server/session/SessionManager.java +++ b/server/src/main/java/io/seata/server/session/SessionManager.java @@ -41,11 +41,10 @@ public interface SessionManager extends SessionLifecycleListener, Disposable { /** * Find global session global session. * - * @param transactionId the transaction id + * @param xid the xid * @return the global session - * @throws TransactionException the transaction exception */ - GlobalSession findGlobalSession(Long transactionId); + GlobalSession findGlobalSession(String xid) ; /** * Update global session status. diff --git a/server/src/main/java/io/seata/server/session/db/DataBaseSessionManager.java b/server/src/main/java/io/seata/server/session/db/DataBaseSessionManager.java new file mode 100644 index 0000000000000000000000000000000000000000..333b78e3ba1312385497447c0b91e3354eb33444 --- /dev/null +++ b/server/src/main/java/io/seata/server/session/db/DataBaseSessionManager.java @@ -0,0 +1,186 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.session.db; + +import io.seata.common.exception.StoreException; +import io.seata.common.executor.Initialize; +import io.seata.common.loader.EnhancedServiceLoader; +import io.seata.common.loader.LoadLevel; +import io.seata.common.util.StringUtils; +import io.seata.core.exception.TransactionException; +import io.seata.core.model.BranchStatus; +import io.seata.core.model.GlobalStatus; +import io.seata.core.store.StoreMode; +import io.seata.server.session.AbstractSessionManager; +import io.seata.server.session.BranchSession; +import io.seata.server.session.GlobalSession; +import io.seata.server.session.SessionCondition; +import io.seata.server.session.SessionHolder; +import io.seata.server.session.SessionLifecycleListener; +import io.seata.server.session.SessionManager; +import io.seata.server.store.TransactionStoreManager; +import io.seata.server.store.TransactionStoreManager.LogOperation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; +import java.util.List; + +/** + * The Data base session manager. + * + * @author zhangsen + * @data 2019 /4/4 + */ +@LoadLevel(name = "db") +public class DataBaseSessionManager extends AbstractSessionManager implements SessionManager, SessionLifecycleListener, Initialize { + + /** + * The constant LOGGER. + */ + protected static final Logger LOGGER = LoggerFactory.getLogger(DataBaseSessionManager.class); + + /** + * The Task name. + */ + protected String taskName; + + /** + * Instantiates a new Data base session manager. + */ + public DataBaseSessionManager() { + super(); + } + + /** + * Instantiates a new Data base session manager. + * + * @param name the name + */ + public DataBaseSessionManager(String name) { + super(); + this.taskName = name; + } + + @Override + public void init() { + transactionStoreManager = EnhancedServiceLoader.load(TransactionStoreManager.class, StoreMode.DB.name()); + } + + @Override + public void addGlobalSession(GlobalSession session) throws TransactionException { + if (StringUtils.isBlank(taskName)) { + boolean ret = transactionStoreManager.writeSession(LogOperation.GLOBAL_ADD, session); + if (!ret) { + throw new StoreException("addGlobalSession failed."); + } + }else { + boolean ret = transactionStoreManager.writeSession(LogOperation.GLOBAL_UPDATE, session); + if (!ret) { + throw new StoreException("addGlobalSession failed."); + } + } + } + + @Override + public void updateGlobalSessionStatus(GlobalSession session, GlobalStatus status) throws TransactionException { + if (StringUtils.isNotBlank(taskName)) { + return; + } + session.setStatus(status); + boolean ret = transactionStoreManager.writeSession(LogOperation.GLOBAL_UPDATE, session); + if (!ret) { + throw new StoreException("updateGlobalSessionStatus failed."); + } + } + + @Override + public void removeGlobalSession(GlobalSession session) throws TransactionException { + if (StringUtils.isNotBlank(taskName)) { + return; + } + boolean ret = transactionStoreManager.writeSession(LogOperation.GLOBAL_REMOVE, session); + if (!ret) { + throw new StoreException("removeGlobalSession failed."); + } + } + + @Override + public void addBranchSession(GlobalSession globalSession, BranchSession session) throws TransactionException { + if (StringUtils.isNotBlank(taskName)) { + return; + } + boolean ret = transactionStoreManager.writeSession(LogOperation.BRANCH_ADD, session); + if (!ret) { + throw new StoreException("addBranchSession failed."); + } + } + + @Override + public void updateBranchSessionStatus(BranchSession session, BranchStatus status) throws TransactionException { + if (StringUtils.isNotBlank(taskName)) { + return; + } + boolean ret = transactionStoreManager.writeSession(LogOperation.BRANCH_UPDATE, session); + if (!ret) { + throw new StoreException("updateBranchSessionStatus failed."); + } + } + + @Override + public void removeBranchSession(GlobalSession globalSession, BranchSession session) throws TransactionException { + if (StringUtils.isNotBlank(taskName)) { + return; + } + boolean ret = transactionStoreManager.writeSession(LogOperation.BRANCH_REMOVE, session); + if (!ret) { + throw new StoreException("removeBranchSession failed."); + } + } + + @Override + public GlobalSession findGlobalSession(String xid) { + return transactionStoreManager.readSession(xid); + } + + @Override + public Collection<GlobalSession> allSessions() { + //get by taskName + if (SessionHolder.ASYNC_COMMITTING_SESSION_MANAGER_NAME.equalsIgnoreCase(taskName)) { + return findGlobalSessions(new SessionCondition(GlobalStatus.AsyncCommitting)); + } else if (SessionHolder.RETRY_COMMITTING_SESSION_MANAGER_NAME.equalsIgnoreCase(taskName)) { + return findGlobalSessions(new SessionCondition(new GlobalStatus[]{GlobalStatus.CommitRetrying})); + } else if (SessionHolder.RETRY_ROLLBACKING_SESSION_MANAGER_NAME.equalsIgnoreCase(taskName)) { + return findGlobalSessions(new SessionCondition(new GlobalStatus[]{GlobalStatus.RollbackRetrying, + GlobalStatus.TimeoutRollbacking, GlobalStatus.TimeoutRollbackRetrying})); + } else { + //all data + return findGlobalSessions(new SessionCondition(new GlobalStatus[]{ + GlobalStatus.UnKnown, GlobalStatus.Begin, + GlobalStatus.Committing, GlobalStatus.CommitRetrying, GlobalStatus.Rollbacking, GlobalStatus.RollbackRetrying, + GlobalStatus.TimeoutRollbacking, GlobalStatus.TimeoutRollbackRetrying, GlobalStatus.AsyncCommitting})); + } + } + + @Override + public List<GlobalSession> findGlobalSessions(SessionCondition condition) { + //nothing need to do + return transactionStoreManager.readSession(condition); + } + + + +} \ No newline at end of file diff --git a/server/src/main/java/io/seata/server/session/FileBasedSessionManager.java b/server/src/main/java/io/seata/server/session/file/FileBasedSessionManager.java similarity index 71% rename from server/src/main/java/io/seata/server/session/FileBasedSessionManager.java rename to server/src/main/java/io/seata/server/session/file/FileBasedSessionManager.java index 1684cccdface1af2f8aa7077c562b6837cba69c4..02ade24cefb126187f0f11b4a649ed0ad1ec4308 100644 --- a/server/src/main/java/io/seata/server/session/FileBasedSessionManager.java +++ b/server/src/main/java/io/seata/server/session/file/FileBasedSessionManager.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.server.session; +package io.seata.server.session.file; import java.io.File; import java.io.IOException; @@ -23,21 +23,31 @@ import java.util.List; import java.util.Map; import io.seata.common.exception.ShouldNeverHappenException; +import io.seata.common.loader.EnhancedServiceLoader; +import io.seata.common.loader.LoadLevel; import io.seata.config.ConfigurationFactory; import io.seata.core.constants.ConfigurationKeys; import io.seata.core.model.GlobalStatus; +import io.seata.core.store.StoreMode; +import io.seata.server.session.BranchSession; +import io.seata.server.session.DefaultSessionManager; +import io.seata.server.session.GlobalSession; +import io.seata.server.session.Reloadable; +import io.seata.server.session.SessionManager; +import io.seata.server.store.ReloadableStore; import io.seata.server.UUIDGenerator; -import io.seata.server.store.FileTransactionStoreManager; import io.seata.server.store.SessionStorable; import io.seata.server.store.TransactionStoreManager; import io.seata.server.store.TransactionWriteStore; + /** * The type File based session manager. * * @author jimin.jm @alibaba-inc.com */ -public class FileBasedSessionManager extends AbstractSessionManager implements Reloadable { +@LoadLevel(name = "file") +public class FileBasedSessionManager extends DefaultSessionManager implements Reloadable { private static final int READ_SIZE = ConfigurationFactory.getInstance().getInt( ConfigurationKeys.SERVICE_SESSION_RELOAD_READ_SIZE, 100); @@ -51,7 +61,9 @@ public class FileBasedSessionManager extends AbstractSessionManager implements R */ public FileBasedSessionManager(String name, String sessionStoreFilePath) throws IOException { super(name); - transactionStoreManager = new FileTransactionStoreManager(sessionStoreFilePath + File.separator + name, this); + transactionStoreManager = EnhancedServiceLoader.load(TransactionStoreManager.class, StoreMode.FILE.name(), + new Class[]{String.class, SessionManager.class}, + new Object[]{sessionStoreFilePath + File.separator + name, this}); } @Override @@ -68,13 +80,13 @@ public class FileBasedSessionManager extends AbstractSessionManager implements R if (!unhandledBranchBuffer.isEmpty()) { unhandledBranchBuffer.values().forEach(branchSession -> { - long tid = branchSession.getTransactionId(); + String xid = branchSession.getXid(); long bid = branchSession.getBranchId(); - GlobalSession found = sessionMap.get(tid); + GlobalSession found = sessionMap.get(xid); if (found == null) { // Ignore if (LOGGER.isInfoEnabled()) { - LOGGER.info("GlobalSession Does Not Exists For BranchSession [" + bid + "/" + tid + "]"); + LOGGER.info("GlobalSession Does Not Exists For BranchSession [" + bid + "/" + xid + "]"); } } else { BranchSession existingBranch = found.getBranch(branchSession.getBranchId()); @@ -91,7 +103,7 @@ public class FileBasedSessionManager extends AbstractSessionManager implements R private void washSessions() { if (sessionMap.size() > 0) { - Iterator<Map.Entry<Long, GlobalSession>> iterator = sessionMap.entrySet().iterator(); + Iterator<Map.Entry<String, GlobalSession>> iterator = sessionMap.entrySet().iterator(); while (iterator.hasNext()) { GlobalSession globalSession = iterator.next().getValue(); @@ -116,8 +128,11 @@ public class FileBasedSessionManager extends AbstractSessionManager implements R } private void restoreSessions(boolean isHistory, Map<Long, BranchSession> unhandledBranchBuffer) { - while (transactionStoreManager.hasRemaining(isHistory)) { - List<TransactionWriteStore> stores = transactionStoreManager.readWriteStoreFromFile(READ_SIZE, isHistory); + if(!(transactionStoreManager instanceof ReloadableStore)){ + return; + } + while (((ReloadableStore)transactionStoreManager).hasRemaining(isHistory)) { + List<TransactionWriteStore> stores = ((ReloadableStore)transactionStoreManager).readWriteStore(READ_SIZE, isHistory); restore(stores, unhandledBranchBuffer); } } @@ -131,31 +146,40 @@ public class FileBasedSessionManager extends AbstractSessionManager implements R switch (logOperation) { case GLOBAL_ADD: case GLOBAL_UPDATE: { - GlobalSession globalSession = (GlobalSession)sessionStorable; - long tid = globalSession.getTransactionId(); - GlobalSession foundGlobalSession = sessionMap.get(tid); + GlobalSession globalSession = (GlobalSession) sessionStorable; + if(globalSession.getTransactionId() == 0){ + LOGGER.error("Restore globalSession from file failed, the transactionId is zero , xid:" + globalSession.getXid()); + break; + } + GlobalSession foundGlobalSession = sessionMap.get(globalSession.getXid()); if (foundGlobalSession == null) { - sessionMap.put(globalSession.getTransactionId(), globalSession); + sessionMap.put(globalSession.getXid(), globalSession); } else { foundGlobalSession.setStatus(globalSession.getStatus()); } break; } case GLOBAL_REMOVE: { - GlobalSession globalSession = (GlobalSession)sessionStorable; - long tid = globalSession.getTransactionId(); - if (sessionMap.remove(tid) == null) { + GlobalSession globalSession = (GlobalSession) sessionStorable; + if(globalSession.getTransactionId() == 0){ + LOGGER.error("Restore globalSession from file failed, the transactionId is zero , xid:" + globalSession.getXid()); + break; + } + if (sessionMap.remove(globalSession.getXid()) == null) { if (LOGGER.isInfoEnabled()) { - LOGGER.info("GlobalSession To Be Removed Does Not Exists [" + tid + "]"); + LOGGER.info("GlobalSession To Be Removed Does Not Exists [" + globalSession.getXid() + "]"); } } break; } case BRANCH_ADD: case BRANCH_UPDATE: { - BranchSession branchSession = (BranchSession)sessionStorable; - long tid = branchSession.getTransactionId(); - GlobalSession foundGlobalSession = sessionMap.get(tid); + BranchSession branchSession = (BranchSession) sessionStorable; + if( branchSession.getTransactionId() == 0){ + LOGGER.error("Restore branchSession from file failed, the transactionId is zero , xid:" + branchSession.getXid()); + break; + } + GlobalSession foundGlobalSession = sessionMap.get(branchSession.getXid()); if (foundGlobalSession == null) { unhandledBranchSessions.put(branchSession.getBranchId(), branchSession); } else { @@ -169,21 +193,25 @@ public class FileBasedSessionManager extends AbstractSessionManager implements R break; } case BRANCH_REMOVE: { - BranchSession branchSession = (BranchSession)sessionStorable; - long tid = branchSession.getTransactionId(); + BranchSession branchSession = (BranchSession) sessionStorable; + String xid = branchSession.getXid(); long bid = branchSession.getBranchId(); - GlobalSession found = sessionMap.get(tid); + if(branchSession.getTransactionId() == 0){ + LOGGER.error("Restore branchSession from file failed, the transactionId is zero , xid:" + branchSession.getXid()); + break; + } + GlobalSession found = sessionMap.get(xid); if (found == null) { if (LOGGER.isInfoEnabled()) { LOGGER.info( - "GlobalSession To Be Updated (Remove Branch) Does Not Exists [" + bid + "/" + tid - + "]"); + "GlobalSession To Be Updated (Remove Branch) Does Not Exists [" + bid + "/" + xid + + "]"); } } else { BranchSession theBranch = found.getBranch(bid); if (theBranch == null) { if (LOGGER.isInfoEnabled()) { - LOGGER.info("BranchSession To Be Updated Does Not Exists [" + bid + "/" + tid + "]"); + LOGGER.info("BranchSession To Be Updated Does Not Exists [" + bid + "/" + xid + "]"); } } else { found.remove(theBranch); diff --git a/server/src/main/java/io/seata/server/store/AbstractTransactionStoreManager.java b/server/src/main/java/io/seata/server/store/AbstractTransactionStoreManager.java new file mode 100644 index 0000000000000000000000000000000000000000..f024bd772bf063eef62b46623ff533b41eff6c3e --- /dev/null +++ b/server/src/main/java/io/seata/server/store/AbstractTransactionStoreManager.java @@ -0,0 +1,44 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.store; + +import io.seata.server.session.GlobalSession; +import io.seata.server.session.SessionCondition; + +import java.util.List; + +/** + * The type Abstract transaction store manager. + * + * @author zhangsen + * @data 2019 /4/25 + */ +public abstract class AbstractTransactionStoreManager implements TransactionStoreManager { + + @Override + public GlobalSession readSession(String xid) { + return null; + } + + @Override + public List<GlobalSession> readSession(SessionCondition sessionCondition) { + return null; + } + + @Override + public void shutdown() { + } +} diff --git a/server/src/main/java/io/seata/server/lock/LockManagerFactory.java b/server/src/main/java/io/seata/server/store/ReloadableStore.java similarity index 54% rename from server/src/main/java/io/seata/server/lock/LockManagerFactory.java rename to server/src/main/java/io/seata/server/store/ReloadableStore.java index 3468cc89449b3781808cf6771f4dae2962c5f5ee..d7b705c65a81f786ff5011dfab09ca8a749dbfcf 100644 --- a/server/src/main/java/io/seata/server/lock/LockManagerFactory.java +++ b/server/src/main/java/io/seata/server/store/ReloadableStore.java @@ -13,34 +13,34 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.server.lock; +package io.seata.server.store; + +import java.util.List; /** - * The type Lock manager factory. + * The interface Reloadable store. * - * @author sharajava + * @author zhangsen + * @data 2019 /4/24 */ -public class LockManagerFactory { - - private static class SingletonHolder { - private static LockManager INSTANCE = new DefaultLockManagerImpl(); - } +public interface ReloadableStore { /** - * Get lock manager. + * Read write store. * - * @return the lock manager + * @param readSize the read size + * @param isHistory the is history + * @return the list */ - public static final LockManager get() { - return SingletonHolder.INSTANCE; - } + List<TransactionWriteStore> readWriteStore(int readSize, boolean isHistory); /** - * Just for test mocking + * Has remaining boolean. * - * @param lockManager the lock manager + * @param isHistory the is history + * @return the boolean */ - public static void set(LockManager lockManager) { - SingletonHolder.INSTANCE = lockManager; - } + boolean hasRemaining(boolean isHistory); + + } diff --git a/server/src/main/java/io/seata/server/store/TransactionStoreManager.java b/server/src/main/java/io/seata/server/store/TransactionStoreManager.java index da7d44a4ad942c6802dc4140170f5acd27922752..26dc122009c24b0fbc17c37d42227a4b864626c9 100644 --- a/server/src/main/java/io/seata/server/store/TransactionStoreManager.java +++ b/server/src/main/java/io/seata/server/store/TransactionStoreManager.java @@ -15,6 +15,9 @@ */ package io.seata.server.store; +import io.seata.server.session.GlobalSession; +import io.seata.server.session.SessionCondition; + import java.util.List; /** @@ -33,27 +36,28 @@ public interface TransactionStoreManager { */ boolean writeSession(LogOperation logOperation, SessionStorable session); + /** - * Shutdown. + * Read global session global session. + * + * @param xid the xid + * @return the global session */ - void shutdown(); + GlobalSession readSession(String xid); /** - * Read write store from file list. + * Read session by status list. * - * @param readSize the read size - * @param isHistory the is history + * @param sessionCondition the session condition * @return the list */ - List<TransactionWriteStore> readWriteStoreFromFile(int readSize, boolean isHistory); + List<GlobalSession> readSession(SessionCondition sessionCondition); /** - * Has remaining boolean. - * - * @param isHistory the is history - * @return the boolean + * Shutdown. */ - boolean hasRemaining(boolean isHistory); + void shutdown(); + /** * The enum Log operation. diff --git a/server/src/main/java/io/seata/server/store/db/DatabaseTransactionStoreManager.java b/server/src/main/java/io/seata/server/store/db/DatabaseTransactionStoreManager.java new file mode 100644 index 0000000000000000000000000000000000000000..1cce734f8eaa4b9024b1d1b0886c488160a288ce --- /dev/null +++ b/server/src/main/java/io/seata/server/store/db/DatabaseTransactionStoreManager.java @@ -0,0 +1,299 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.store.db; + + +import io.seata.common.exception.StoreException; +import io.seata.common.executor.Initialize; +import io.seata.common.loader.EnhancedServiceLoader; +import io.seata.common.loader.LoadLevel; +import io.seata.common.util.CollectionUtils; +import io.seata.common.util.StringUtils; +import io.seata.config.Configuration; +import io.seata.config.ConfigurationFactory; +import io.seata.core.constants.ConfigurationKeys; +import io.seata.core.model.BranchStatus; +import io.seata.core.model.BranchType; +import io.seata.core.model.GlobalStatus; +import io.seata.core.store.BranchTransactionDO; +import io.seata.core.store.GlobalTransactionDO; +import io.seata.core.store.LogStore; +import io.seata.core.store.StoreMode; +import io.seata.core.store.db.DataSourceGenerator; +import io.seata.server.session.BranchSession; +import io.seata.server.session.GlobalSession; +import io.seata.server.session.SessionCondition; +import io.seata.server.store.AbstractTransactionStoreManager; +import io.seata.server.store.SessionStorable; +import io.seata.server.store.TransactionStoreManager; + +import javax.sql.DataSource; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * The type Database transaction store manager. + * + * @author zhangsen + * @data 2019 /4/2 + */ +@LoadLevel(name = "db") +public class DatabaseTransactionStoreManager extends AbstractTransactionStoreManager implements TransactionStoreManager, Initialize { + + /** + * The constant CONFIG. + */ + protected static final Configuration CONFIG = ConfigurationFactory.getInstance(); + + /** + * The constant DEFAULT_LOG_QUERY_LIMIT. + */ + protected static final int DEFAULT_LOG_QUERY_LIMIT = 100; + + /** + * is inited + */ + protected AtomicBoolean inited = new AtomicBoolean(false); + + /** + * The Log store. + */ + protected LogStore logStore ; + + + /** + * The Log query limit. + */ + protected int logQueryLimit ; + + /** + * Instantiates a new Database transaction store manager. + */ + public DatabaseTransactionStoreManager(){ + } + + @Override + public synchronized void init(){ + if(inited.get()){ + return ; + } + logQueryLimit = CONFIG.getInt(ConfigurationKeys.STORE_DB_LOG_QUERY_LIMIT, DEFAULT_LOG_QUERY_LIMIT); + String datasourceType = CONFIG.getConfig(ConfigurationKeys.STORE_DB_DATASOURCE_TYPE); + //init dataSource + DataSourceGenerator dataSourceGenerator = EnhancedServiceLoader.load(DataSourceGenerator.class, datasourceType); + DataSource logStoreDataSource = dataSourceGenerator.generateDataSource(); + logStore = EnhancedServiceLoader.load(LogStore.class, StoreMode.DB.name(), new Class[]{DataSource.class}, new Object[]{logStoreDataSource}); + inited.set(true); + } + + @Override + public boolean writeSession(LogOperation logOperation, SessionStorable session) { + if (LogOperation.GLOBAL_ADD.equals(logOperation)){ + logStore.insertGlobalTransactionDO(convertGlobalTransactionDO(session)); + }else if(LogOperation.GLOBAL_UPDATE.equals(logOperation)){ + logStore.updateGlobalTransactionDO(convertGlobalTransactionDO(session)); + }else if(LogOperation.GLOBAL_REMOVE.equals(logOperation)){ + logStore.deleteGlobalTransactionDO(convertGlobalTransactionDO(session)); + }else if(LogOperation.BRANCH_ADD.equals(logOperation)){ + logStore.insertBranchTransactionDO(convertBranchTransactionDO(session)); + }else if(LogOperation.BRANCH_UPDATE.equals(logOperation)){ + logStore.updateBranchTransactionDO(convertBranchTransactionDO(session)); + }else if(LogOperation.BRANCH_REMOVE.equals(logOperation)){ + logStore.deleteBranchTransactionDO(convertBranchTransactionDO(session)); + }else { + throw new StoreException("Unknown LogOperation:" + logOperation.name()); + } + return true; + } + + /** + * Read session global session. + * + * @param transactionId the transaction id + * @return the global session + */ + public GlobalSession readSession(Long transactionId) { + //global transaction + GlobalTransactionDO globalTransactionDO = logStore.queryGlobalTransactionDO(transactionId); + if(globalTransactionDO == null){ + return null; + } + //branch transactions + List<BranchTransactionDO> branchTransactionDOs = logStore.queryBranchTransactionDO(globalTransactionDO.getXid()); + return getGlobalSession(globalTransactionDO, branchTransactionDOs); + } + + /** + * Read session global session. + * + * @param xid the xid + * @return the global session + */ + @Override + public GlobalSession readSession(String xid) { + //global transaction + GlobalTransactionDO globalTransactionDO = logStore.queryGlobalTransactionDO(xid); + if(globalTransactionDO == null){ + return null; + } + //branch transactions + List<BranchTransactionDO> branchTransactionDOs = logStore.queryBranchTransactionDO(globalTransactionDO.getXid()); + return getGlobalSession(globalTransactionDO, branchTransactionDOs); + } + + /** + * Read session list. + * + * @param statuses the statuses + * @return the list + */ + public List<GlobalSession> readSession(GlobalStatus[] statuses) { + int[] states = new int[statuses.length]; + for(int i = 0; i < statuses.length; i++){ + states[i] = statuses[i].getCode(); + } + List<GlobalSession> globalSessions = new ArrayList<GlobalSession>(); + //global transaction + List<GlobalTransactionDO> globalTransactionDOs = logStore.queryGlobalTransactionDO(states, logQueryLimit); + if(CollectionUtils.isEmpty(globalTransactionDOs)){ + return null; + } + for(GlobalTransactionDO globalTransactionDO : globalTransactionDOs){ + List<BranchTransactionDO> branchTransactionDOs = logStore.queryBranchTransactionDO(globalTransactionDO.getXid()); + globalSessions.add(getGlobalSession(globalTransactionDO, branchTransactionDOs)); + } + return globalSessions; + } + + @Override + public List<GlobalSession> readSession(SessionCondition sessionCondition) { + if(StringUtils.isNotBlank(sessionCondition.getXid())){ + GlobalSession globalSession = readSession(sessionCondition.getXid()); + if(globalSession != null){ + List<GlobalSession> globalSessions = new ArrayList(); + globalSessions.add(globalSession); + return globalSessions; + } + }else if(sessionCondition.getTransactionId() != null){ + GlobalSession globalSession = readSession(sessionCondition.getTransactionId()); + if(globalSession != null){ + List<GlobalSession> globalSessions = new ArrayList(); + globalSessions.add(globalSession); + return globalSessions; + } + }else if(CollectionUtils.isNotEmpty(sessionCondition.getStatuses())){ + return readSession(sessionCondition.getStatuses()); + } + return null; + } + + private GlobalSession getGlobalSession(GlobalTransactionDO globalTransactionDO, List<BranchTransactionDO> branchTransactionDOs){ + GlobalSession globalSession = convertGlobalSession(globalTransactionDO); + //branch transactions + if(branchTransactionDOs != null && branchTransactionDOs.size() > 0){ + for(BranchTransactionDO branchTransactionDO : branchTransactionDOs){ + globalSession.add(convertBranchSession(branchTransactionDO)); + } + } + return globalSession; + } + + private GlobalSession convertGlobalSession(GlobalTransactionDO globalTransactionDO){ + GlobalSession session = new GlobalSession(globalTransactionDO.getApplicationId(), + globalTransactionDO.getTransactionServiceGroup(), + globalTransactionDO.getTransactionName(), + globalTransactionDO.getTimeout()); + session.setTransactionId(globalTransactionDO.getTransactionId()); + session.setXid(globalTransactionDO.getXid()); + session.setStatus(GlobalStatus.get(globalTransactionDO.getStatus())); + session.setApplicationData(globalTransactionDO.getApplicationData()); + session.setBeginTime(globalTransactionDO.getBeginTime()); + return session; + } + + private BranchSession convertBranchSession(BranchTransactionDO branchTransactionDO){ + BranchSession branchSession = new BranchSession(); + branchSession.setXid(branchTransactionDO.getXid()); + branchSession.setTransactionId(branchTransactionDO.getTransactionId()); + branchSession.setApplicationData(branchTransactionDO.getApplicationData()); + branchSession.setBranchId(branchTransactionDO.getBranchId()); + branchSession.setBranchType(BranchType.valueOf(branchTransactionDO.getBranchType())); + branchSession.setResourceId(branchTransactionDO.getResourceId()); + branchSession.setClientId(branchTransactionDO.getClientId()); + branchSession.setLockKey(branchTransactionDO.getLockKey()); + branchSession.setResourceGroupId(branchTransactionDO.getResourceGroupId()); + branchSession.setStatus(BranchStatus.get(branchTransactionDO.getStatus())); + return branchSession; + } + + private GlobalTransactionDO convertGlobalTransactionDO(SessionStorable session){ + if(session == null || !(session instanceof GlobalSession)){ + throw new IllegalArgumentException("the parameter of SessionStorable is not available, SessionStorable:" + StringUtils.toString(session)); + } + GlobalSession globalSession = (GlobalSession) session; + + GlobalTransactionDO globalTransactionDO = new GlobalTransactionDO(); + globalTransactionDO.setXid(globalSession.getXid()); + globalTransactionDO.setStatus(globalSession.getStatus().getCode()); + globalTransactionDO.setApplicationId(globalSession.getApplicationId()); + globalTransactionDO.setBeginTime(globalSession.getBeginTime()); + globalTransactionDO.setTimeout(globalSession.getTimeout()); + globalTransactionDO.setTransactionId(globalSession.getTransactionId()); + globalTransactionDO.setTransactionName(globalSession.getTransactionName()); + globalTransactionDO.setTransactionServiceGroup(globalSession.getTransactionServiceGroup()); + globalTransactionDO.setApplicationData(globalSession.getApplicationData()); + return globalTransactionDO; + } + + private BranchTransactionDO convertBranchTransactionDO(SessionStorable session){ + if(session == null || !(session instanceof BranchSession)){ + throw new IllegalArgumentException("the parameter of SessionStorable is not available, SessionStorable:" + StringUtils.toString(session)); + } + BranchSession branchSession = (BranchSession) session; + + BranchTransactionDO branchTransactionDO = new BranchTransactionDO(); + branchTransactionDO.setXid(branchSession.getXid()); + branchTransactionDO.setBranchId(branchSession.getBranchId()); + branchTransactionDO.setBranchType(branchSession.getBranchType().name()); + branchTransactionDO.setClientId(branchSession.getClientId()); + branchTransactionDO.setLockKey(branchSession.getLockKey()); + branchTransactionDO.setResourceGroupId(branchSession.getResourceGroupId()); + branchTransactionDO.setTransactionId(branchSession.getTransactionId()); + branchTransactionDO.setApplicationData(branchSession.getApplicationData()); + branchTransactionDO.setResourceId(branchSession.getResourceId()); + branchTransactionDO.setStatus(branchSession.getStatus().getCode()); + return branchTransactionDO; + } + + /** + * Sets log store. + * + * @param logStore the log store + */ + public void setLogStore(LogStore logStore) { + this.logStore = logStore; + } + + /** + * Sets log query limit. + * + * @param logQueryLimit the log query limit + */ + public void setLogQueryLimit(int logQueryLimit) { + this.logQueryLimit = logQueryLimit; + } +} \ No newline at end of file diff --git a/server/src/main/java/io/seata/server/store/db/DbcpDataSourceGenerator.java b/server/src/main/java/io/seata/server/store/db/DbcpDataSourceGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..2cc8e14929e99b95f72331cf549bdec0438b99a7 --- /dev/null +++ b/server/src/main/java/io/seata/server/store/db/DbcpDataSourceGenerator.java @@ -0,0 +1,54 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.store.db; + +import io.seata.common.loader.LoadLevel; +import io.seata.core.store.db.AbstractDataSourceGenerator; +import org.apache.commons.dbcp.BasicDataSource; + +import javax.sql.DataSource; + +/** + * The type Dbcp data source generator. + * + * @author zhangsen + * @data 2019 /4/24 + */ +@LoadLevel(name = "dbcp") +public class DbcpDataSourceGenerator extends AbstractDataSourceGenerator { + + @Override + public DataSource generateDataSource() { + BasicDataSource ds = new BasicDataSource(); + ds.setDriverClassName(getDriverName(getDBType())); + ds.setUrl(getUrl()); + ds.setUsername(getUser()); + ds.setPassword(getPassword()); + ds.setInitialSize(getMinConn()); + ds.setMaxActive(getMaxConn()); + ds.setMinIdle(getMinConn()); + ds.setMaxIdle(getMinConn()); + ds.setMaxWait(5000); + ds.setTimeBetweenEvictionRunsMillis(120000); + ds.setNumTestsPerEvictionRun(1); + ds.setTestWhileIdle(true); + ds.setValidationQuery(getValidationQuery(getDBType())); + ds.setConnectionProperties("useUnicode=yes;characterEncoding=utf8;socketTimeout=5000;connectTimeout=500"); + return ds; + } + + +} diff --git a/server/src/main/java/io/seata/server/store/db/DruidDataSourceGenerator.java b/server/src/main/java/io/seata/server/store/db/DruidDataSourceGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..04ddfe77b6320928108d0a97b0da4b52037f1026 --- /dev/null +++ b/server/src/main/java/io/seata/server/store/db/DruidDataSourceGenerator.java @@ -0,0 +1,54 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.store.db; + +import com.alibaba.druid.pool.DruidDataSource; +import io.seata.common.loader.LoadLevel; +import io.seata.core.store.db.AbstractDataSourceGenerator; + +import javax.sql.DataSource; + +/** + * The type Druid data source generator. + * + * @author zhangsen + * @data 2019 /4/28 + */ +@LoadLevel(name = "druid") +public class DruidDataSourceGenerator extends AbstractDataSourceGenerator { + + @Override + public DataSource generateDataSource() { + DruidDataSource ds = new DruidDataSource(); + ds.setDriverClassName(getDriverName(getDBType())); + ds.setUrl(getUrl()); + ds.setUsername(getUser()); + ds.setPassword(getPassword()); + ds.setInitialSize(getMinConn()); + ds.setMaxActive(getMaxConn()); + ds.setMinIdle(getMinConn()); + ds.setMaxWait(5000); + ds.setTimeBetweenEvictionRunsMillis(120000); + ds.setMinEvictableIdleTimeMillis(300000); + ds.setTestWhileIdle(true); + ds.setTestOnBorrow(true); + ds.setPoolPreparedStatements(true); + ds.setMaxPoolPreparedStatementPerConnectionSize(20); + ds.setValidationQuery(getValidationQuery(getDBType())); + ds.setDefaultAutoCommit(true); + return ds; + } +} diff --git a/server/src/main/java/io/seata/server/store/FileTransactionStoreManager.java b/server/src/main/java/io/seata/server/store/file/FileTransactionStoreManager.java similarity index 92% rename from server/src/main/java/io/seata/server/store/FileTransactionStoreManager.java rename to server/src/main/java/io/seata/server/store/file/FileTransactionStoreManager.java index 9b3a32229c57e031c6c7c7edb1b00954a70a2a89..5f8e42d8a31bae8e1173f486c471a1e48fd298e3 100644 --- a/server/src/main/java/io/seata/server/store/FileTransactionStoreManager.java +++ b/server/src/main/java/io/seata/server/store/file/FileTransactionStoreManager.java @@ -13,16 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.server.store; - -import io.seata.common.thread.NamedThreadFactory; -import io.seata.common.util.CollectionUtils; -import io.seata.server.session.BranchSession; -import io.seata.server.session.GlobalSession; -import io.seata.server.session.SessionCondition; -import io.seata.server.session.SessionManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +package io.seata.server.store.file; import java.io.File; import java.io.IOException; @@ -33,17 +24,39 @@ import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.*; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.ReentrantLock; +import io.seata.common.exception.StoreException; +import io.seata.common.loader.LoadLevel; +import io.seata.common.thread.NamedThreadFactory; +import io.seata.common.util.CollectionUtils; +import io.seata.server.session.BranchSession; +import io.seata.server.session.GlobalSession; +import io.seata.server.session.SessionCondition; +import io.seata.server.session.SessionManager; +import io.seata.server.store.AbstractTransactionStoreManager; +import io.seata.server.store.FlushDiskMode; +import io.seata.server.store.ReloadableStore; +import io.seata.server.store.SessionStorable; +import io.seata.server.store.StoreConfig; +import io.seata.server.store.TransactionStoreManager; +import io.seata.server.store.TransactionWriteStore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * The type File transaction store manager. * * @author jimin.jm @alibaba-inc.com */ -public class FileTransactionStoreManager implements TransactionStoreManager { - +@LoadLevel(name = "file") +public class FileTransactionStoreManager extends AbstractTransactionStoreManager implements TransactionStoreManager, ReloadableStore { private static final Logger LOGGER = LoggerFactory.getLogger(FileTransactionStoreManager.class); private static final int MAX_THREAD_WRITE = 1; @@ -119,8 +132,7 @@ public class FileTransactionStoreManager implements TransactionStoreManager { */ public FileTransactionStoreManager(String fullFileName, SessionManager sessionManager) throws IOException { initFile(fullFileName); - fileWriteExecutor = - new ThreadPoolExecutor(MAX_THREAD_WRITE, MAX_THREAD_WRITE, Integer.MAX_VALUE, TimeUnit.MILLISECONDS, + fileWriteExecutor = new ThreadPoolExecutor(MAX_THREAD_WRITE, MAX_THREAD_WRITE, Integer.MAX_VALUE, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), new NamedThreadFactory("fileTransactionStore", MAX_THREAD_WRITE, true)); writeDataFileRunnable = new WriteDataFileRunnable(); @@ -247,6 +259,16 @@ public class FileTransactionStoreManager implements TransactionStoreManager { return false; } + @Override + public GlobalSession readSession(String xid) { + throw new StoreException("unsupport for read from file, xid:" + xid); + } + + @Override + public List<GlobalSession> readSession(SessionCondition sessionCondition) { + throw new StoreException("unsupport for read from file"); + } + @Override public void shutdown() { if (null != fileWriteExecutor) { @@ -257,7 +279,7 @@ public class FileTransactionStoreManager implements TransactionStoreManager { ++retry; try { Thread.sleep(SHUTDOWN_CHECK_INTERNAL); - } catch (InterruptedException exx) { + } catch (InterruptedException ignore) { } } if (retry >= MAX_SHUTDOWN_RETRY) { @@ -273,7 +295,7 @@ public class FileTransactionStoreManager implements TransactionStoreManager { } @Override - public List<TransactionWriteStore> readWriteStoreFromFile(int readSize, boolean isHistory) { + public List<TransactionWriteStore> readWriteStore(int readSize, boolean isHistory) { File file = null; long currentOffset = 0; if (isHistory) { @@ -305,7 +327,7 @@ public class FileTransactionStoreManager implements TransactionStoreManager { raf = new RandomAccessFile(file, "r"); return currentOffset < raf.length(); - } catch (IOException exx) { + } catch (IOException ignore) { } finally { closeFile(raf); } @@ -512,7 +534,7 @@ public class FileTransactionStoreManager implements TransactionStoreManager { StoreRequest storeRequest = storeRequests.poll(MAX_WAIT_TIME_MILLS, TimeUnit.MILLISECONDS); handleStoreRequest(storeRequest); } catch (Exception exx) { - LOGGER.error("write file error", exx.getMessage()); + LOGGER.error("write file error: {}", exx.getMessage(), exx); } } handleRestRequest(); diff --git a/server/src/main/resources/META-INF/services/io.seata.core.lock.Locker b/server/src/main/resources/META-INF/services/io.seata.core.lock.Locker new file mode 100644 index 0000000000000000000000000000000000000000..d8b311d6e939d90cf4e63c6c0c4f16b0f1167a58 --- /dev/null +++ b/server/src/main/resources/META-INF/services/io.seata.core.lock.Locker @@ -0,0 +1,2 @@ +io.seata.server.lock.memory.MemoryLocker +io.seata.server.lock.db.DataBaseLocker \ No newline at end of file diff --git a/server/src/main/resources/META-INF/services/io.seata.core.store.db.DataSourceGenerator b/server/src/main/resources/META-INF/services/io.seata.core.store.db.DataSourceGenerator new file mode 100644 index 0000000000000000000000000000000000000000..68b9548e561d4ec4dd3cc8eee59007740b286e9e --- /dev/null +++ b/server/src/main/resources/META-INF/services/io.seata.core.store.db.DataSourceGenerator @@ -0,0 +1,2 @@ +io.seata.server.store.db.DbcpDataSourceGenerator +io.seata.server.store.db.DruidDataSourceGenerator \ No newline at end of file diff --git a/server/src/main/resources/META-INF/services/io.seata.server.session.SessionManager b/server/src/main/resources/META-INF/services/io.seata.server.session.SessionManager new file mode 100644 index 0000000000000000000000000000000000000000..9d3f0808a601dc62c5fde38ddd466c2fd8d363ae --- /dev/null +++ b/server/src/main/resources/META-INF/services/io.seata.server.session.SessionManager @@ -0,0 +1,3 @@ +io.seata.server.session.file.FileBasedSessionManager +io.seata.server.session.db.DataBaseSessionManager +io.seata.server.session.DefaultSessionManager \ No newline at end of file diff --git a/server/src/main/resources/META-INF/services/io.seata.server.store.TransactionStoreManager b/server/src/main/resources/META-INF/services/io.seata.server.store.TransactionStoreManager new file mode 100644 index 0000000000000000000000000000000000000000..948d6c079dd7e5069225bdd0dc297f9c47d263cb --- /dev/null +++ b/server/src/main/resources/META-INF/services/io.seata.server.store.TransactionStoreManager @@ -0,0 +1,2 @@ +io.seata.server.store.db.DatabaseTransactionStoreManager +io.seata.server.store.file.FileTransactionStoreManager \ No newline at end of file diff --git a/server/src/main/resources/db_store.sql b/server/src/main/resources/db_store.sql new file mode 100644 index 0000000000000000000000000000000000000000..78b4fa01871b0213c99309239ca3a6f119c4119a --- /dev/null +++ b/server/src/main/resources/db_store.sql @@ -0,0 +1,52 @@ +-- the table to store GlobalSession data +drop table `global_table`; +create table `global_table` ( + `xid` varchar(128) not null, + `transaction_id` bigint, + `status` tinyint not null, + `application_id` varchar(32), + `transaction_service_group` varchar(32), + `transaction_name` varchar(64), + `timeout` int, + `begin_time` bigint, + `application_data` varchar(2000), + `gmt_create` datetime, + `gmt_modified` datetime, + primary key (`xid`), + key `idx_gmt_modified_status` (`gmt_modified`, `status`), + key `idx_transaction_id` (`transaction_id`) +); + +-- the table to store BranchSession data +drop table `branch_table`; +create table `branch_table` ( + `branch_id` bigint not null, + `xid` varchar(128) not null, + `transaction_id` bigint , + `resource_group_id` varchar(32), + `resource_id` varchar(256) , + `lock_key` varchar(128) , + `branch_type` varchar(8) , + `status` tinyint, + `client_id` varchar(64), + `application_data` varchar(2000), + `gmt_create` datetime, + `gmt_modified` datetime, + primary key (`branch_id`), + key `idx_xid` (`xid`) +); + +-- the table to store lock data +drop table `lock_table`; +create table `lock_table` ( + `row_key` varchar(128) not null, + `xid` varchar(96), + `transaction_id` long , + `branch_id` long, + `resource_id` varchar(256) , + `table_name` varchar(32) , + `pk` varchar(32) , + `gmt_create` datetime , + `gmt_modified` datetime, + primary key(`row_key`) +); diff --git a/server/src/main/resources/file.conf b/server/src/main/resources/file.conf index dc908a344ef9b8c7b3583b2838c284f7c0a69199..59e2143329929434139540affcc1c16127b2dce7 100644 --- a/server/src/main/resources/file.conf +++ b/server/src/main/resources/file.conf @@ -29,6 +29,9 @@ service { enableDegrade = false #disable disable = false + #unit ms,s,m,h,d represents milliseconds, seconds, minutes, hours, days, default permanent + max.commit.retry.timeout = "-1" + max.rollback.retry.timeout = "-1" } client { @@ -63,10 +66,44 @@ store { ## database store db { - driver_class = "" - url = "" - user = "" - password = "" - } + ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc. + datasource = "dbcp" + ## mysql/oracle/h2/oceanbase etc. + db-type = "mysql" + url = "jdbc:mysql://127.0.0.1:3306/seata" + user = "mysql" + password = "mysql" + min-conn = 1 + max-conn = 3 + global.table = "global_table" + branch.table = "branch_table" + query-limit = 100 + } +} +lock { + ## the data row lock store mode: local_db、memory or db + mode = "memory" + + memory{ + ## store lock in memory of server + } + db{ + ## use db of server to store lock, the db is ${store.db.url} + lock-table= "lock_table" + } + + local_db { + ## store lock in local db + } +} +recovery{ + committing-retry-delay = 5 + asyn-committing-retry-delay = 5 + rollbacking-retry-delay = 5 + timeout-retry-delay = 5 } + +transaction { + undo.data.validation = true +} \ No newline at end of file diff --git a/server/src/main/resources/logback.xml b/server/src/main/resources/logback.xml index 62d9e1acd56c81223fc52e0328e430f78e223c6a..4f2b1d02edc5aa07ea63baf4e05f25281d7015fd 100644 --- a/server/src/main/resources/logback.xml +++ b/server/src/main/resources/logback.xml @@ -41,7 +41,7 @@ </encoder> </appender> - <logger name="io.seata.server.store.FileTransactionStoreManager" additivity="false"> + <logger name="io.seata.server.store.file.FileTransactionStoreManager" additivity="false"> <level value="INFO"/> <appender-ref ref="seata-default"/> </logger> diff --git a/server/src/main/resources/nacos-config.txt b/server/src/main/resources/nacos-config.txt index 254a8db09d4370a1e920dac62759ae76ee73ffb8..b80827982df3026b004d37eca1ad91dd4dc955c5 100644 --- a/server/src/main/resources/nacos-config.txt +++ b/server/src/main/resources/nacos-config.txt @@ -13,6 +13,8 @@ transport.thread-factory.worker-thread-size=8 service.vgroup_mapping.my_test_tx_group=default service.enableDegrade=false service.disable=false +service.max.commit.retry.timeout=-1 +service.max.rollback.retry.timeout=-1 client.async.commit.buffer.limit=10000 client.lock.retry.internal=10 client.lock.retry.times=30 @@ -23,7 +25,21 @@ store.file.max-global-session-size=512 store.file.file-write-buffer-cache-size=16384 store.file.flush-disk-mode=async store.file.session.reload.read_size=100 -store.db.driver_class=com.mysql.jdbc.Driver -store.db.url=jdbc:mysql://localhost:3306/seata_demo -store.db.user=user -store.db.password=password \ No newline at end of file +store.db.datasource=dbcp +store.db.db-type=mysql +store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true +store.db.user=mysql +store.db.password=mysql +store.db.min-conn=1 +store.db.max-conn=3 +store.db.global.table=global_table +store.db.branch.table=branch_table +store.db.query-limit=100 +lock.mode=memory +lock.db.lock-table=lock_table +recovery.committing-retry-delay = 5 +recovery.asyn-committing-retry-delay = 5 +recovery.rollbacking-retry-delay = 5 +recovery.timeout-retry-delay = 5 +transaction.undo.data.validation=true + diff --git a/server/src/main/resources/registry.conf b/server/src/main/resources/registry.conf index 1cddea7219444a8f50ee5bdc7f7170de40884356..66b963d139a7255e99a5212ba47015e831058c44 100644 --- a/server/src/main/resources/registry.conf +++ b/server/src/main/resources/registry.conf @@ -45,7 +45,7 @@ registry { } config { - # file、nacos 、apollo、zk、consul + # file、nacos 、apollo、zk、consul、etcd3 type = "file" nacos { @@ -65,6 +65,9 @@ config { session.timeout = 6000 connect.timeout = 2000 } + etcd3 { + serverAddr = "http://localhost:2379" + } file { name = "file.conf" } diff --git a/server/src/test/java/WriteStoreMultithreadTest.java b/server/src/test/java/WriteStoreMultithreadTest.java index 94eb5c8877069c078feeecb49be2999ec73ce7ff..360759f5b69f78f9b95858b1dc8fcda5cab3ee43 100644 --- a/server/src/test/java/WriteStoreMultithreadTest.java +++ b/server/src/test/java/WriteStoreMultithreadTest.java @@ -20,8 +20,8 @@ import io.seata.server.session.BranchSession; import io.seata.server.session.GlobalSession; import io.seata.server.session.SessionCondition; import io.seata.server.session.SessionManager; -import io.seata.server.store.FileTransactionStoreManager; import io.seata.server.store.TransactionStoreManager; +import io.seata.server.store.file.FileTransactionStoreManager; import java.util.ArrayList; import java.util.Collection; @@ -55,10 +55,11 @@ public class WriteStoreMultithreadTest { } @Override - public GlobalSession findGlobalSession(Long transactionId) { + public GlobalSession findGlobalSession(String xid) { return null; } + @Override public void updateGlobalSessionStatus(GlobalSession session, GlobalStatus status) throws TransactionException { diff --git a/server/src/test/java/WriteStoreTest.java b/server/src/test/java/WriteStoreTest.java index 9f9964d1b868156b382d7dd3accf1f971e8b9d69..d2056f75f63421b0bd322368d4f5a8a08cbbf512 100644 --- a/server/src/test/java/WriteStoreTest.java +++ b/server/src/test/java/WriteStoreTest.java @@ -27,11 +27,13 @@ import io.seata.server.session.BranchSession; import io.seata.server.session.GlobalSession; import io.seata.server.session.SessionCondition; import io.seata.server.session.SessionManager; -import io.seata.server.store.FileTransactionStoreManager; +import io.seata.server.store.ReloadableStore; import io.seata.server.store.SessionStorable; import io.seata.server.store.TransactionStoreManager; import io.seata.server.store.TransactionStoreManager.LogOperation; import io.seata.server.store.TransactionWriteStore; +import io.seata.server.store.file.FileTransactionStoreManager; + /** * The type Write store test. @@ -71,7 +73,7 @@ public class WriteStoreTest { } @Override - public GlobalSession findGlobalSession(Long transactionId) { + public GlobalSession findGlobalSession(String xid) { return null; } @@ -220,8 +222,8 @@ public class WriteStoreTest { private static Map<SessionStorable, LogOperation> readAll(TransactionStoreManager transactionStoreManager) { Map<SessionStorable, LogOperation> resultMap = new HashMap<>(65535 * 5 * 9); - while (transactionStoreManager.hasRemaining(true)) { - List<TransactionWriteStore> transactionWriteStores = transactionStoreManager.readWriteStoreFromFile(2000, + while (((ReloadableStore)transactionStoreManager).hasRemaining(true)) { + List<TransactionWriteStore> transactionWriteStores = ((ReloadableStore)transactionStoreManager).readWriteStore(2000, true); if (null != transactionWriteStores) { for (TransactionWriteStore transactionWriteStore : transactionWriteStores) { @@ -230,8 +232,8 @@ public class WriteStoreTest { } } } - while (transactionStoreManager.hasRemaining(false)) { - List<TransactionWriteStore> transactionWriteStores = transactionStoreManager.readWriteStoreFromFile(2000, + while (((ReloadableStore)transactionStoreManager).hasRemaining(false)) { + List<TransactionWriteStore> transactionWriteStores = ((ReloadableStore)transactionStoreManager).readWriteStore(2000, false); if (null != transactionWriteStores) { for (TransactionWriteStore transactionWriteStore : transactionWriteStores) { diff --git a/server/src/test/java/io/seata/server/coordinator/DefaultCoordinatorTest.java b/server/src/test/java/io/seata/server/coordinator/DefaultCoordinatorTest.java index d68dcf16bb5cd4622c9f1df36e7fc1fe1ec07b0b..d5a03ece98313ab5f94c451669d2a646f9ca9fa5 100644 --- a/server/src/test/java/io/seata/server/coordinator/DefaultCoordinatorTest.java +++ b/server/src/test/java/io/seata/server/coordinator/DefaultCoordinatorTest.java @@ -31,6 +31,8 @@ import io.seata.server.session.SessionHolder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + import org.junit.jupiter.api.Disabled; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -76,7 +78,7 @@ public class DefaultCoordinatorTest { SessionHolder.init(null); serverMessageSender = new MockServerMessageSender(); defaultCoordinator = new DefaultCoordinator(serverMessageSender); - defaultCoordinator.init(); +// defaultCoordinator.init(); } @ParameterizedTest @@ -105,6 +107,23 @@ public class DefaultCoordinatorTest { Assertions.assertEquals(result, BranchStatus.PhaseTwo_Rollbacked); } + + @Test + public void test_handleRetryRollbacking() throws TransactionException, InterruptedException { + + String xid = core.begin(applicationId, txServiceGroup, txName, 10); + Long branchId = core.branchRegister(BranchType.AT, "abcd", clientId, xid, applicationData, lockKeys_2); + + Thread.sleep(100); + + defaultCoordinator.timeoutCheck(); + defaultCoordinator.handleRetryRollbacking(); + + GlobalSession globalSession = SessionHolder.findGlobalSession(xid); + Assertions.assertNull(globalSession); + + } + @AfterAll public static void afterClass() throws Exception { @@ -164,4 +183,4 @@ public class DefaultCoordinatorTest { } } -} +} \ No newline at end of file diff --git a/server/src/test/java/io/seata/server/coordinator/DefaultCoreTest.java b/server/src/test/java/io/seata/server/coordinator/DefaultCoreTest.java index 42b33e4ff5a4baf8ec36e0bfb4dc7b4b42b3c7a7..8569001e89a9ed038f70147c035337c860673ae4 100644 --- a/server/src/test/java/io/seata/server/coordinator/DefaultCoreTest.java +++ b/server/src/test/java/io/seata/server/coordinator/DefaultCoreTest.java @@ -23,6 +23,7 @@ import io.seata.core.model.GlobalStatus; import io.seata.core.model.ResourceManagerInbound; import io.seata.server.session.BranchSession; import io.seata.server.session.GlobalSession; +import io.seata.server.session.SessionHelper; import io.seata.server.session.SessionHolder; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; @@ -84,7 +85,7 @@ public class DefaultCoreTest { public void branchRegisterTest(String xid) throws Exception { core.branchRegister(BranchType.AT, resourceId, clientId, xid, "abc", lockKeys_1); long transactionId = XID.getTransactionId(xid); - GlobalSession globalSession = SessionHolder.findGlobalSession(transactionId); + GlobalSession globalSession = SessionHolder.findGlobalSession(xid); Assertions.assertEquals(globalSession.getSortedBranches().size(), 1); //clear @@ -103,7 +104,7 @@ public class DefaultCoreTest { public void branchReportTest(String xid, Long branchId) throws Exception { core.branchReport(BranchType.AT, xid, branchId, BranchStatus.PhaseOne_Done, applicationData); long transactionId = XID.getTransactionId(xid); - GlobalSession globalSession = SessionHolder.findGlobalSession(transactionId); + GlobalSession globalSession = SessionHolder.findGlobalSession(xid); BranchSession branchSession = globalSession.getBranch(branchId); Assertions.assertEquals(branchSession.getStatus(), BranchStatus.PhaseOne_Done); @@ -120,8 +121,12 @@ public class DefaultCoreTest { public void beginTest() throws Exception { String xid = core.begin(applicationId, txServiceGroup, txName, timeout); long transactionId = XID.getTransactionId(xid); - GlobalSession globalSession = SessionHolder.findGlobalSession(transactionId); + GlobalSession globalSession = SessionHolder.findGlobalSession(xid); Assertions.assertNotNull(globalSession); + + //clear + globalSession.end(); + } /** @@ -146,8 +151,9 @@ public class DefaultCoreTest { @ParameterizedTest @MethodSource("xidProvider") public void doGlobalCommitCommitTest(String xid) throws Exception { - GlobalSession globalSession = SessionHolder.findGlobalSession(XID.getTransactionId(xid)); - BranchSession branchSession = new BranchSession(); + GlobalSession globalSession = SessionHolder.findGlobalSession(xid); + BranchSession branchSession = SessionHelper.newBranchByGlobal(globalSession, BranchType.AT, resourceId, + applicationData, "t1:1", clientId); globalSession.addBranch(branchSession); globalSession.changeBranchStatus(branchSession, BranchStatus.PhaseOne_Done); core.setResourceManagerInbound(new MockResourceManagerInbound(BranchStatus.PhaseTwo_Committed, BranchStatus.PhaseOne_Done)); @@ -168,8 +174,9 @@ public class DefaultCoreTest { @ParameterizedTest @MethodSource("xidProvider") public void doGlobalCommitUnretryableTest(String xid) throws Exception { - GlobalSession globalSession = SessionHolder.findGlobalSession(XID.getTransactionId(xid)); - BranchSession branchSession = new BranchSession(); + GlobalSession globalSession = SessionHolder.findGlobalSession(xid); + BranchSession branchSession = SessionHelper.newBranchByGlobal(globalSession, BranchType.AT, resourceId, + applicationData, "t1:1", clientId); globalSession.addBranch(branchSession); globalSession.changeBranchStatus(branchSession, BranchStatus.PhaseOne_Done); core.setResourceManagerInbound(new MockResourceManagerInbound(BranchStatus.PhaseTwo_CommitFailed_Unretryable, BranchStatus.PhaseOne_Done)); @@ -189,8 +196,9 @@ public class DefaultCoreTest { @ParameterizedTest @MethodSource("xidProvider") public void doGlobalCommitExpTest(String xid) throws Exception { - GlobalSession globalSession = SessionHolder.findGlobalSession(XID.getTransactionId(xid)); - BranchSession branchSession = new BranchSession(); + GlobalSession globalSession = SessionHolder.findGlobalSession(xid); + BranchSession branchSession = SessionHelper.newBranchByGlobal(globalSession, BranchType.AT, resourceId, + applicationData, "t1:1", clientId); globalSession.addBranch(branchSession); globalSession.changeBranchStatus(branchSession, BranchStatus.PhaseOne_Done); core.setResourceManagerInbound(new MockResourceManagerInbound(BranchStatus.PhaseOne_Timeout, BranchStatus.PhaseOne_Done)); @@ -223,8 +231,9 @@ public class DefaultCoreTest { @ParameterizedTest @MethodSource("xidProvider") public void doGlobalRollBackRollbackedTest(String xid) throws Exception { - GlobalSession globalSession = SessionHolder.findGlobalSession(XID.getTransactionId(xid)); - BranchSession branchSession = new BranchSession(); + GlobalSession globalSession = SessionHolder.findGlobalSession(xid); + BranchSession branchSession = SessionHelper.newBranchByGlobal(globalSession, BranchType.AT, resourceId, + applicationData, "t1:1", clientId); globalSession.addBranch(branchSession); globalSession.changeBranchStatus(branchSession, BranchStatus.PhaseOne_Done); core.setResourceManagerInbound(new MockResourceManagerInbound(BranchStatus.PhaseTwo_Committed, BranchStatus.PhaseTwo_Rollbacked)); @@ -245,8 +254,9 @@ public class DefaultCoreTest { @ParameterizedTest @MethodSource("xidProvider") public void doGlobalRollBackUnretryableTest(String xid) throws Exception { - GlobalSession globalSession = SessionHolder.findGlobalSession(XID.getTransactionId(xid)); - BranchSession branchSession = new BranchSession(); + GlobalSession globalSession = SessionHolder.findGlobalSession(xid); + BranchSession branchSession = SessionHelper.newBranchByGlobal(globalSession, BranchType.AT, resourceId, + applicationData, "t1:1", clientId); globalSession.addBranch(branchSession); globalSession.changeBranchStatus(branchSession, BranchStatus.PhaseOne_Done); core.setResourceManagerInbound(new MockResourceManagerInbound(BranchStatus.PhaseTwo_Committed, BranchStatus.PhaseTwo_RollbackFailed_Unretryable)); @@ -266,14 +276,16 @@ public class DefaultCoreTest { @ParameterizedTest @MethodSource("xidProvider") public void doGlobalRollBackRetryableExpTest(String xid) throws Exception { - GlobalSession globalSession = SessionHolder.findGlobalSession(XID.getTransactionId(xid)); - BranchSession branchSession = new BranchSession(); + GlobalSession globalSession = SessionHolder.findGlobalSession(xid); + BranchSession branchSession = SessionHelper.newBranchByGlobal(globalSession, BranchType.AT, resourceId, + applicationData, "t1:1", clientId); globalSession.addBranch(branchSession); globalSession.changeBranchStatus(branchSession, BranchStatus.PhaseOne_Done); core.setResourceManagerInbound(new MockResourceManagerInbound(BranchStatus.PhaseTwo_Committed, BranchStatus.PhaseTwo_RollbackFailed_Retryable)); core.doGlobalRollback(globalSession, false); Assertions.assertEquals(globalSession.getStatus(), GlobalStatus.RollbackRetrying); + //clear globalSession.end(); } diff --git a/server/src/test/java/io/seata/server/lock/LockManagerTest.java b/server/src/test/java/io/seata/server/lock/LockManagerTest.java index 31084b1358ae24226a769d6dd89ae289e627713d..f420ebe7635d2da392938b0ac11df82ad222ed8c 100644 --- a/server/src/test/java/io/seata/server/lock/LockManagerTest.java +++ b/server/src/test/java/io/seata/server/lock/LockManagerTest.java @@ -17,6 +17,7 @@ package io.seata.server.lock; import io.seata.core.model.BranchType; import io.seata.server.UUIDGenerator; +import io.seata.server.lock.memory.MemoryLockManagerForTest; import io.seata.server.session.BranchSession; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.params.ParameterizedTest; @@ -42,7 +43,7 @@ public class LockManagerTest { @ParameterizedTest @MethodSource("branchSessionProvider") public void acquireLock_success(BranchSession branchSession) throws Exception { - LockManager lockManager = LockManagerFactory.get(); + LockManager lockManager = new MemoryLockManagerForTest(); Assertions.assertTrue(lockManager.acquireLock(branchSession)); } @@ -56,7 +57,7 @@ public class LockManagerTest { @ParameterizedTest @MethodSource("branchSessionsProvider") public void acquireLock_failed(BranchSession branchSession1, BranchSession branchSession2) throws Exception { - LockManager lockManager = LockManagerFactory.get(); + LockManager lockManager = new MemoryLockManagerForTest(); Assertions.assertTrue(lockManager.acquireLock(branchSession1)); Assertions.assertFalse(lockManager.acquireLock(branchSession2)); } @@ -71,13 +72,13 @@ public class LockManagerTest { @MethodSource("branchSessionProvider") public void isLockableTest(BranchSession branchSession) throws Exception { branchSession.setLockKey("t:4"); - LockManager lockManager = LockManagerFactory.get(); + LockManager lockManager = new MemoryLockManagerForTest(); Assertions.assertTrue(lockManager - .isLockable(branchSession.getTransactionId(), branchSession.getResourceId(), branchSession.getLockKey())); + .isLockable(branchSession.getXid(), branchSession.getResourceId(), branchSession.getLockKey())); lockManager.acquireLock(branchSession); branchSession.setTransactionId(UUIDGenerator.generateUUID()); Assertions.assertFalse(lockManager - .isLockable(branchSession.getTransactionId(), branchSession.getResourceId(), branchSession.getLockKey())); + .isLockable(branchSession.getXid(), branchSession.getResourceId(), branchSession.getLockKey())); } /** diff --git a/server/src/test/java/io/seata/server/lock/db/DataBaseLockManagerImplTest.java b/server/src/test/java/io/seata/server/lock/db/DataBaseLockManagerImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f11485d64083f98b05aede67b13d239475b05288 --- /dev/null +++ b/server/src/test/java/io/seata/server/lock/db/DataBaseLockManagerImplTest.java @@ -0,0 +1,301 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.lock.db; + +import io.seata.core.exception.TransactionException; +import io.seata.core.lock.Locker; +import io.seata.core.store.db.LockStoreDataBaseDAO; +import io.seata.server.lock.DefaultLockManager; +import io.seata.server.lock.LockManager; +import io.seata.server.session.BranchSession; +import org.apache.commons.dbcp.BasicDataSource; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Assertions; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + + + +/** + * @author zhangsen + * @data 2019/4/28 + */ +public class DataBaseLockManagerImplTest { + + static LockManager lockManager = null; + + static BasicDataSource dataSource = null; + + static LockStoreDataBaseDAO dataBaseLockStoreDAO = null; + + @BeforeAll + public static void start(){ + dataSource = new BasicDataSource(); + dataSource.setDriverClassName("org.h2.Driver"); + dataSource.setUrl("jdbc:h2:./db_store/db_lock"); + dataSource.setUsername("sa"); + dataSource.setPassword(""); + + dataBaseLockStoreDAO = new LockStoreDataBaseDAO(dataSource); + dataBaseLockStoreDAO.setDbType("h2"); + dataBaseLockStoreDAO.setLockTable("lock_table"); + + lockManager = new DBLockManagerForTest(dataBaseLockStoreDAO); + + prepareTable(dataSource); + } + + private static void prepareTable(BasicDataSource dataSource) { + Connection conn = null; + try { + conn = dataSource.getConnection(); + Statement s = conn.createStatement(); + try { + s.execute("drop table lock_table"); + } catch (Exception e) { + } + s.execute("CREATE TABLE lock_table ( xid varchar(96), transaction_id long , branch_id long, resource_id varchar(32) ,table_name varchar(32) ,pk varchar(32) , row_key varchar(128) primary key not null, gmt_create TIMESTAMP(6) ,gmt_modified TIMESTAMP(6)) "); + System.out.println("create table lock_table success."); + + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (conn != null) { + try { + conn.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + } + } + + @Test + public void acquireLock() throws TransactionException, SQLException { + BranchSession branchSession = new BranchSession(); + branchSession.setXid("abc-123:786756"); + branchSession.setTransactionId(123543465); + branchSession.setBranchId(5756678); + branchSession.setResourceId("abcss"); + branchSession.setLockKey("t1:13,14;t2:11,12"); + + Assertions.assertTrue(lockManager.acquireLock(branchSession)); + + String sql = "select * from lock_table where xid = 'abc-123:786756'" ; + String sql2 = "select count(*) from lock_table where xid = 'abc-123:786756' " + + "and row_key in ('abcss^^^t1^^^13', 'abcss^^^t1^^^14', 'abcss^^^t2^^^11', 'abcss^^^t2^^^12')" ; + String delSql = "delete from lock_table where xid = 'abc-123:786756'" ; + Connection conn = null; + try { + conn = dataSource.getConnection(); + + ResultSet rs = conn.createStatement().executeQuery(sql); + if(rs.next()){ + Assertions.assertTrue(true); + }else { + Assertions.assertTrue(false); + } + rs.close(); + + rs = conn.createStatement().executeQuery(sql2); + if(rs.next()){ + Assertions.assertTrue(true); + Assertions.assertEquals(4, rs.getInt(1)); + }else { + Assertions.assertTrue(false); + } + rs.close(); + + conn.createStatement().execute(delSql); + } finally { + if(conn != null){ + try { + conn.close(); + } catch (SQLException e) { + } + } + } + } + + @Test + public void re_acquireLock() throws TransactionException, SQLException { + BranchSession branchSession = new BranchSession(); + branchSession.setXid("abc-123:65867978"); + branchSession.setTransactionId(123543465); + branchSession.setBranchId(5756678); + branchSession.setResourceId("abcss"); + branchSession.setLockKey("t1:53,54;t2:21,32"); + + Assertions.assertTrue(lockManager.acquireLock(branchSession)); + + BranchSession branchSession2 = new BranchSession(); + branchSession2.setXid("abc-123:65867978"); + branchSession2.setTransactionId(123543465); + branchSession2.setBranchId(575667854); + branchSession2.setResourceId("abcss"); + branchSession2.setLockKey("t1:13,14;t2:21,45"); + + Assertions.assertTrue(lockManager.acquireLock(branchSession2)); + + BranchSession branchSession3 = new BranchSession(); + branchSession3.setXid("abc-123:5678789"); + branchSession3.setTransactionId(334123); + branchSession3.setBranchId(5657); + branchSession3.setResourceId("abcss"); + branchSession3.setLockKey("t1:53,14;t2:21,45"); + + Assertions.assertTrue(!lockManager.acquireLock(branchSession3)); + + String delSql = "delete from lock_table where xid in( 'abc-123:65867978' , 'abc-123:65867978' , 'abc-123:5678789' )" ; + Connection conn = null; + try { + conn = dataSource.getConnection(); + + conn.createStatement().execute(delSql); + } finally { + if(conn != null){ + try { + conn.close(); + } catch (SQLException e) { + } + } + } + } + + @Test + public void unLock() throws TransactionException, SQLException { + BranchSession branchSession = new BranchSession(); + branchSession.setXid("abc-123:56867"); + branchSession.setTransactionId(1236765); + branchSession.setBranchId(204565); + branchSession.setResourceId("abcss"); + branchSession.setLockKey("t1:3,4;t2:4,5"); + + Assertions.assertTrue(lockManager.acquireLock(branchSession)); + + String sql = "select * from lock_table where xid = 'abc-123:56867'" ; + String sql2 = "select count(*) from lock_table where xid = 'abc-123:56867' " + + "and row_key in ('abcss^^^t1^^^3', 'abcss^^^t1^^^4', 'abcss^^^t2^^^4', 'abcss^^^t2^^^5')" ; + Connection conn = null; + try { + conn = dataSource.getConnection(); + + ResultSet rs = conn.createStatement().executeQuery(sql); + if(rs.next()){ + Assertions.assertTrue(true); + }else { + Assertions.assertTrue(false); + } + rs.close(); + + rs = conn.createStatement().executeQuery(sql2); + if(rs.next()){ + Assertions.assertTrue(true); + Assertions.assertEquals(4, rs.getInt(1)); + }else { + Assertions.assertTrue(false); + } + rs.close(); + + //un lock + Assertions.assertTrue(lockManager.releaseLock(branchSession)); + + rs = conn.createStatement().executeQuery(sql); + if(rs.next()){ + Assertions.assertTrue(false); + }else { + Assertions.assertTrue(true); + } + rs.close(); + + } finally { + if(conn != null){ + try { + conn.close(); + } catch (SQLException e) { + } + } + } + + + + } + + @Test + public void isLockable() throws TransactionException, SQLException { + BranchSession branchSession = new BranchSession(); + branchSession.setXid("abc-123:56877898"); + branchSession.setTransactionId(245686786); + branchSession.setBranchId(467568); + branchSession.setResourceId("abcss"); + branchSession.setLockKey("t1:8,7;t2:1,2"); + + Assertions.assertTrue(lockManager.acquireLock(branchSession)); + + BranchSession branchSession2 = new BranchSession(); + branchSession2.setXid("abc-123:56877898"); + branchSession2.setTransactionId(245686786); + branchSession2.setBranchId(1242354576); + branchSession2.setResourceId("abcss"); + branchSession2.setLockKey("t1:8"); + + Assertions.assertTrue(lockManager.isLockable(branchSession2.getXid(), branchSession2.getResourceId(), branchSession2.getLockKey())); + + BranchSession branchSession3 = new BranchSession(); + branchSession3.setXid("abc-123:4575614354"); + branchSession3.setTransactionId(65867867); + branchSession3.setBranchId(123123); + branchSession3.setResourceId("abcss"); + branchSession3.setLockKey("t2:1,12"); + + Assertions.assertTrue(!lockManager.isLockable(branchSession3.getXid(), branchSession3.getResourceId(), branchSession3.getLockKey())); + + String delSql = "delete from lock_table where xid in( 'abc-123:56877898' , 'abc-123:56877898' , 'abc-123:4575614354' )" ; + Connection conn = null; + try { + conn = dataSource.getConnection(); + + conn.createStatement().execute(delSql); + } finally { + if(conn != null){ + try { + conn.close(); + } catch (SQLException e) { + } + } + } + } + + public static class DBLockManagerForTest extends DefaultLockManager { + + protected LockStoreDataBaseDAO lockStore; + + public DBLockManagerForTest(LockStoreDataBaseDAO db){ + lockStore = db; + } + + @Override + protected Locker getLocker(BranchSession branchSession) { + DataBaseLocker locker = new DataBaseLocker(); + locker.setLockStore(lockStore); + return locker; + } + } +} \ No newline at end of file diff --git a/server/src/test/java/io/seata/server/lock/memory/MemoryLockManagerForTest.java b/server/src/test/java/io/seata/server/lock/memory/MemoryLockManagerForTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1bdd7c0c0b7a9cbecbafac9e7ccb8b1d653f63ff --- /dev/null +++ b/server/src/test/java/io/seata/server/lock/memory/MemoryLockManagerForTest.java @@ -0,0 +1,32 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.lock.memory; + +import io.seata.core.lock.Locker; +import io.seata.server.lock.DefaultLockManager; +import io.seata.server.session.BranchSession; + +/** + * @author zhangsen + * @data 2019-05-16 + */ +public class MemoryLockManagerForTest extends DefaultLockManager { + + @Override + protected Locker getLocker(BranchSession branchSession) { + return new MemoryLocker(branchSession); + } +} diff --git a/server/src/test/java/io/seata/server/lock/DefaultLockManagerImplTest.java b/server/src/test/java/io/seata/server/lock/memory/MemoryLockManagerImplTest.java similarity index 87% rename from server/src/test/java/io/seata/server/lock/DefaultLockManagerImplTest.java rename to server/src/test/java/io/seata/server/lock/memory/MemoryLockManagerImplTest.java index fbe0c216b420e65f1bad9de08a3210277ef004c7..7bf4148174e1d295843a5625ca73a3c9cb9f4f30 100644 --- a/server/src/test/java/io/seata/server/lock/DefaultLockManagerImplTest.java +++ b/server/src/test/java/io/seata/server/lock/memory/MemoryLockManagerImplTest.java @@ -13,10 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.seata.server.lock; +package io.seata.server.lock.memory; +import io.seata.common.XID; import io.seata.core.model.BranchType; import io.seata.server.UUIDGenerator; +import io.seata.server.lock.LockManager; import io.seata.server.session.BranchSession; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -25,15 +27,16 @@ import org.junit.jupiter.params.provider.MethodSource; import java.util.stream.Stream; + /** * The type Default lock manager impl test. * * @author zhimo.xiao @gmail.com * @since 2019 /1/23 */ -public class DefaultLockManagerImplTest { +public class MemoryLockManagerImplTest { - private LockManager lockManager = new DefaultLockManagerImpl(); + private LockManager lockManager = new MemoryLockManagerForTest();; private static final long transactionId = UUIDGenerator.generateUUID(); @@ -50,6 +53,7 @@ public class DefaultLockManagerImplTest { @ParameterizedTest @MethodSource("branchSessionProvider") public void acquireLockTest(BranchSession branchSession) throws Exception { + boolean result = lockManager.acquireLock(branchSession); Assertions.assertTrue(result); branchSession.unlock(); @@ -62,7 +66,8 @@ public class DefaultLockManagerImplTest { */ @Test public void isLockableTest() throws Exception { - boolean resultOne = lockManager.isLockable(transactionId, resourceId, lockKey); + boolean resultOne = lockManager.isLockable(XID.generateXID(transactionId), resourceId, lockKey); + Assertions.assertTrue(resultOne); } @@ -73,6 +78,7 @@ public class DefaultLockManagerImplTest { */ static Stream<BranchSession> branchSessionProvider() { BranchSession branchSession = new BranchSession(); + branchSession.setXid(XID.generateXID(transactionId)); branchSession.setBranchId(1L); branchSession.setTransactionId(transactionId); branchSession.setClientId("c1"); diff --git a/server/src/test/java/io/seata/server/session/DefaultSessionManagerTest.java b/server/src/test/java/io/seata/server/session/DefaultSessionManagerTest.java index 33544245aa50756ecc6782aeb03dc29360cc7e65..69b12a0c2473bc3aba80c410ea3b771e8f84296f 100644 --- a/server/src/test/java/io/seata/server/session/DefaultSessionManagerTest.java +++ b/server/src/test/java/io/seata/server/session/DefaultSessionManagerTest.java @@ -15,6 +15,7 @@ */ package io.seata.server.session; +import io.seata.common.XID; import io.seata.core.model.BranchStatus; import io.seata.core.model.BranchType; import io.seata.core.model.GlobalStatus; @@ -61,7 +62,7 @@ public class DefaultSessionManagerTest { @MethodSource("globalSessionProvider") public void findGlobalSessionTest(GlobalSession globalSession) throws Exception { sessionManager.addGlobalSession(globalSession); - GlobalSession expected = sessionManager.findGlobalSession(globalSession.getTransactionId()); + GlobalSession expected = sessionManager.findGlobalSession(globalSession.getXid()); Assertions.assertNotNull(expected); Assertions.assertEquals(expected.getTransactionId(), globalSession.getTransactionId()); Assertions.assertEquals(expected.getApplicationId(), globalSession.getApplicationId()); @@ -84,7 +85,7 @@ public class DefaultSessionManagerTest { sessionManager.addGlobalSession(globalSession); globalSession.setStatus(GlobalStatus.Finished); sessionManager.updateGlobalSessionStatus(globalSession, GlobalStatus.Finished); - GlobalSession expected = sessionManager.findGlobalSession(globalSession.getTransactionId()); + GlobalSession expected = sessionManager.findGlobalSession(globalSession.getXid()); Assertions.assertNotNull(expected); Assertions.assertEquals(GlobalStatus.Finished, expected.getStatus()); sessionManager.removeGlobalSession(globalSession); @@ -101,7 +102,7 @@ public class DefaultSessionManagerTest { public void removeGlobalSessionTest(GlobalSession globalSession) throws Exception { sessionManager.addGlobalSession(globalSession); sessionManager.removeGlobalSession(globalSession); - GlobalSession expected = sessionManager.findGlobalSession(globalSession.getTransactionId()); + GlobalSession expected = sessionManager.findGlobalSession(globalSession.getXid()); Assertions.assertNull(expected); } @@ -301,6 +302,10 @@ public class DefaultSessionManagerTest { */ static Stream<Arguments> globalSessionProvider() { GlobalSession globalSession = new GlobalSession("demo-app", "my_test_tx_group", "test", 6000); + + String xid = XID.generateXID(globalSession.getTransactionId()); + globalSession.setXid(xid); + return Stream.of( Arguments.of(globalSession) ); diff --git a/server/src/test/java/io/seata/server/session/FileBasedSessionManagerTest.java b/server/src/test/java/io/seata/server/session/FileBasedSessionManagerTest.java index 803f1200c47376936c91e23e91ddbf041a3e6674..7c58a309b89be3df875be9b04f771eece58b5083 100644 --- a/server/src/test/java/io/seata/server/session/FileBasedSessionManagerTest.java +++ b/server/src/test/java/io/seata/server/session/FileBasedSessionManagerTest.java @@ -15,6 +15,11 @@ */ package io.seata.server.session; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import io.seata.common.XID; import io.seata.core.model.BranchStatus; import io.seata.core.model.BranchType; import io.seata.core.model.GlobalStatus; @@ -24,11 +29,10 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; +import io.seata.server.session.file.FileBasedSessionManager; import java.util.stream.Stream; + /** * The type File based session manager test. * @@ -72,7 +76,7 @@ public class FileBasedSessionManagerTest { @MethodSource("globalSessionProvider") public void findGlobalSessionTest(GlobalSession globalSession) throws Exception { sessionManager.addGlobalSession(globalSession); - GlobalSession expected = sessionManager.findGlobalSession(globalSession.getTransactionId()); + GlobalSession expected = sessionManager.findGlobalSession(globalSession.getXid()); Assertions.assertNotNull(expected); Assertions.assertEquals(expected.getTransactionId(), globalSession.getTransactionId()); Assertions.assertEquals(expected.getApplicationId(), globalSession.getApplicationId()); @@ -95,7 +99,7 @@ public class FileBasedSessionManagerTest { sessionManager.addGlobalSession(globalSession); globalSession.setStatus(GlobalStatus.Finished); sessionManager.updateGlobalSessionStatus(globalSession, GlobalStatus.Finished); - GlobalSession expected = sessionManager.findGlobalSession(globalSession.getTransactionId()); + GlobalSession expected = sessionManager.findGlobalSession(globalSession.getXid()); Assertions.assertNotNull(expected); Assertions.assertEquals(GlobalStatus.Finished, expected.getStatus()); sessionManager.removeGlobalSession(globalSession); @@ -112,7 +116,7 @@ public class FileBasedSessionManagerTest { public void removeGlobalSessionTest(GlobalSession globalSession) throws Exception { sessionManager.addGlobalSession(globalSession); sessionManager.removeGlobalSession(globalSession); - GlobalSession expected = sessionManager.findGlobalSession(globalSession.getTransactionId()); + GlobalSession expected = sessionManager.findGlobalSession(globalSession.getXid()); Assertions.assertNull(expected); } @@ -312,6 +316,10 @@ public class FileBasedSessionManagerTest { */ static Stream<Arguments> globalSessionProvider() { GlobalSession globalSession = new GlobalSession("demo-app", "my_test_tx_group", "test", 6000); + + String xid = XID.generateXID(globalSession.getTransactionId()); + globalSession.setXid(xid); + return Stream.of( Arguments.of(globalSession) ); @@ -337,6 +345,7 @@ public class FileBasedSessionManagerTest { */ static Stream<Arguments> branchSessionProvider() { GlobalSession globalSession = new GlobalSession("demo-app", "my_test_tx_group", "test", 6000); + globalSession.setXid(XID.generateXID(globalSession.getTransactionId())); BranchSession branchSession = new BranchSession(); branchSession.setTransactionId(globalSession.getTransactionId()); branchSession.setBranchId(1L); diff --git a/server/src/test/java/io/seata/server/session/db/DataBaseSessionManagerTest.java b/server/src/test/java/io/seata/server/session/db/DataBaseSessionManagerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..14b04c30b20e6e65200ffb38167335fdd178efc4 --- /dev/null +++ b/server/src/test/java/io/seata/server/session/db/DataBaseSessionManagerTest.java @@ -0,0 +1,565 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.seata.server.session.db; + +import io.seata.common.XID; +import io.seata.core.exception.TransactionException; +import io.seata.core.model.BranchStatus; +import io.seata.core.model.BranchType; +import io.seata.core.model.GlobalStatus; +import io.seata.core.store.db.LogStoreDataBaseDAO; +import io.seata.server.UUIDGenerator; +import io.seata.server.session.BranchSession; +import io.seata.server.session.GlobalSession; +import io.seata.server.session.SessionCondition; +import io.seata.server.session.SessionManager; +import io.seata.server.store.db.DatabaseTransactionStoreManager; +import org.apache.commons.dbcp.BasicDataSource; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Assertions; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collection; + +/** + * The type Data base session manager test. + * + * @author zhangsen + * @data 2019 /4/28 + */ +public class DataBaseSessionManagerTest { + + static SessionManager sessionManager = null; + + static LogStoreDataBaseDAO logStoreDataBaseDAO = null; + + static BasicDataSource dataSource = null; + + @BeforeAll + public static void start() throws Exception { + DataBaseSessionManager tempSessionManager = new DataBaseSessionManager(); + DatabaseTransactionStoreManager transactionStoreManager = new DatabaseTransactionStoreManager(); + + dataSource = new BasicDataSource(); + dataSource.setDriverClassName("org.h2.Driver"); + dataSource.setUrl("jdbc:h2:./db_store/db_session"); + dataSource.setUsername("sa"); + dataSource.setPassword(""); + + logStoreDataBaseDAO = new LogStoreDataBaseDAO(dataSource); + logStoreDataBaseDAO.setDbType("h2"); + logStoreDataBaseDAO.setGlobalTable("global_table"); + logStoreDataBaseDAO.setBrachTable("branch_table"); + + transactionStoreManager.setLogQueryLimit(100); + transactionStoreManager.setLogStore(logStoreDataBaseDAO); + + tempSessionManager.setTransactionStoreManager(transactionStoreManager); + sessionManager = tempSessionManager; + + prepareTable(dataSource); + } + + private static void prepareTable(BasicDataSource dataSource) { + Connection conn = null; + try { + conn = dataSource.getConnection(); + Statement s = conn.createStatement(); + try { + s.execute("drop table global_table"); + } catch (Exception e) { + } + s.execute("CREATE TABLE global_table ( xid varchar(96), transaction_id long , STATUS int, application_id varchar(32), transaction_service_group varchar(32) ,transaction_name varchar(32) ,timeout int, begin_time long, application_data varchar(500), gmt_create TIMESTAMP(6) ,gmt_modified TIMESTAMP(6) ) "); + System.out.println("create table global_table success."); + + try { + s.execute("drop table branch_table"); + } catch (Exception e) { + } + s.execute("CREATE TABLE branch_table ( xid varchar(96), transaction_id long , branch_id long, resource_group_id varchar(32), resource_id varchar(32) ,lock_key varchar(64) ,branch_type varchar(32) , status int , client_id varchar(128), application_data varchar(500), gmt_create TIMESTAMP(6) ,gmt_modified TIMESTAMP(6) ) "); + System.out.println("create table branch_table success."); + + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (conn != null) { + try { + conn.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + } + } + + + @Test + public void test_addGlobalSession() throws TransactionException, SQLException { + GlobalSession session = GlobalSession.createGlobalSession("test", + "test", "test123", 100); + String xid = XID.generateXID(session.getTransactionId()); + session.setXid(xid); + session.setTransactionId(146757978); + session.setBeginTime(System.currentTimeMillis()); + session.setApplicationData("abc=878s"); + session.setStatus(GlobalStatus.Begin); + + sessionManager.addGlobalSession(session); + + String sql = "select * from global_table where xid= '"+xid+"'"; + String delSql = "delete from global_table where xid= '"+xid+"'"; + Connection conn = null; + try{ + conn = dataSource.getConnection(); + ResultSet rs = conn.createStatement().executeQuery(sql); + if(rs.next()){ + Assertions.assertTrue(true); + }else{ + Assertions.assertTrue(false); + } + + conn.createStatement().execute(delSql); + }finally { + if(conn != null){ + conn.close(); + } + } + } + + + @Test + public void test_updateGlobalSessionStatus() throws TransactionException, SQLException { + GlobalSession session = GlobalSession.createGlobalSession("test", + "test", "test123", 100); + String xid = XID.generateXID(session.getTransactionId()); + session.setXid(xid); + session.setTransactionId(146757978); + session.setBeginTime(System.currentTimeMillis()); + session.setApplicationData("abc=878s"); + session.setStatus(GlobalStatus.Begin); + + sessionManager.addGlobalSession(session); + + session.setStatus(GlobalStatus.Committing); + sessionManager.updateGlobalSessionStatus(session, GlobalStatus.Committing); + + String sql = "select * from global_table where xid= '"+xid+"'"; + String delSql = "delete from global_table where xid= '"+xid+"'"; + Connection conn = null; + try{ + conn = dataSource.getConnection(); + ResultSet rs = conn.createStatement().executeQuery(sql); + if(rs.next()){ + Assertions.assertTrue(true); + Assertions.assertEquals(rs.getInt("status"), GlobalStatus.Committing.getCode()); + }else{ + Assertions.assertTrue(false); + } + + conn.createStatement().execute(delSql); + }finally { + if(conn != null){ + conn.close(); + } + } + } + + @Test + public void test_removeGlobalSession() throws Exception { + GlobalSession session = GlobalSession.createGlobalSession("test", + "test", "test123", 100); + String xid = XID.generateXID(session.getTransactionId()); + session.setXid(xid); + session.setTransactionId(146757978); + session.setBeginTime(System.currentTimeMillis()); + session.setApplicationData("abc=878s"); + session.setStatus(GlobalStatus.Begin); + + sessionManager.addGlobalSession(session); + + String sql = "select * from global_table where xid= '"+xid+"'"; + String delSql = "delete from global_table where xid= '"+xid+"'"; + Connection conn = null; + try{ + conn = dataSource.getConnection(); + ResultSet rs = conn.createStatement().executeQuery(sql); + if(rs.next()){ + Assertions.assertTrue(true); + }else{ + Assertions.assertTrue(false); + } + rs.close(); + + //delete + sessionManager.removeGlobalSession(session); + + rs = conn.createStatement().executeQuery(sql); + if(rs.next()){ + Assertions.assertTrue(false); + }else{ + Assertions.assertTrue(true); + } + rs.close(); + + conn.createStatement().execute(delSql); + }finally { + if(conn != null){ + conn.close(); + } + } + } + + @Test + public void test_findGlobalSession() throws Exception { + GlobalSession session = GlobalSession.createGlobalSession("test", + "test", "test123", 100); + String xid = XID.generateXID(session.getTransactionId()); + session.setXid(xid); + session.setTransactionId(146757978); + session.setBeginTime(System.currentTimeMillis()); + session.setApplicationData("abc=878s"); + session.setStatus(GlobalStatus.Begin); + + sessionManager.addGlobalSession(session); + + GlobalSession globalSession_db = sessionManager.findGlobalSession(session.getXid()); + Assertions.assertNotNull(globalSession_db); + + Assertions.assertEquals(globalSession_db.getTransactionId(), session.getTransactionId()); + Assertions.assertEquals(globalSession_db.getXid(), session.getXid()); + Assertions.assertEquals(globalSession_db.getApplicationData(), session.getApplicationData()); + Assertions.assertEquals(globalSession_db.getApplicationId(), session.getApplicationId()); + Assertions.assertEquals(globalSession_db.getTransactionName(), session.getTransactionName()); + Assertions.assertEquals(globalSession_db.getTransactionServiceGroup(), session.getTransactionServiceGroup()); + Assertions.assertEquals(globalSession_db.getBeginTime(), session.getBeginTime()); + Assertions.assertEquals(globalSession_db.getTimeout(), session.getTimeout()); + Assertions.assertEquals(globalSession_db.getStatus(), session.getStatus()); + + String delSql = "delete from global_table where xid= '"+xid+"'"; + Connection conn = null; + try{ + conn = dataSource.getConnection(); + conn.createStatement().execute(delSql); + }finally { + if(conn != null){ + conn.close(); + } + } + } + + + @Test + public void test_addBranchSession() throws Exception { + GlobalSession globalSession = GlobalSession.createGlobalSession("test", + "test", "test123", 100); + String xid = XID.generateXID(globalSession.getTransactionId()); + globalSession.setXid(xid); + globalSession.setTransactionId(146757978); + globalSession.setBeginTime(System.currentTimeMillis()); + globalSession.setApplicationData("abc=878s"); + globalSession.setStatus(GlobalStatus.Begin); + + BranchSession branchSession = new BranchSession(); + branchSession.setBranchId(UUIDGenerator.generateUUID()); + branchSession.setXid(xid); + branchSession.setTransactionId(globalSession.getTransactionId()); + branchSession.setBranchId(1L); + branchSession.setResourceGroupId("my_test_tx_group"); + branchSession.setResourceId("tb_1"); + branchSession.setLockKey("t_1"); + branchSession.setBranchType(BranchType.AT); + branchSession.setApplicationData("{\"data\":\"test\"}"); + + sessionManager.addBranchSession(globalSession, branchSession); + + String sql = "select * from branch_table where xid= '"+xid+"'"; + String delSql = "delete from branch_table where xid= '"+xid+"'" + ";" + "delete from global_table where xid= '"+xid+"'"; + Connection conn = null; + try{ + conn = dataSource.getConnection(); + ResultSet rs = conn.createStatement().executeQuery(sql); + if(rs.next()){ + Assertions.assertTrue(true); + }else{ + Assertions.assertTrue(false); + } + + conn.createStatement().execute(delSql); + }finally { + if(conn != null){ + conn.close(); + } + } + } + + + @Test + public void test_updateBranchSessionStatus() throws Exception { + GlobalSession globalSession = GlobalSession.createGlobalSession("test", + "test", "test123", 100); + String xid = XID.generateXID(globalSession.getTransactionId()); + globalSession.setXid(xid); + globalSession.setTransactionId(146757978); + globalSession.setBeginTime(System.currentTimeMillis()); + globalSession.setApplicationData("abc=878s"); + globalSession.setStatus(GlobalStatus.Begin); + + BranchSession branchSession = new BranchSession(); + branchSession.setBranchId(UUIDGenerator.generateUUID()); + branchSession.setXid(xid); + branchSession.setTransactionId(globalSession.getTransactionId()); + branchSession.setBranchId(1L); + branchSession.setResourceGroupId("my_test_tx_group"); + branchSession.setResourceId("tb_1"); + branchSession.setLockKey("t_1"); + branchSession.setBranchType(BranchType.AT); + branchSession.setApplicationData("{\"data\":\"test\"}"); + branchSession.setStatus(BranchStatus.PhaseOne_Done); + + sessionManager.addBranchSession(globalSession, branchSession); + + branchSession.setStatus(BranchStatus.PhaseOne_Timeout); + sessionManager.updateBranchSessionStatus(branchSession, BranchStatus.PhaseOne_Timeout); + + String sql = "select * from branch_table where xid= '"+xid+"'"; + String delSql = "delete from branch_table where xid= '"+xid+"'" + ";" + "delete from global_table where xid= '"+xid+"'"; + Connection conn = null; + try{ + conn = dataSource.getConnection(); + ResultSet rs = conn.createStatement().executeQuery(sql); + if(rs.next()){ + Assertions.assertTrue(true); + Assertions.assertEquals(rs.getInt("status"), BranchStatus.PhaseOne_Timeout.getCode()); + }else{ + Assertions.assertTrue(false); + } + + conn.createStatement().execute(delSql); + }finally { + if(conn != null){ + conn.close(); + } + } + } + + @Test + public void test_removeBranchSession() throws Exception { + GlobalSession globalSession = GlobalSession.createGlobalSession("test", + "test", "test123", 100); + String xid = XID.generateXID(globalSession.getTransactionId()); + globalSession.setXid(xid); + globalSession.setTransactionId(146757978); + globalSession.setBeginTime(System.currentTimeMillis()); + globalSession.setApplicationData("abc=878s"); + globalSession.setStatus(GlobalStatus.Begin); + + BranchSession branchSession = new BranchSession(); + branchSession.setBranchId(UUIDGenerator.generateUUID()); + branchSession.setXid(xid); + branchSession.setTransactionId(globalSession.getTransactionId()); + branchSession.setBranchId(1L); + branchSession.setResourceGroupId("my_test_tx_group"); + branchSession.setResourceId("tb_1"); + branchSession.setLockKey("t_1"); + branchSession.setBranchType(BranchType.AT); + branchSession.setApplicationData("{\"data\":\"test\"}"); + branchSession.setStatus(BranchStatus.PhaseOne_Done); + + sessionManager.addBranchSession(globalSession, branchSession); + + sessionManager.removeBranchSession(globalSession, branchSession); + + String sql = "select * from branch_table where xid= '"+xid+"'"; + String delSql = "delete from branch_table where xid= '"+xid+"'" + ";" + "delete from global_table where xid= '"+xid+"'"; + Connection conn = null; + try{ + conn = dataSource.getConnection(); + ResultSet rs = conn.createStatement().executeQuery(sql); + if(rs.next()){ + Assertions.assertTrue(false); + }else{ + Assertions.assertTrue(true); + } + + conn.createStatement().execute(delSql); + }finally { + if(conn != null){ + conn.close(); + } + } + } + + + @Test + public void test_allSessions() throws Exception { + GlobalSession globalSession = GlobalSession.createGlobalSession("test", + "test", "test123", 100); + String xid = XID.generateXID(globalSession.getTransactionId()); + globalSession.setXid(xid); + globalSession.setTransactionId(146757978); + globalSession.setBeginTime(System.currentTimeMillis()); + globalSession.setApplicationData("abc=878s"); + globalSession.setStatus(GlobalStatus.Begin); + + sessionManager.addGlobalSession(globalSession); + + BranchSession branchSession = new BranchSession(); + branchSession.setBranchId(UUIDGenerator.generateUUID()); + branchSession.setXid(xid); + branchSession.setTransactionId(globalSession.getTransactionId()); + branchSession.setBranchId(1L); + branchSession.setResourceGroupId("my_test_tx_group"); + branchSession.setResourceId("tb_1"); + branchSession.setLockKey("t_1"); + branchSession.setBranchType(BranchType.AT); + branchSession.setClientId("abc-123"); + branchSession.setApplicationData("{\"data\":\"test\"}"); + branchSession.setStatus(BranchStatus.PhaseOne_Done); + + sessionManager.addBranchSession(globalSession, branchSession); + + + BranchSession branchSession2 = new BranchSession(); + branchSession2.setBranchId(UUIDGenerator.generateUUID()); + branchSession2.setXid(xid); + branchSession2.setTransactionId(globalSession.getTransactionId()); + branchSession2.setBranchId(2L); + branchSession2.setResourceGroupId("my_test_tx_group"); + branchSession2.setResourceId("tb_1"); + branchSession2.setLockKey("t_1"); + branchSession2.setBranchType(BranchType.TCC); + branchSession2.setClientId("abc-123"); + branchSession2.setApplicationData("{\"data\":\"test\"}"); + branchSession2.setStatus(BranchStatus.PhaseOne_Done); + + sessionManager.addBranchSession(globalSession, branchSession2); + + Collection<GlobalSession> rets = sessionManager.allSessions(); + Assertions.assertNotNull(rets); + Assertions.assertEquals(1, rets.size()); + + GlobalSession globalSession_db = (io.seata.server.session.GlobalSession) new ArrayList(rets).get(0); + + Assertions.assertNotNull(globalSession_db.getReverseSortedBranches()); + Assertions.assertEquals(2, globalSession_db.getReverseSortedBranches().size()); + + Assertions.assertNotNull(globalSession_db.getBranch(1L)); + Assertions.assertNotNull(globalSession_db.getBranch(2L)); + + String delSql = "delete from branch_table where xid= '"+xid+"'" + ";" + "delete from global_table where xid= '"+xid+"'"; + Connection conn = null; + try{ + conn = dataSource.getConnection(); + conn.createStatement().execute(delSql); + }finally { + if(conn != null){ + conn.close(); + } + } + } + + @Test + public void test_findGlobalSessions() throws TransactionException, SQLException { + String xid = null; + { + GlobalSession globalSession = GlobalSession.createGlobalSession("test", + "test", "test123", 100); + xid = XID.generateXID(globalSession.getTransactionId()); + globalSession.setXid(xid); + globalSession.setTransactionId(146757978); + globalSession.setBeginTime(System.currentTimeMillis()); + globalSession.setApplicationData("abc=878s"); + globalSession.setStatus(GlobalStatus.Begin); + + sessionManager.addGlobalSession(globalSession); + + BranchSession branchSession = new BranchSession(); + branchSession.setBranchId(UUIDGenerator.generateUUID()); + branchSession.setXid(xid); + branchSession.setTransactionId(globalSession.getTransactionId()); + branchSession.setBranchId(1L); + branchSession.setResourceGroupId("my_test_tx_group"); + branchSession.setResourceId("tb_1"); + branchSession.setLockKey("t_1"); + branchSession.setBranchType(BranchType.AT); + branchSession.setClientId("abc-123"); + branchSession.setApplicationData("{\"data\":\"test\"}"); + branchSession.setStatus(BranchStatus.PhaseOne_Done); + sessionManager.addBranchSession(globalSession, branchSession); + } + String xid2 = null; + { + GlobalSession globalSession = GlobalSession.createGlobalSession("test", + "test", "test123", 100); + xid2 = XID.generateXID(globalSession.getTransactionId()); + globalSession.setXid(xid); + globalSession.setTransactionId(146757978); + globalSession.setBeginTime(System.currentTimeMillis()); + globalSession.setApplicationData("abc=878s"); + globalSession.setStatus(GlobalStatus.CommitRetrying); + + sessionManager.addGlobalSession(globalSession); + + BranchSession branchSession = new BranchSession(); + branchSession.setBranchId(UUIDGenerator.generateUUID()); + branchSession.setXid(xid2); + branchSession.setTransactionId(globalSession.getTransactionId()); + branchSession.setBranchId(1L); + branchSession.setResourceGroupId("my_test_tx_group"); + branchSession.setResourceId("tb_1"); + branchSession.setLockKey("t_1"); + branchSession.setBranchType(BranchType.AT); + branchSession.setClientId("abc-123"); + branchSession.setApplicationData("{\"data\":\"test\"}"); + branchSession.setStatus(BranchStatus.PhaseOne_Done); + sessionManager.addBranchSession(globalSession, branchSession); + } + + + Collection<GlobalSession> rets = sessionManager.findGlobalSessions(new SessionCondition( GlobalStatus.Begin)); + Assertions.assertNotNull(rets); + Assertions.assertEquals(1, rets.size()); + + GlobalSession globalSession_db = (io.seata.server.session.GlobalSession) new ArrayList(rets).get(0); + + Assertions.assertNotNull(globalSession_db.getReverseSortedBranches()); + Assertions.assertEquals(1, globalSession_db.getReverseSortedBranches().size()); + + Assertions.assertNotNull(globalSession_db.getBranch(1L)); + + String delSql = "delete from branch_table where xid= '"+xid+"'" + ";" + "delete from global_table where xid= '"+xid+"'"; + String delSql2 = "delete from branch_table where xid= '"+xid2+"'" + ";" + "delete from global_table where xid= '"+xid2+"'"; + Connection conn = null; + try{ + conn = dataSource.getConnection(); + conn.createStatement().execute(delSql); + conn.createStatement().execute(delSql2); + }finally { + if(conn != null){ + conn.close(); + } + } + } + + + + +} \ No newline at end of file diff --git a/server/src/test/java/io/seata/server/store/SessionStoreTest.java b/server/src/test/java/io/seata/server/store/SessionStoreTest.java index 45048d7f63da748e082bb35c3b5c4198596de8fa..37fecd0d6c40400d7b539bf45df49664d680ce28 100644 --- a/server/src/test/java/io/seata/server/store/SessionStoreTest.java +++ b/server/src/test/java/io/seata/server/store/SessionStoreTest.java @@ -15,6 +15,9 @@ */ package io.seata.server.store; +import java.io.File; + +import io.seata.common.XID; import io.seata.config.Configuration; import io.seata.config.ConfigurationFactory; import io.seata.core.constants.ConfigurationKeys; @@ -22,7 +25,7 @@ import io.seata.core.model.BranchStatus; import io.seata.core.model.BranchType; import io.seata.core.model.GlobalStatus; import io.seata.server.lock.LockManager; -import io.seata.server.lock.LockManagerFactory; +import io.seata.server.lock.memory.MemoryLockManagerForTest; import io.seata.server.session.BranchSession; import io.seata.server.session.GlobalSession; import io.seata.server.session.SessionHelper; @@ -31,7 +34,6 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.io.File; /** * The type Session store test. @@ -63,7 +65,8 @@ public class SessionStoreTest { if (rootDataFileHis.exists()) { rootDataFileHis.delete(); } - LockManagerFactory.get().cleanAllLocks(); + LockManager lockManager = new MemoryLockManagerForTest(); + lockManager.cleanAllLocks(); } /** @@ -75,43 +78,48 @@ public class SessionStoreTest { public void testRestoredFromFile() throws Exception { SessionHolder.init("file"); GlobalSession globalSession = new GlobalSession("demo-app", "my_test_tx_group", "test", 6000); + String xid = XID.generateXID(globalSession.getTransactionId()); + globalSession.setXid(xid); globalSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); globalSession.begin(); BranchSession branchSession1 = SessionHelper.newBranchByGlobal(globalSession, BranchType.AT, RESOURCE_ID, "ta:1,2;tb:3", "xxx"); + branchSession1.setXid(xid); branchSession1.lock(); globalSession.addBranch(branchSession1); - LockManager lockManager = LockManagerFactory.get(); + LockManager lockManager = new MemoryLockManagerForTest(); - Assertions.assertFalse(lockManager.isLockable(0L, RESOURCE_ID, "ta:1")); - Assertions.assertFalse(lockManager.isLockable(0L, RESOURCE_ID, "ta:2")); - Assertions.assertFalse(lockManager.isLockable(0L, RESOURCE_ID, "tb:3")); + String otherXID = XID.generateXID(0L); - Assertions.assertTrue(lockManager.isLockable(0L, RESOURCE_ID, "ta:4")); - Assertions.assertTrue(lockManager.isLockable(0L, RESOURCE_ID, "tb:5")); + Assertions.assertFalse(lockManager.isLockable(otherXID, RESOURCE_ID, "ta:1")); + Assertions.assertFalse(lockManager.isLockable(otherXID, RESOURCE_ID, "ta:2")); + Assertions.assertFalse(lockManager.isLockable(otherXID, RESOURCE_ID, "tb:3")); + + Assertions.assertTrue(lockManager.isLockable(otherXID, RESOURCE_ID, "ta:4")); + Assertions.assertTrue(lockManager.isLockable(otherXID, RESOURCE_ID, "tb:5")); lockManager.cleanAllLocks(); - Assertions.assertTrue(lockManager.isLockable(0L, RESOURCE_ID, "ta:1")); - Assertions.assertTrue(lockManager.isLockable(0L, RESOURCE_ID, "ta:2")); - Assertions.assertTrue(lockManager.isLockable(0L, RESOURCE_ID, "tb:3")); + Assertions.assertTrue(lockManager.isLockable(otherXID, RESOURCE_ID, "ta:1")); + Assertions.assertTrue(lockManager.isLockable(otherXID, RESOURCE_ID, "ta:2")); + Assertions.assertTrue(lockManager.isLockable(otherXID, RESOURCE_ID, "tb:3")); // Re-init SessionHolder: restore sessions from file SessionHolder.init("file"); long tid = globalSession.getTransactionId(); - GlobalSession reloadSession = SessionHolder.findGlobalSession(tid); + GlobalSession reloadSession = SessionHolder.findGlobalSession(globalSession.getXid()); Assertions.assertNotNull(reloadSession); Assertions.assertFalse(globalSession == reloadSession); Assertions.assertEquals(globalSession.getApplicationId(), reloadSession.getApplicationId()); - Assertions.assertFalse(lockManager.isLockable(0L, RESOURCE_ID, "ta:1")); - Assertions.assertFalse(lockManager.isLockable(0L, RESOURCE_ID, "ta:2")); - Assertions.assertFalse(lockManager.isLockable(0L, RESOURCE_ID, "tb:3")); - Assertions.assertTrue(lockManager.isLockable(globalSession.getTransactionId(), RESOURCE_ID, "tb:3")); + Assertions.assertFalse(lockManager.isLockable(otherXID, RESOURCE_ID, "ta:1")); + Assertions.assertFalse(lockManager.isLockable(otherXID, RESOURCE_ID, "ta:2")); + Assertions.assertFalse(lockManager.isLockable(otherXID, RESOURCE_ID, "tb:3")); + Assertions.assertTrue(lockManager.isLockable(xid, RESOURCE_ID, "tb:3")); //clear reloadSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); @@ -145,6 +153,9 @@ public class SessionStoreTest { SessionHolder.init("file"); GlobalSession globalSession = new GlobalSession("demo-app", "my_test_tx_group", "test", 6000); + String xid = XID.generateXID(globalSession.getTransactionId()); + globalSession.setXid(xid); + globalSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); globalSession.begin(); @@ -153,29 +164,31 @@ public class SessionStoreTest { Assertions.assertTrue(branchSession1.lock()); globalSession.addBranch(branchSession1); - LockManager lockManager = LockManagerFactory.get(); + LockManager lockManager = new MemoryLockManagerForTest(); + + String otherXID = XID.generateXID(0L); - Assertions.assertFalse(lockManager.isLockable(0L, RESOURCE_ID, "ta:1")); + Assertions.assertFalse(lockManager.isLockable(otherXID, RESOURCE_ID, "ta:1")); globalSession.changeStatus(GlobalStatus.AsyncCommitting); lockManager.cleanAllLocks(); - Assertions.assertTrue(lockManager.isLockable(0L, RESOURCE_ID, "ta:1")); + Assertions.assertTrue(lockManager.isLockable(otherXID, RESOURCE_ID, "ta:1")); // Re-init SessionHolder: restore sessions from file SessionHolder.init("file"); long tid = globalSession.getTransactionId(); - GlobalSession reloadSession = SessionHolder.findGlobalSession(tid); + GlobalSession reloadSession = SessionHolder.findGlobalSession(globalSession.getXid()); Assertions.assertEquals(reloadSession.getStatus(), GlobalStatus.AsyncCommitting); GlobalSession sessionInAsyncCommittingQueue = SessionHolder.getAsyncCommittingSessionManager() - .findGlobalSession(tid); + .findGlobalSession(globalSession.getXid()); Assertions.assertTrue(reloadSession == sessionInAsyncCommittingQueue); // No locking for session in AsyncCommitting status - Assertions.assertTrue(lockManager.isLockable(0L, RESOURCE_ID, "ta:1")); + Assertions.assertTrue(lockManager.isLockable(otherXID, RESOURCE_ID, "ta:1")); //clear reloadSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); @@ -192,6 +205,9 @@ public class SessionStoreTest { SessionHolder.init("file"); GlobalSession globalSession = new GlobalSession("demo-app", "my_test_tx_group", "test", 6000); + String xid = XID.generateXID(globalSession.getTransactionId()); + globalSession.setXid(xid); + globalSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); globalSession.begin(); @@ -200,9 +216,11 @@ public class SessionStoreTest { branchSession1.lock(); globalSession.addBranch(branchSession1); - LockManager lockManager = LockManagerFactory.get(); + LockManager lockManager = new MemoryLockManagerForTest(); + + String otherXID = XID.generateXID(0L); - Assertions.assertFalse(lockManager.isLockable(0L, RESOURCE_ID, "ta:1")); + Assertions.assertFalse(lockManager.isLockable(otherXID, RESOURCE_ID, "ta:1")); globalSession.changeStatus(GlobalStatus.Committing); globalSession.changeBranchStatus(branchSession1, BranchStatus.PhaseTwo_CommitFailed_Retryable); @@ -210,23 +228,23 @@ public class SessionStoreTest { lockManager.cleanAllLocks(); - Assertions.assertTrue(lockManager.isLockable(0L, RESOURCE_ID, "ta:1")); + Assertions.assertTrue(lockManager.isLockable(otherXID, RESOURCE_ID, "ta:1")); // Re-init SessionHolder: restore sessions from file SessionHolder.init("file"); long tid = globalSession.getTransactionId(); - GlobalSession reloadSession = SessionHolder.findGlobalSession(tid); + GlobalSession reloadSession = SessionHolder.findGlobalSession(globalSession.getXid()); Assertions.assertEquals(reloadSession.getStatus(), GlobalStatus.CommitRetrying); GlobalSession sessionInRetryCommittingQueue = SessionHolder.getRetryCommittingSessionManager() - .findGlobalSession(tid); + .findGlobalSession(globalSession.getXid()); Assertions.assertTrue(reloadSession == sessionInRetryCommittingQueue); BranchSession reloadBranchSession = reloadSession.getBranch(branchSession1.getBranchId()); Assertions.assertEquals(reloadBranchSession.getStatus(), BranchStatus.PhaseTwo_CommitFailed_Retryable); // Lock is held by session in CommitRetrying status - Assertions.assertFalse(lockManager.isLockable(0L, RESOURCE_ID, "ta:1")); + Assertions.assertFalse(lockManager.isLockable(otherXID, RESOURCE_ID, "ta:1")); //clear reloadSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); @@ -244,6 +262,9 @@ public class SessionStoreTest { GlobalSession globalSession = new GlobalSession("demo-app", "my_test_tx_group", "test", 6000); + String xid = XID.generateXID(globalSession.getTransactionId()); + globalSession.setXid(xid); + globalSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); globalSession.begin(); @@ -252,9 +273,11 @@ public class SessionStoreTest { branchSession1.lock(); globalSession.addBranch(branchSession1); - LockManager lockManager = LockManagerFactory.get(); + LockManager lockManager = new MemoryLockManagerForTest(); - Assertions.assertFalse(lockManager.isLockable(0L, RESOURCE_ID, "ta:1")); + String otherXID = XID.generateXID(0L); + + Assertions.assertFalse(lockManager.isLockable(otherXID, RESOURCE_ID, "ta:1")); globalSession.changeStatus(GlobalStatus.Rollbacking); globalSession.changeBranchStatus(branchSession1, BranchStatus.PhaseTwo_RollbackFailed_Retryable); @@ -262,23 +285,23 @@ public class SessionStoreTest { lockManager.cleanAllLocks(); - Assertions.assertTrue(lockManager.isLockable(0L, RESOURCE_ID, "ta:1")); + Assertions.assertTrue(lockManager.isLockable(otherXID, RESOURCE_ID, "ta:1")); // Re-init SessionHolder: restore sessions from file SessionHolder.init("file"); long tid = globalSession.getTransactionId(); - GlobalSession reloadSession = SessionHolder.findGlobalSession(tid); + GlobalSession reloadSession = SessionHolder.findGlobalSession(globalSession.getXid()); Assertions.assertEquals(reloadSession.getStatus(), GlobalStatus.RollbackRetrying); GlobalSession sessionInRetryRollbackingQueue = SessionHolder.getRetryRollbackingSessionManager() - .findGlobalSession(tid); + .findGlobalSession(globalSession.getXid()); Assertions.assertTrue(reloadSession == sessionInRetryRollbackingQueue); BranchSession reloadBranchSession = reloadSession.getBranch(branchSession1.getBranchId()); Assertions.assertEquals(reloadBranchSession.getStatus(), BranchStatus.PhaseTwo_RollbackFailed_Retryable); // Lock is held by session in RollbackRetrying status - Assertions.assertFalse(lockManager.isLockable(0L, RESOURCE_ID, "ta:1")); + Assertions.assertFalse(lockManager.isLockable(otherXID, RESOURCE_ID, "ta:1")); //clear reloadSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); @@ -296,6 +319,9 @@ public class SessionStoreTest { GlobalSession globalSession = new GlobalSession("demo-app", "my_test_tx_group", "test", 6000); + String xid = XID.generateXID(globalSession.getTransactionId()); + globalSession.setXid(xid); + globalSession.addSessionLifecycleListener(SessionHolder.getRootSessionManager()); globalSession.begin(); @@ -304,26 +330,28 @@ public class SessionStoreTest { branchSession1.lock(); globalSession.addBranch(branchSession1); - LockManager lockManager = LockManagerFactory.get(); + LockManager lockManager = new MemoryLockManagerForTest(); + + String otherXID = XID.generateXID(0L); - Assertions.assertFalse(lockManager.isLockable(0L, RESOURCE_ID, "ta:1")); + Assertions.assertFalse(lockManager.isLockable(otherXID, RESOURCE_ID, "ta:1")); globalSession.changeStatus(GlobalStatus.Rollbacking); globalSession.changeBranchStatus(branchSession1, BranchStatus.PhaseTwo_CommitFailed_Unretryable); SessionHelper.endRollbackFailed(globalSession); // Lock is released. - Assertions.assertTrue(lockManager.isLockable(0L, RESOURCE_ID, "ta:1")); + Assertions.assertTrue(lockManager.isLockable(otherXID, RESOURCE_ID, "ta:1")); lockManager.cleanAllLocks(); - Assertions.assertTrue(lockManager.isLockable(0L, RESOURCE_ID, "ta:1")); + Assertions.assertTrue(lockManager.isLockable(otherXID, RESOURCE_ID, "ta:1")); // Re-init SessionHolder: restore sessions from file SessionHolder.init("file"); long tid = globalSession.getTransactionId(); - GlobalSession reloadSession = SessionHolder.findGlobalSession(tid); + GlobalSession reloadSession = SessionHolder.findGlobalSession(globalSession.getXid()); Assertions.assertNull(reloadSession); } } diff --git a/tcc/src/main/java/io/seata/rm/tcc/TwoPhaseResult.java b/tcc/src/main/java/io/seata/rm/tcc/TwoPhaseResult.java index dddd5e83c298988078a93667b5fef3c55bd8111b..0fe6860eb5aa082c77c240ad55048e459c94fa9d 100644 --- a/tcc/src/main/java/io/seata/rm/tcc/TwoPhaseResult.java +++ b/tcc/src/main/java/io/seata/rm/tcc/TwoPhaseResult.java @@ -17,9 +17,6 @@ package io.seata.rm.tcc; import io.seata.common.util.StringUtils; -import java.util.HashMap; -import java.util.Map; - /** * the TCC method result * diff --git a/tcc/src/main/java/io/seata/rm/tcc/remoting/RemotingParser.java b/tcc/src/main/java/io/seata/rm/tcc/remoting/RemotingParser.java index a4cda53d59a97dda60acd3da5d7884149d707271..2ff32ca49801977613dfef0b431ea28ea77c8583 100644 --- a/tcc/src/main/java/io/seata/rm/tcc/remoting/RemotingParser.java +++ b/tcc/src/main/java/io/seata/rm/tcc/remoting/RemotingParser.java @@ -17,8 +17,6 @@ package io.seata.rm.tcc.remoting; import io.seata.common.exception.FrameworkException; -import java.lang.reflect.InvocationTargetException; - /** * extract remoting bean info * diff --git a/tcc/src/main/java/io/seata/rm/tcc/remoting/parser/LocalTCCRemotingParser.java b/tcc/src/main/java/io/seata/rm/tcc/remoting/parser/LocalTCCRemotingParser.java index d07050c5f348870ddfbbb102996221750c6214b7..c4764df599cf4d50f81aa601951d42104019b26b 100644 --- a/tcc/src/main/java/io/seata/rm/tcc/remoting/parser/LocalTCCRemotingParser.java +++ b/tcc/src/main/java/io/seata/rm/tcc/remoting/parser/LocalTCCRemotingParser.java @@ -20,7 +20,6 @@ import io.seata.common.util.ReflectionUtil; import io.seata.rm.tcc.api.LocalTCC; import io.seata.rm.tcc.remoting.Protocols; import io.seata.rm.tcc.remoting.RemotingDesc; -import io.seata.rm.tcc.remoting.RemotingDesc; import java.util.Set; diff --git a/tcc/src/main/java/io/seata/rm/tcc/remoting/parser/SofaRpcRemotingParser.java b/tcc/src/main/java/io/seata/rm/tcc/remoting/parser/SofaRpcRemotingParser.java index ff1cc8349cfb0fc0eb226a8706c75857dc6aeac6..8af247f16f16dc55a4521cb5c2433b9bb6033b0d 100644 --- a/tcc/src/main/java/io/seata/rm/tcc/remoting/parser/SofaRpcRemotingParser.java +++ b/tcc/src/main/java/io/seata/rm/tcc/remoting/parser/SofaRpcRemotingParser.java @@ -19,8 +19,6 @@ import io.seata.common.exception.FrameworkException; import io.seata.common.util.ReflectionUtil; import io.seata.rm.tcc.remoting.Protocols; import io.seata.rm.tcc.remoting.RemotingDesc; -import io.seata.rm.tcc.remoting.Protocols; -import io.seata.rm.tcc.remoting.RemotingDesc; /** * sofa-rpc remoting bean parsing diff --git a/test/src/test/java/io/seata/core/rpc/netty/TmRpcClientTest.java b/test/src/test/java/io/seata/core/rpc/netty/TmRpcClientTest.java index dc5682b9b10f6ee89d182b0d85f97ee162bfb0a3..9f3a192b98fafcc2a4b3ce7e1d908d4458f2f51a 100644 --- a/test/src/test/java/io/seata/core/rpc/netty/TmRpcClientTest.java +++ b/test/src/test/java/io/seata/core/rpc/netty/TmRpcClientTest.java @@ -20,9 +20,6 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; -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; diff --git a/test/src/test/resources/file.conf b/test/src/test/resources/file.conf index 30801f29901834757ef6e68d5fbf6c4b1486a975..65573b25b31e86f066fcba04882ad8b1afb8488d 100644 --- a/test/src/test/resources/file.conf +++ b/test/src/test/resources/file.conf @@ -70,3 +70,7 @@ client { } report.retry.count = 5 } + +transaction { + undo.data.validation = true +} \ No newline at end of file diff --git a/test/src/test/resources/registry.conf b/test/src/test/resources/registry.conf index 1cddea7219444a8f50ee5bdc7f7170de40884356..5115876d916fa5e59a5511796e5cc92622f5a442 100644 --- a/test/src/test/resources/registry.conf +++ b/test/src/test/resources/registry.conf @@ -45,7 +45,7 @@ registry { } config { - # file、nacos 、apollo、zk、consul + # file、nacos 、apollo、zk、consul、etcd3 type = "file" nacos { @@ -65,7 +65,10 @@ config { session.timeout = 6000 connect.timeout = 2000 } + etcd3 { + serverAddr = "http://localhost:2379" + } file { name = "file.conf" } -} +} \ No newline at end of file diff --git a/tm/src/main/java/io/seata/tm/TransactionManagerHolder.java b/tm/src/main/java/io/seata/tm/TransactionManagerHolder.java index f4deba4aaf08507ee8f31d9706f8519bd3681f1c..9833634c61f28dc70623850b448c7039148c9212 100644 --- a/tm/src/main/java/io/seata/tm/TransactionManagerHolder.java +++ b/tm/src/main/java/io/seata/tm/TransactionManagerHolder.java @@ -17,16 +17,10 @@ package io.seata.tm; import io.seata.common.exception.ShouldNeverHappenException; import io.seata.common.loader.EnhancedServiceLoader; -import io.seata.core.exception.TransactionException; -import io.seata.core.exception.TransactionExceptionCode; -import io.seata.core.model.GlobalStatus; import io.seata.core.model.TransactionManager; -import io.seata.core.protocol.transaction.*; -import io.seata.core.rpc.netty.TmRpcClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.concurrent.TimeoutException; /** * The type Default transaction manager. diff --git a/tm/src/main/java/io/seata/tm/api/DefaultGlobalTransaction.java b/tm/src/main/java/io/seata/tm/api/DefaultGlobalTransaction.java index 2736c79d28228478ae3e36e46e09621f91378b3f..78e85d66e7535f51bf581d6de0099613a145f695 100644 --- a/tm/src/main/java/io/seata/tm/api/DefaultGlobalTransaction.java +++ b/tm/src/main/java/io/seata/tm/api/DefaultGlobalTransaction.java @@ -20,7 +20,6 @@ import io.seata.core.context.RootContext; import io.seata.core.exception.TransactionException; import io.seata.core.model.GlobalStatus; import io.seata.core.model.TransactionManager; -import io.seata.tm.DefaultTransactionManager; import io.seata.tm.TransactionManagerHolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory;