From 705cd2dac563e8e974ad7e831735516c775e84c2 Mon Sep 17 00:00:00 2001 From: linghengqian Date: Mon, 12 Feb 2024 21:14:26 +0800 Subject: [PATCH] Add GraalVM Reachability Metadata and corresponding nativeTest for Seata integration --- .../graalvm-native-image/_index.cn.md | 54 ++++++++- .../graalvm-native-image/_index.en.md | 63 ++++++++-- .../caffeine/2.9.3/reflect-config.json | 21 +++- .../reflect-config.json | 43 +++---- .../type/classpath/ClassPathURLLoader.java | 19 +-- .../jdbc/sql/JDBCRepositorySQLLoader.java | 18 +-- .../native-image-filter/extra-filter.json | 4 + test/native/pom.xml | 30 +++++ .../jdbc/commons/TestShardingService.java | 1 + ...ssBasedInlineShardingAlgorithmFixture.java | 1 - .../commons/repository/AddressRepository.java | 36 ++++++ .../jdbc/transactions/base/SeataTest.java | 113 ++++++++++++++++++ .../reflect-config.json | 2 +- .../resource-config.json | 14 ++- test/native/src/test/resources/file.conf | 21 ++++ test/native/src/test/resources/registry.conf | 30 +++++ test/native/src/test/resources/seata.conf | 24 ++++ .../yaml/transactions/base/seata.yaml | 76 ++++++++++++ 18 files changed, 498 insertions(+), 72 deletions(-) create mode 100644 test/native/src/test/java/org/apache/shardingsphere/test/natived/jdbc/transactions/base/SeataTest.java create mode 100644 test/native/src/test/resources/file.conf create mode 100644 test/native/src/test/resources/registry.conf create mode 100644 test/native/src/test/resources/seata.conf create mode 100644 test/native/src/test/resources/test-native/yaml/transactions/base/seata.yaml diff --git a/docs/document/content/user-manual/shardingsphere-jdbc/graalvm-native-image/_index.cn.md b/docs/document/content/user-manual/shardingsphere-jdbc/graalvm-native-image/_index.cn.md index f70c8a456f5afd..8a74109e524b90 100644 --- a/docs/document/content/user-manual/shardingsphere-jdbc/graalvm-native-image/_index.cn.md +++ b/docs/document/content/user-manual/shardingsphere-jdbc/graalvm-native-image/_index.cn.md @@ -78,6 +78,7 @@ plugins { dependencies { implementation 'org.apache.shardingsphere:shardingsphere-jdbc-core:${shardingsphere.version}' + implementation 'org.graalvm.buildtools:graalvm-reachability-metadata:0.10.0' } graalvmNative { @@ -89,6 +90,9 @@ graalvmNative { buildArgs.add('-H:+AddAllCharsets') } } + metadataRepository { + enabled.set(false) + } } ``` @@ -181,7 +185,7 @@ Image 下使用。 ``` 2. 对于 `读写分离` 的功能,你需要使用 `行表达式` SPI 的其他实现,以在配置 `logic database name`,`writeDataSourceName` 和 `readDataSourceNames` -时绕开对 GroovyShell 的调用。一个可能的配置是使用 `LITERAL` 的 `行表达式` SPI 的实现。对于 `数据分片` 的功能的 `actualDataNodes` 同理。 +时绕开对 GroovyShell 的调用。一个可能的配置是使用 `LITERAL` 的 `行表达式` SPI 的实现。 ```yaml rules: - !READWRITE_SPLITTING @@ -193,6 +197,18 @@ rules: - ds_2 ``` +对于 `数据分片` 的功能的 `actualDataNodes` 同理。 + +```yaml +- !SHARDING + tables: + t_order: + actualDataNodes: ds_0.t_order_0, ds_0.t_order_1, ds_1.t_order_0, ds_1.t_order_1 + keyGenerateStrategy: + column: order_id + keyGeneratorName: snowflake +``` + 3. 使用者依然需要在 `src/main/resources/META-INF/native-image` 文件夹或 `src/test/resources/META-INF/native-image` 文件夹配置独立 文件的 GraalVM Reachability Metadata。使用者可通过 GraalVM Native Build Tools 的 GraalVM Tracing Agent 来快速采集 GraalVM Reachability Metadata。 @@ -201,11 +217,6 @@ Reachability Metadata。 当遇到如下 Error,使用者需要添加 `-H:+AddAllCharsets` 的 `buildArg` 到 GraalVM Native Build Tools 的配置中。 ```shell -Caused by: java.io.UnsupportedEncodingException: SQL Server collation SQL_Latin1_General_CP1_CI_AS is not supported by this driver. - com.microsoft.sqlserver.jdbc.SQLCollation.encodingFromSortId(SQLCollation.java:506) - com.microsoft.sqlserver.jdbc.SQLCollation.(SQLCollation.java:63) - com.microsoft.sqlserver.jdbc.SQLServerConnection.processEnvChange(SQLServerConnection.java:3174) - [...] Caused by: java.io.UnsupportedEncodingException: Codepage Cp1252 is not supported by the Java environment. com.microsoft.sqlserver.jdbc.Encoding.checkSupported(SQLCollation.java:572) com.microsoft.sqlserver.jdbc.SQLCollation$SortOrder.getEncoding(SQLCollation.java:473) @@ -213,6 +224,37 @@ Caused by: java.io.UnsupportedEncodingException: Codepage Cp1252 is not supporte [...] ``` +5. 当使用 Seata 的 BASE 集成时,用户需要使用特定的 `io.seata:seata-all:1.8.0` 版本以避开对 ByteBuddy Java API 的使用, +并排除 `io.seata:seata-all:1.8.0` 中过时的 `org.antlr:antlr4-runtime:4.8` 的 Maven 依赖。可能的配置例子如下, + +```xml + + + + org.apache.shardingsphere + shardingsphere-jdbc-core + ${shardingsphere.version} + + + org.apache.shardingsphere + shardingsphere-transaction-base-seata-at + ${shardingsphere.version} + + + io.seata + seata-all + 1.8.0 + + + org.antlr + antlr4-runtime + + + + + +``` + ## 贡献 GraalVM Reachability Metadata ShardingSphere 对在 GraalVM Native Image 下的可用性的验证,是通过 GraalVM Native Build Tools 的 Maven Plugin 子项目来完成的。 diff --git a/docs/document/content/user-manual/shardingsphere-jdbc/graalvm-native-image/_index.en.md b/docs/document/content/user-manual/shardingsphere-jdbc/graalvm-native-image/_index.en.md index 57d083e7667462..1b46b1d40c5770 100644 --- a/docs/document/content/user-manual/shardingsphere-jdbc/graalvm-native-image/_index.en.md +++ b/docs/document/content/user-manual/shardingsphere-jdbc/graalvm-native-image/_index.en.md @@ -76,11 +76,12 @@ and the documentation of GraalVM Native Build Tools shall prevail. ```groovy plugins { - id 'org.graalvm.buildtools.native' version '0.10.0' + id 'org.graalvm.buildtools.native' version '0.10.0' } dependencies { - implementation 'org.apache.shardingsphere:shardingsphere-jdbc-core:${shardingsphere.version}' + implementation 'org.apache.shardingsphere:shardingsphere-jdbc-core:${shardingsphere.version}' + implementation 'org.graalvm.buildtools:graalvm-reachability-metadata:0.10.0' } graalvmNative { @@ -92,6 +93,9 @@ graalvmNative { buildArgs.add('-H:+AddAllCharsets') } } + metadataRepository { + enabled.set(false) + } } ``` @@ -186,9 +190,8 @@ normally under GraalVM Native Image. ``` 2. For the `ReadWrite Splitting` feature, you need to use other implementations of `Row Value Expressions` SPI to configure -`logic database name`, `writeDataSourceName` and `readDataSourceNames` when bypassing calls to GroovyShell. One possible -configuration is to use the `Row Value Expressions` SPI implementation of `LITERAL`. The same applies to `actualDataNodes` -for the `Sharding` feature. +`logic database name`, `writeDataSourceName` and `readDataSourceNames` when bypassing calls to GroovyShell. +One possible configuration is to use the `Row Value Expressions` SPI implementation of `LITERAL`. ```yaml rules: @@ -201,6 +204,18 @@ rules: - ds_2 ``` +The same applies to `actualDataNodes` for the `Sharding` feature. + +```yaml +- !SHARDING + tables: + t_order: + actualDataNodes: ds_0.t_order_0, ds_0.t_order_1, ds_1.t_order_0, ds_1.t_order_1 + keyGenerateStrategy: + column: order_id + keyGeneratorName: snowflake +``` + 3. Users still need to configure GraalVM Reachability Metadata for independent files in the `src/main/resources/META-INF/native-image` folder or `src/test/resources/META-INF/native-image` folder. Users can quickly collect GraalVM Reachability Metadata through the GraalVM Tracing Agent of GraalVM Native Build Tools. @@ -210,11 +225,6 @@ will dynamically load different character sets based on the encoding used in the When encountering the following Error, users need to add the `buildArg` of `-H:+AddAllCharsets` to the configuration of GraalVM Native Build Tools. ```shell -Caused by: java.io.UnsupportedEncodingException: SQL Server collation SQL_Latin1_General_CP1_CI_AS is not supported by this driver. - com.microsoft.sqlserver.jdbc.SQLCollation.encodingFromSortId(SQLCollation.java:506) - com.microsoft.sqlserver.jdbc.SQLCollation.(SQLCollation.java:63) - com.microsoft.sqlserver.jdbc.SQLServerConnection.processEnvChange(SQLServerConnection.java:3174) - [...] Caused by: java.io.UnsupportedEncodingException: Codepage Cp1252 is not supported by the Java environment. com.microsoft.sqlserver.jdbc.Encoding.checkSupported(SQLCollation.java:572) com.microsoft.sqlserver.jdbc.SQLCollation$SortOrder.getEncoding(SQLCollation.java:473) @@ -222,6 +232,39 @@ Caused by: java.io.UnsupportedEncodingException: Codepage Cp1252 is not supporte [...] ``` +5. When using Seata's BASE integration, +users need to use a specific `io.seata:seata-all:1.8.0` version to avoid using the ByteBuddy Java API, +and exclude the outdated Maven dependency of `org.antlr:antlr4-runtime:4.8` in `io.seata:seata-all:1.8.0`. +Possible configuration examples are as follows, + +```xml + + + + org.apache.shardingsphere + shardingsphere-jdbc-core + ${shardingsphere.version} + + + org.apache.shardingsphere + shardingsphere-transaction-base-seata-at + ${shardingsphere.version} + + + io.seata + seata-all + 1.8.0 + + + org.antlr + antlr4-runtime + + + + + +``` + ## Contribute GraalVM Reachability Metadata The verification of ShardingSphere's availability under GraalVM Native Image is completed through the Maven Plugin subproject diff --git a/infra/reachability-metadata/src/main/resources/META-INF/native-image/com.github.ben-manes.caffeine/caffeine/2.9.3/reflect-config.json b/infra/reachability-metadata/src/main/resources/META-INF/native-image/com.github.ben-manes.caffeine/caffeine/2.9.3/reflect-config.json index 995222a3c6b519..410db67b1c2d9a 100644 --- a/infra/reachability-metadata/src/main/resources/META-INF/native-image/com.github.ben-manes.caffeine/caffeine/2.9.3/reflect-config.json +++ b/infra/reachability-metadata/src/main/resources/META-INF/native-image/com.github.ben-manes.caffeine/caffeine/2.9.3/reflect-config.json @@ -40,15 +40,25 @@ "fields":[{"name":"value"}] }, { - "condition":{"typeReachable":"com.github.benmanes.caffeine.cache.NodeFactory"}, - "name":"com.github.benmanes.caffeine.cache.PDMS", + "condition":{"typeReachable":"com.github.benmanes.caffeine.cache.PDW"}, + "name":"com.github.benmanes.caffeine.cache.PDW", + "fields":[{"name":"writeTime"}] +}, +{ + "condition":{"typeReachable":"com.github.benmanes.caffeine.cache.SIMSW"}, + "name":"com.github.benmanes.caffeine.cache.PDWMS", "methods":[{"name":"","parameterTypes":[] }] }, { - "condition":{"typeReachable":"com.github.benmanes.caffeine.cache.LocalCacheFactory"}, + "condition":{"typeReachable":"com.github.benmanes.caffeine.cache.BoundedLocalCache$BoundedLocalLoadingCache"}, "name":"com.github.benmanes.caffeine.cache.SIMS", "methods":[{"name":"","parameterTypes":["com.github.benmanes.caffeine.cache.Caffeine","com.github.benmanes.caffeine.cache.CacheLoader","boolean"] }] }, +{ + "condition":{"typeReachable":"com.github.benmanes.caffeine.cache.BoundedLocalCache$BoundedLocalManualCache"}, + "name":"com.github.benmanes.caffeine.cache.SIMSW", + "methods":[{"name":"","parameterTypes":["com.github.benmanes.caffeine.cache.Caffeine","com.github.benmanes.caffeine.cache.CacheLoader","boolean"] }] +}, { "condition":{"typeReachable":"com.github.benmanes.caffeine.cache.StripedBuffer"}, "name":"com.github.benmanes.caffeine.cache.StripedBuffer", @@ -58,5 +68,10 @@ "condition":{"typeReachable":"com.github.benmanes.caffeine.cache.StripedBuffer"}, "name":"java.lang.Thread", "fields":[{"name":"threadLocalRandomProbe"}] +}, +{ + "condition":{"typeReachable":"com.github.benmanes.caffeine.cache.NodeFactory"}, + "name":"com.github.benmanes.caffeine.cache.PDMS", + "methods":[{"name":"","parameterTypes":[] }] } ] diff --git a/infra/reachability-metadata/src/main/resources/META-INF/native-image/org.apache.shardingsphere/shardingsphere-infra-reachability-metadata/reflect-config.json b/infra/reachability-metadata/src/main/resources/META-INF/native-image/org.apache.shardingsphere/shardingsphere-infra-reachability-metadata/reflect-config.json index 02082e722764e1..bd4f46aff8c474 100644 --- a/infra/reachability-metadata/src/main/resources/META-INF/native-image/org.apache.shardingsphere/shardingsphere-infra-reachability-metadata/reflect-config.json +++ b/infra/reachability-metadata/src/main/resources/META-INF/native-image/org.apache.shardingsphere/shardingsphere-infra-reachability-metadata/reflect-config.json @@ -65,31 +65,6 @@ "condition":{"typeReachable":"org.apache.shardingsphere.infra.util.yaml.YamlEngine"}, "name":"org.apache.shardingsphere.driver.api.yaml.YamlJDBCConfigurationCustomizer" }, -{ - "condition":{"typeReachable":"org.apache.shardingsphere.driver.jdbc.core.driver.ShardingSphereURLManager"}, - "name":"org.apache.shardingsphere.driver.jdbc.core.driver.spi.absolutepath.AbsolutePathURLProvider", - "methods":[{"name":"","parameterTypes":[] }] -}, -{ - "condition":{"typeReachable":"org.apache.shardingsphere.driver.jdbc.core.driver.ShardingSphereURLManager"}, - "name":"org.apache.shardingsphere.driver.jdbc.core.driver.spi.absolutepath.AbsolutePathWithEnvironmentURLProvider", - "methods":[{"name":"","parameterTypes":[] }] -}, -{ - "condition":{"typeReachable":"org.apache.shardingsphere.driver.jdbc.core.driver.ShardingSphereURLManager"}, - "name":"org.apache.shardingsphere.driver.jdbc.core.driver.spi.classpath.ClasspathURLProvider", - "methods":[{"name":"","parameterTypes":[] }] -}, -{ - "condition":{"typeReachable":"org.apache.shardingsphere.driver.jdbc.core.driver.ShardingSphereURLManager"}, - "name":"org.apache.shardingsphere.driver.jdbc.core.driver.spi.classpath.ClasspathWithEnvironmentURLProvider", - "methods":[{"name":"","parameterTypes":[] }] -}, -{ - "condition":{"typeReachable":"org.apache.shardingsphere.driver.jdbc.core.driver.ShardingSphereURLManager"}, - "name":"org.apache.shardingsphere.driver.jdbc.core.driver.spi.classpath.ClasspathWithSystemPropsURLProvider", - "methods":[{"name":"","parameterTypes":[] }] -}, { "condition":{"typeReachable":"org.apache.shardingsphere.encrypt.rule.EncryptRule"}, "name":"org.apache.shardingsphere.encrypt.algorithm.assisted.MD5AssistedEncryptAlgorithm", @@ -979,7 +954,7 @@ "methods":[{"name":"","parameterTypes":[] }] }, { - "condition":{"typeReachable":"org.apache.shardingsphere.infra.parser.cache.SQLStatementCacheLoader"}, + "condition":{"typeReachable":"org.apache.shardingsphere.sql.parser.core.database.visitor.SQLStatementVisitorFactory"}, "name":"org.apache.shardingsphere.sql.parser.mysql.visitor.statement.type.MySQLDMLStatementVisitor", "methods":[{"name":"","parameterTypes":[] }] }, @@ -1004,7 +979,7 @@ "methods":[{"name":"","parameterTypes":[] }] }, { - "condition":{"typeReachable":"org.apache.shardingsphere.infra.parser.cache.SQLStatementCacheLoader"}, + "condition":{"typeReachable":"org.apache.shardingsphere.sql.parser.core.database.visitor.SQLStatementVisitorFactory"}, "name":"org.apache.shardingsphere.sql.parser.opengauss.visitor.statement.type.OpenGaussDMLStatementVisitor", "methods":[{"name":"","parameterTypes":[] }] }, @@ -1034,7 +1009,7 @@ "methods":[{"name":"","parameterTypes":[] }] }, { - "condition":{"typeReachable":"org.apache.shardingsphere.infra.parser.cache.SQLStatementCacheLoader"}, + "condition":{"typeReachable":"org.apache.shardingsphere.sql.parser.core.database.visitor.SQLStatementVisitorFactory"}, "name":"org.apache.shardingsphere.sql.parser.postgresql.visitor.statement.type.PostgreSQLDMLStatementVisitor", "methods":[{"name":"","parameterTypes":[] }] }, @@ -1124,7 +1099,7 @@ "methods":[{"name":"","parameterTypes":[] }] }, { - "condition":{"typeReachable":"org.apache.shardingsphere.infra.parser.cache.SQLStatementCacheLoader"}, + "condition":{"typeReachable":"org.apache.shardingsphere.sql.parser.core.database.visitor.SQLStatementVisitorFactory"}, "name":"org.apache.shardingsphere.sql.parser.sqlserver.visitor.statement.type.SQLServerDMLStatementVisitor", "methods":[{"name":"","parameterTypes":[] }] }, @@ -1191,6 +1166,16 @@ "condition":{"typeReachable":"org.apache.shardingsphere.infra.yaml.config.shortcut.YamlRuleConfigurationShortcuts"}, "name":"org.apache.shardingsphere.traffic.yaml.config.YamlTrafficRuleConfiguration" }, +{ + "condition":{"typeReachable":"org.apache.shardingsphere.transaction.ShardingSphereTransactionManagerEngine"}, + "name":"org.apache.shardingsphere.transaction.base.seata.at.SeataATShardingSphereTransactionManager", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "condition":{"typeReachable":"org.apache.shardingsphere.infra.executor.sql.hook.SPISQLExecutionHook"}, + "name":"org.apache.shardingsphere.transaction.base.seata.at.SeataTransactionalSQLExecutionHook", + "methods":[{"name":"","parameterTypes":[] }] +}, { "condition":{"typeReachable":"org.apache.shardingsphere.transaction.ShardingSphereTransactionManagerEngine"}, "name":"org.apache.shardingsphere.transaction.xa.XAShardingSphereTransactionManager", diff --git a/infra/url/type/classpath/src/main/java/org/apache/shardingsphere/infra/url/type/classpath/ClassPathURLLoader.java b/infra/url/type/classpath/src/main/java/org/apache/shardingsphere/infra/url/type/classpath/ClassPathURLLoader.java index 892d236c54d7ad..b870a8f12b686c 100644 --- a/infra/url/type/classpath/src/main/java/org/apache/shardingsphere/infra/url/type/classpath/ClassPathURLLoader.java +++ b/infra/url/type/classpath/src/main/java/org/apache/shardingsphere/infra/url/type/classpath/ClassPathURLLoader.java @@ -20,10 +20,11 @@ import lombok.SneakyThrows; import org.apache.shardingsphere.infra.url.ShardingSphereURLLoader; -import java.io.File; +import java.io.BufferedReader; import java.io.IOException; -import java.net.URISyntaxException; -import java.nio.file.Files; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.util.Objects; import java.util.Properties; import java.util.stream.Collectors; @@ -36,12 +37,12 @@ public final class ClassPathURLLoader implements ShardingSphereURLLoader { @Override @SneakyThrows(IOException.class) public String load(final String configurationSubject, final Properties queryProps) { - return Files.readAllLines(getResourceFile(configurationSubject).toPath()).stream().collect(Collectors.joining(System.lineSeparator())); - } - - @SneakyThrows(URISyntaxException.class) - private File getResourceFile(final String configurationSubject) { - return new File(Objects.requireNonNull(Thread.currentThread().getContextClassLoader().getResource(configurationSubject)).toURI().getPath()); + try (InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(configurationSubject)) { + Objects.requireNonNull(inputStream); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { + return reader.lines().collect(Collectors.joining(System.lineSeparator())); + } + } } @Override diff --git a/mode/type/standalone/repository/provider/jdbc/src/main/java/org/apache/shardingsphere/mode/repository/standalone/jdbc/sql/JDBCRepositorySQLLoader.java b/mode/type/standalone/repository/provider/jdbc/src/main/java/org/apache/shardingsphere/mode/repository/standalone/jdbc/sql/JDBCRepositorySQLLoader.java index 94f691d680cfd5..8a9085e64a35fe 100644 --- a/mode/type/standalone/repository/provider/jdbc/src/main/java/org/apache/shardingsphere/mode/repository/standalone/jdbc/sql/JDBCRepositorySQLLoader.java +++ b/mode/type/standalone/repository/provider/jdbc/src/main/java/org/apache/shardingsphere/mode/repository/standalone/jdbc/sql/JDBCRepositorySQLLoader.java @@ -85,15 +85,10 @@ public static JDBCRepositorySQL load(final String type) { } /** - * Under the GraalVM Native Image, although there is - * `com.oracle.svm.core.jdk.resources.NativeImageResourceFileSystemProvider`, the corresponding - * `com.oracle.svm.core.jdk.resources.NativeImageResourceFileSystem` does not autoload. This is mainly to align the - * behavior of `ZipFileSystemProvider`, so ShardingSphere need to manually open and close the FileSystem - * corresponding to the `resource:/` scheme. For more background reference oracle/graal#7682. - *

- * ShardingSphere use the System Property of `org.graalvm.nativeimage.imagecode` to identify whether this class is in the - * GraalVM Native Image environment. The background of this property comes from - * Annotation Interface DisabledInNativeImage. + * Under the GraalVM Native Image, `com.oracle.svm.core.jdk.resources.NativeImageResourceFileSystem` does not autoload. + * This is mainly to align the behavior of `jdk.nio.zipfs.ZipFileSystem`, + * so ShardingSphere need to manually open and close the FileSystem corresponding to the `resource:/` scheme. + * For more background reference oracle/graal#7682. * * @param url url * @param type type of JDBC repository SQL @@ -104,13 +99,12 @@ public static JDBCRepositorySQL load(final String type) { * @see sun.nio.fs.UnixFileSystemProvider */ private static JDBCRepositorySQL loadFromDirectory(final URL url, final String type) throws URISyntaxException, IOException { - if (null == System.getProperty("org.graalvm.nativeimage.imagecode") || !"runtime".equals(System.getProperty("org.graalvm.nativeimage.imagecode"))) { - return loadFromDirectoryLegacy(url, type); - } else { + if ("resource".equals(url.getProtocol())) { try (FileSystem ignored = FileSystems.newFileSystem(URI.create("resource:/"), Collections.emptyMap())) { return loadFromDirectoryInNativeImage(url, type); } } + return loadFromDirectoryLegacy(url, type); } /** diff --git a/test/native/native-image-filter/extra-filter.json b/test/native/native-image-filter/extra-filter.json index facc03118b9fb3..197076d15ed76b 100644 --- a/test/native/native-image-filter/extra-filter.json +++ b/test/native/native-image-filter/extra-filter.json @@ -19,6 +19,7 @@ {"excludeClasses": "android.app.**"}, {"excludeClasses": "com.arjuna.**"}, {"excludeClasses": "com.atomikos.logging.**"}, + {"excludeClasses": "com.alibaba.druid.**"}, {"excludeClasses": "com.fasterxml.jackson.databind.**"}, {"excludeClasses": "com.github.benmanes.caffeine.cache.**"}, {"excludeClasses": "com.github.dockerjava.api.**"}, @@ -30,10 +31,13 @@ {"excludeClasses": "ch.qos.logback.classic.**"}, {"excludeClasses": "io.grpc.**"}, {"excludeClasses": "io.netty.**"}, + {"excludeClasses": "io.seata.**"}, {"excludeClasses": "io.vertx.core.**"}, {"excludeClasses": "groovy.**"}, {"excludeClasses": "libcore.io.**"}, + {"excludeClasses": "net.bytebuddy.**"}, {"excludeClasses": "org.apache.calcite.**"}, + {"excludeClasses": "org.apache.commons.logging.**"}, {"excludeClasses": "org.apache.logging.log4j.**"}, {"excludeClasses": "org.apache.log4j.**"}, {"excludeClasses": "org.apache.zookeeper.**"}, diff --git a/test/native/pom.xml b/test/native/pom.xml index 79fac3e641ec8c..5a28b5224c0a14 100644 --- a/test/native/pom.xml +++ b/test/native/pom.xml @@ -26,6 +26,12 @@ shardingsphere-test-native ${project.artifactId} + + + 1.8.0 + + org.apache.shardingsphere @@ -61,31 +67,55 @@ org.apache.shardingsphere shardingsphere-transaction-xa-core ${project.version} + test org.apache.shardingsphere shardingsphere-transaction-xa-narayana ${project.version} + test org.jboss.narayana.jta jta ${narayana.version} + test org.jboss.narayana.jts narayana-jts-integration ${narayana.version} + test org.jboss jboss-transaction-spi ${jboss-transaction-spi.version} + test org.jboss.logging jboss-logging ${jboss-logging.version} + test + + + org.apache.shardingsphere + shardingsphere-transaction-base-seata-at + ${project.version} + test + + + io.seata + seata-all + ${seata.version} + test + + + org.antlr + antlr4-runtime + + diff --git a/test/native/src/test/java/org/apache/shardingsphere/test/natived/jdbc/commons/TestShardingService.java b/test/native/src/test/java/org/apache/shardingsphere/test/natived/jdbc/commons/TestShardingService.java index 501c19efdcbbfb..8e734f8d956e6f 100644 --- a/test/native/src/test/java/org/apache/shardingsphere/test/natived/jdbc/commons/TestShardingService.java +++ b/test/native/src/test/java/org/apache/shardingsphere/test/natived/jdbc/commons/TestShardingService.java @@ -81,6 +81,7 @@ public void processSuccess() throws SQLException { assertThat(orderRepository.selectAll(), equalTo(new ArrayList<>())); assertThat(orderItemRepository.selectAll(), equalTo(new ArrayList<>())); assertThat(addressRepository.selectAll(), equalTo(new ArrayList<>())); + addressRepository.assertRollbackWithTransactions(); } /** diff --git a/test/native/src/test/java/org/apache/shardingsphere/test/natived/jdbc/commons/algorithm/ClassBasedInlineShardingAlgorithmFixture.java b/test/native/src/test/java/org/apache/shardingsphere/test/natived/jdbc/commons/algorithm/ClassBasedInlineShardingAlgorithmFixture.java index 9923409a72983a..c561a26c9b0087 100644 --- a/test/native/src/test/java/org/apache/shardingsphere/test/natived/jdbc/commons/algorithm/ClassBasedInlineShardingAlgorithmFixture.java +++ b/test/native/src/test/java/org/apache/shardingsphere/test/natived/jdbc/commons/algorithm/ClassBasedInlineShardingAlgorithmFixture.java @@ -23,7 +23,6 @@ import java.util.Collection; -@SuppressWarnings("unused") public final class ClassBasedInlineShardingAlgorithmFixture implements StandardShardingAlgorithm { @Override diff --git a/test/native/src/test/java/org/apache/shardingsphere/test/natived/jdbc/commons/repository/AddressRepository.java b/test/native/src/test/java/org/apache/shardingsphere/test/natived/jdbc/commons/repository/AddressRepository.java index 4dcf0c7e2aa88e..20dae55798024e 100644 --- a/test/native/src/test/java/org/apache/shardingsphere/test/natived/jdbc/commons/repository/AddressRepository.java +++ b/test/native/src/test/java/org/apache/shardingsphere/test/natived/jdbc/commons/repository/AddressRepository.java @@ -28,6 +28,9 @@ import java.util.LinkedList; import java.util.List; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + @SuppressWarnings({"SqlDialectInspection", "SqlNoDataSourceInspection"}) public final class AddressRepository { @@ -39,6 +42,7 @@ public AddressRepository(final DataSource dataSource) { /** * create table t_address if not exists. + * * @throws SQLException SQL exception */ public void createTableIfNotExists() throws SQLException { @@ -53,6 +57,7 @@ public void createTableIfNotExists() throws SQLException { /** * create table t_address in MS SQL Server. * This also ignored the default schema of the `dbo`. + * * @throws SQLException SQL exception */ public void createTableInSQLServer() throws SQLException { @@ -70,6 +75,7 @@ public void createTableInSQLServer() throws SQLException { /** * drop table t_address. + * * @throws SQLException SQL exception */ public void dropTable() throws SQLException { @@ -83,6 +89,7 @@ public void dropTable() throws SQLException { /** * truncate table t_address. + * * @throws SQLException SQL exception */ public void truncateTable() throws SQLException { @@ -96,6 +103,7 @@ public void truncateTable() throws SQLException { /** * insert something to table t_address. + * * @param address address * @return addressId of the insert statement * @throws SQLException SQL exception @@ -114,6 +122,7 @@ public Long insert(final Address address) throws SQLException { /** * delete by id. + * * @param id id * @throws SQLException SQL exception */ @@ -129,6 +138,7 @@ public void delete(final Long id) throws SQLException { /** * select all. + * * @return list of address * @throws SQLException SQL exception */ @@ -148,4 +158,30 @@ public List

selectAll() throws SQLException { } return result; } + + /** + * Assert rollback with transactions. + * This is currently just a simple test against a non-existent table and does not involve the competition scenario of distributed transactions. + * + * @throws SQLException An exception that provides information on a database access error or other errors. + */ + public void assertRollbackWithTransactions() throws SQLException { + Connection connection = dataSource.getConnection(); + try { + connection.setAutoCommit(false); + connection.createStatement().executeUpdate("INSERT INTO t_address (address_id, address_name) VALUES (2024, 'address_test_2024')"); + connection.createStatement().executeUpdate("INSERT INTO t_table_does_not_exist (test_id_does_not_exist) VALUES (2024)"); + connection.commit(); + } catch (SQLException e) { + connection.rollback(); + } finally { + connection.setAutoCommit(true); + connection.close(); + } + try ( + Connection conn = dataSource.getConnection(); + ResultSet resultSet = conn.createStatement().executeQuery("SELECT * FROM t_address WHERE address_id = 2024")) { + assertThat(resultSet.next(), is(false)); + } + } } diff --git a/test/native/src/test/java/org/apache/shardingsphere/test/natived/jdbc/transactions/base/SeataTest.java b/test/native/src/test/java/org/apache/shardingsphere/test/natived/jdbc/transactions/base/SeataTest.java new file mode 100644 index 00000000000000..5b1d1592588a97 --- /dev/null +++ b/test/native/src/test/java/org/apache/shardingsphere/test/natived/jdbc/transactions/base/SeataTest.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.shardingsphere.test.natived.jdbc.transactions.base; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import org.apache.shardingsphere.test.natived.jdbc.commons.TestShardingService; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledInNativeImage; +import org.testcontainers.containers.FixedHostPortGenericContainer; +import org.testcontainers.containers.GenericContainer; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.Properties; +import java.util.stream.Stream; + +class SeataTest { + + private TestShardingService testShardingService; + + @SuppressWarnings({"resource", "deprecation"}) + @Test + @EnabledInNativeImage + void assertShardingInSeataTransactions() throws SQLException { + try ( + GenericContainer container = new FixedHostPortGenericContainer<>("seataio/seata-server:1.8.0") + .withFixedExposedPort(39567, 8091)) { + container.start(); + DataSource dataSource = createDataSource(); + testShardingService = new TestShardingService(dataSource); + this.initEnvironment(); + testShardingService.processSuccess(); + testShardingService.cleanEnvironment(); + } + } + + private void initEnvironment() throws SQLException { + testShardingService.getOrderRepository().createTableIfNotExistsInPostgres(); + testShardingService.getOrderItemRepository().createTableIfNotExistsInPostgres(); + testShardingService.getAddressRepository().createTableIfNotExists(); + testShardingService.getOrderRepository().truncateTable(); + testShardingService.getOrderItemRepository().truncateTable(); + testShardingService.getAddressRepository().truncateTable(); + } + + private Connection openConnection(final String databaseName) throws SQLException { + String jdbcUrlPrefix = "jdbc:tc:postgresql:16.2-bookworm://test-native-transactions-base/"; + return DriverManager.getConnection(jdbcUrlPrefix + databaseName + "?TC_DAEMON=true", new Properties()); + } + + private DataSource createDataSource() { + String firstSql = "CREATE TABLE IF NOT EXISTS public.undo_log\n" + + "(\n" + + " id SERIAL NOT NULL,\n" + + " branch_id BIGINT NOT NULL,\n" + + " xid VARCHAR(128) NOT NULL,\n" + + " context VARCHAR(128) NOT NULL,\n" + + " rollback_info BYTEA NOT NULL,\n" + + " log_status INT NOT NULL,\n" + + " log_created TIMESTAMP(0) NOT NULL,\n" + + " log_modified TIMESTAMP(0) NOT NULL,\n" + + " CONSTRAINT pk_undo_log PRIMARY KEY (id),\n" + + " CONSTRAINT ux_undo_log UNIQUE (xid, branch_id)\n" + + ");"; + String secondSql = "CREATE INDEX ix_log_created ON undo_log(log_created);"; + String thirdSql = "COMMENT ON TABLE public.undo_log IS 'AT transaction mode undo table';"; + String fourthSql = "COMMENT ON COLUMN public.undo_log.branch_id IS 'branch transaction id';"; + String fifthSql = "COMMENT ON COLUMN public.undo_log.xid IS 'global transaction id';"; + String sixthSql = "COMMENT ON COLUMN public.undo_log.context IS 'undo_log context,such as serialization';"; + String seventhSql = "COMMENT ON COLUMN public.undo_log.rollback_info IS 'rollback info';"; + String eighthSql = "COMMENT ON COLUMN public.undo_log.log_status IS '0:normal status,1:defense status';"; + String ninthSql = "COMMENT ON COLUMN public.undo_log.log_created IS 'create datetime';"; + String tenthSql = "COMMENT ON COLUMN public.undo_log.log_modified IS 'modify datetime';"; + String eleventhSql = "CREATE SEQUENCE IF NOT EXISTS undo_log_id_seq INCREMENT BY 1 MINVALUE 1 ;"; + Stream.of(firstSql, secondSql, thirdSql, fourthSql, fifthSql, sixthSql, seventhSql, eighthSql, ninthSql, tenthSql, eleventhSql) + .forEachOrdered(this::executeSqlToShardingDB); + HikariConfig config = new HikariConfig(); + config.setDriverClassName("org.apache.shardingsphere.driver.ShardingSphereDriver"); + config.setJdbcUrl("jdbc:shardingsphere:classpath:test-native/yaml/transactions/base/seata.yaml"); + return new HikariDataSource(config); + } + + private void executeSqlToShardingDB(final String sqlString) { + try ( + Connection ds0Connection = openConnection("demo_ds_0"); + Connection ds1Connection = openConnection("demo_ds_1"); + Connection ds2Connection = openConnection("demo_ds_2")) { + ds0Connection.createStatement().executeUpdate(sqlString); + ds1Connection.createStatement().executeUpdate(sqlString); + ds2Connection.createStatement().executeUpdate(sqlString); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } +} diff --git a/test/native/src/test/resources/META-INF/native-image/shardingsphere-test-native-test-metadata/reflect-config.json b/test/native/src/test/resources/META-INF/native-image/shardingsphere-test-native-test-metadata/reflect-config.json index 2c18921fb05aad..74ded5e33d0d76 100644 --- a/test/native/src/test/resources/META-INF/native-image/shardingsphere-test-native-test-metadata/reflect-config.json +++ b/test/native/src/test/resources/META-INF/native-image/shardingsphere-test-native-test-metadata/reflect-config.json @@ -5,7 +5,7 @@ "methods":[{"name":"","parameterTypes":[] }] }, { - "condition":{"typeReachable":"org.apache.shardingsphere.encrypt.rule.EncryptRule"}, + "condition":{"typeReachable":"org.apache.shardingsphere.test.natived.jdbc.features.EncryptTest"}, "name":"org.apache.shardingsphere.test.natived.jdbc.commons.algorithm.TestQueryAssistedShardingEncryptAlgorithm", "methods":[{"name":"","parameterTypes":[] }] } diff --git a/test/native/src/test/resources/META-INF/native-image/shardingsphere-test-native-test-metadata/resource-config.json b/test/native/src/test/resources/META-INF/native-image/shardingsphere-test-native-test-metadata/resource-config.json index eb118f6ef69d3f..7930e72de9862f 100644 --- a/test/native/src/test/resources/META-INF/native-image/shardingsphere-test-native-test-metadata/resource-config.json +++ b/test/native/src/test/resources/META-INF/native-image/shardingsphere-test-native-test-metadata/resource-config.json @@ -1,7 +1,7 @@ { "resources":{ "includes":[{ - "condition":{"typeReachable":"org.apache.shardingsphere.mode.metadata.NewMetaDataContextsFactory"}, + "condition":{"typeReachable":"org.apache.shardingsphere.test.natived.jdbc.databases.SQLServerTest"}, "pattern":"\\Qcontainer-license-acceptance.txt\\E" }, { "condition":{"typeReachable":"ch.qos.logback.core.util.Loader"}, @@ -45,6 +45,18 @@ }, { "condition":{"typeReachable":"org.apache.shardingsphere.test.natived.jdbc.transactions.xa.NarayanaTest"}, "pattern":"\\Qtest-native/yaml/transactions/xa/narayana.yaml\\E" + }, { + "condition":{"typeReachable":"org.apache.shardingsphere.test.natived.jdbc.transactions.base.SeataTest"}, + "pattern":"\\Qtest-native/yaml/transactions/base/seata.yaml\\E" + }, { + "condition":{"typeReachable":"org.apache.shardingsphere.test.natived.jdbc.transactions.base.SeataTest"}, + "pattern":"\\Qfile.conf\\E" + }, { + "condition":{"typeReachable":"org.apache.shardingsphere.test.natived.jdbc.transactions.base.SeataTest"}, + "pattern":"\\Qregistry.conf\\E" + },{ + "condition":{"typeReachable":"org.apache.shardingsphere.test.natived.jdbc.transactions.base.SeataTest"}, + "pattern":"\\Qseata.conf\\E" }]}, "bundles":[] } diff --git a/test/native/src/test/resources/file.conf b/test/native/src/test/resources/file.conf new file mode 100644 index 00000000000000..b6b8dc5b3c028b --- /dev/null +++ b/test/native/src/test/resources/file.conf @@ -0,0 +1,21 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +service { + vgroupMapping.default_tx_group = "default" + default.grouplist = "127.0.0.1:39567" +} diff --git a/test/native/src/test/resources/registry.conf b/test/native/src/test/resources/registry.conf new file mode 100644 index 00000000000000..961283a7f82fa1 --- /dev/null +++ b/test/native/src/test/resources/registry.conf @@ -0,0 +1,30 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +registry { + type = "file" + file { + name = "file.conf" + } +} + +config { + type = "file" + file { + name = "file.conf" + } +} diff --git a/test/native/src/test/resources/seata.conf b/test/native/src/test/resources/seata.conf new file mode 100644 index 00000000000000..5026984ee401e7 --- /dev/null +++ b/test/native/src/test/resources/seata.conf @@ -0,0 +1,24 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +sharding.transaction.seata.at.enable = true +sharding.transaction.seata.tx.timeout = 60 + +client { + application.id = test-native + transaction.service.group = default_tx_group +} diff --git a/test/native/src/test/resources/test-native/yaml/transactions/base/seata.yaml b/test/native/src/test/resources/test-native/yaml/transactions/base/seata.yaml new file mode 100644 index 00000000000000..7dab3be115bc7c --- /dev/null +++ b/test/native/src/test/resources/test-native/yaml/transactions/base/seata.yaml @@ -0,0 +1,76 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# + +mode: + type: Standalone + repository: + type: JDBC + +dataSources: + ds_0: + dataSourceClassName: com.zaxxer.hikari.HikariDataSource + driverClassName: org.testcontainers.jdbc.ContainerDatabaseDriver + jdbcUrl: jdbc:tc:postgresql:16.2-bookworm://test-native-transactions-base/demo_ds_0?TC_DAEMON=true + ds_1: + dataSourceClassName: com.zaxxer.hikari.HikariDataSource + driverClassName: org.testcontainers.jdbc.ContainerDatabaseDriver + jdbcUrl: jdbc:tc:postgresql:16.2-bookworm://test-native-transactions-base/demo_ds_1?TC_DAEMON=true + ds_2: + dataSourceClassName: com.zaxxer.hikari.HikariDataSource + driverClassName: org.testcontainers.jdbc.ContainerDatabaseDriver + jdbcUrl: jdbc:tc:postgresql:16.2-bookworm://test-native-transactions-base/demo_ds_2?TC_DAEMON=true + +rules: +- !SHARDING + tables: + t_order: + actualDataNodes: + keyGenerateStrategy: + column: order_id + keyGeneratorName: snowflake + t_order_item: + actualDataNodes: + keyGenerateStrategy: + column: order_item_id + keyGeneratorName: snowflake + defaultDatabaseStrategy: + standard: + shardingColumn: user_id + shardingAlgorithmName: inline + shardingAlgorithms: + inline: + type: CLASS_BASED + props: + strategy: STANDARD + algorithmClassName: org.apache.shardingsphere.test.natived.jdbc.commons.algorithm.ClassBasedInlineShardingAlgorithmFixture + keyGenerators: + snowflake: + type: SNOWFLAKE + auditors: + sharding_key_required_auditor: + type: DML_SHARDING_CONDITIONS + +- !BROADCAST + tables: + - t_address + +transaction: + defaultType: BASE + providerType: Seata + +props: + sql-show: false