From c5b01a0388e1f1561b4924a667b7a21828b0260a 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 ++--- .../driver/url/type/ClassPathURLLoader.java | 19 ++- .../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 +++++ .../natived/jdbc/databases/MySQLTest.java | 3 +- .../jdbc/transactions/base/SeataTest.java | 149 ++++++++++++++++++ .../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 | 82 ++++++++++ 18 files changed, 540 insertions(+), 57 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..fab5dd0b85a0be 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` 中过时的 `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..72b6e7b365392a 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`. +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/jdbc/core/src/main/java/org/apache/shardingsphere/driver/jdbc/core/driver/url/type/ClassPathURLLoader.java b/jdbc/core/src/main/java/org/apache/shardingsphere/driver/jdbc/core/driver/url/type/ClassPathURLLoader.java index 31331c4b0dec88..00186b5b7a8da3 100644 --- a/jdbc/core/src/main/java/org/apache/shardingsphere/driver/jdbc/core/driver/url/type/ClassPathURLLoader.java +++ b/jdbc/core/src/main/java/org/apache/shardingsphere/driver/jdbc/core/driver/url/type/ClassPathURLLoader.java @@ -20,10 +20,15 @@ import lombok.SneakyThrows; import org.apache.shardingsphere.driver.jdbc.core.driver.url.ShardingSphereURLLoader; -import java.io.File; import java.io.IOException; +import java.net.URI; import java.net.URISyntaxException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collections; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; @@ -36,12 +41,18 @@ public final class ClassPathURLLoader implements ShardingSphereURLLoader { @Override @SneakyThrows(IOException.class) public String load(final String configurationSubject, final Map parameters) { - return Files.readAllLines(getResourceFile(configurationSubject).toPath()).stream().collect(Collectors.joining(System.lineSeparator())); + if (null == System.getProperty("org.graalvm.nativeimage.imagecode") || !"runtime".equals(System.getProperty("org.graalvm.nativeimage.imagecode"))) { + return Files.readAllLines(getResourcePath(configurationSubject)).stream().collect(Collectors.joining(System.lineSeparator())); + } else { + try (FileSystem ignored = FileSystems.newFileSystem(URI.create("resource:/"), Collections.emptyMap())) { + return Files.readAllLines(getResourcePath(configurationSubject)).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()); + private Path getResourcePath(final String configurationSubject) { + return Paths.get(Objects.requireNonNull(Thread.currentThread().getContextClassLoader().getResource(configurationSubject)).toURI()); } @Override 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..e884bd58e22933 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/databases/MySQLTest.java b/test/native/src/test/java/org/apache/shardingsphere/test/natived/jdbc/databases/MySQLTest.java index 32d8a37321566c..108bb97bcefff1 100644 --- a/test/native/src/test/java/org/apache/shardingsphere/test/natived/jdbc/databases/MySQLTest.java +++ b/test/native/src/test/java/org/apache/shardingsphere/test/natived/jdbc/databases/MySQLTest.java @@ -17,7 +17,6 @@ package org.apache.shardingsphere.test.natived.jdbc.databases; -import com.mysql.cj.jdbc.exceptions.CommunicationsException; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import org.apache.shardingsphere.test.natived.jdbc.commons.TestShardingService; @@ -93,7 +92,7 @@ private Connection openConnection() throws SQLException { @SuppressWarnings({"SqlDialectInspection", "SqlNoDataSourceInspection"}) private DataSource createDataSource() { - Awaitility.await().atMost(Duration.ofMinutes(1)).ignoreExceptionsMatching(e -> e instanceof CommunicationsException) + Awaitility.await().atMost(Duration.ofMinutes(1)).ignoreExceptions() .until(() -> { openConnection().close(); return true; 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..fedcfa04801c3f --- /dev/null +++ b/test/native/src/test/java/org/apache/shardingsphere/test/natived/jdbc/transactions/base/SeataTest.java @@ -0,0 +1,149 @@ +/* + * 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.awaitility.Awaitility; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledInNativeImage; +import org.testcontainers.containers.FixedHostPortGenericContainer; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.utility.DockerImageName; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.time.Duration; +import java.util.Properties; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; + +class SeataTest { + + private static final String SYSTEM_PROP_KEY_PREFIX = "fixture.test-native.yaml.transactions.base."; + + private static final String USERNAME = "root"; + + private static final String PASSWORD = "123456"; + + private static final String DATABASE = "test"; + + private String jdbcUrlPrefix; + + 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); + GenericContainer mySqlContainer = new GenericContainer<>(DockerImageName.parse("mysql:8.3.0-oraclelinux8")) + .withEnv("MYSQL_DATABASE", DATABASE) + .withEnv("MYSQL_ROOT_PASSWORD", PASSWORD) + .withExposedPorts(3306)) { + container.start(); + mySqlContainer.start(); + jdbcUrlPrefix = "jdbc:mysql://localhost:" + mySqlContainer.getMappedPort(3306) + "/"; + DataSource dataSource = createDataSource(); + testShardingService = new TestShardingService(dataSource); + this.initEnvironment(); + testShardingService.processSuccess(); + testShardingService.cleanEnvironment(); + } + } + + private void initEnvironment() throws SQLException { + testShardingService.getOrderRepository().createTableIfNotExistsInMySQL(); + testShardingService.getOrderItemRepository().createTableIfNotExistsInMySQL(); + testShardingService.getAddressRepository().createTableIfNotExists(); + testShardingService.getOrderRepository().truncateTable(); + testShardingService.getOrderItemRepository().truncateTable(); + testShardingService.getAddressRepository().truncateTable(); + } + + private Connection openConnection(final String databaseName) throws SQLException { + Properties props = new Properties(); + props.setProperty("user", USERNAME); + props.setProperty("password", PASSWORD); + return DriverManager.getConnection(jdbcUrlPrefix + databaseName, props); + } + + @SuppressWarnings({"SqlNoDataSourceInspection", "SqlDialectInspection"}) + private DataSource createDataSource() { + Awaitility.await().atMost(Duration.ofMinutes(1)).ignoreExceptions() + .until(() -> { + openConnection(DATABASE).close(); + return true; + }); + try (Connection connection = openConnection(DATABASE)) { + connection.createStatement().executeUpdate("CREATE DATABASE demo_ds_0;"); + connection.createStatement().executeUpdate("CREATE DATABASE demo_ds_1;"); + connection.createStatement().executeUpdate("CREATE DATABASE demo_ds_2;"); + } catch (SQLException e) { + throw new RuntimeException(e); + } + String firstSql = "CREATE TABLE IF NOT EXISTS `undo_log`\n" + + "(\n" + + " `branch_id` BIGINT NOT NULL COMMENT 'branch transaction id',\n" + + " `xid` VARCHAR(128) NOT NULL COMMENT 'global transaction id',\n" + + " `context` VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',\n" + + " `rollback_info` LONGBLOB NOT NULL COMMENT 'rollback info',\n" + + " `log_status` INT(11) NOT NULL COMMENT '0:normal status,1:defense status',\n" + + " `log_created` DATETIME(6) NOT NULL COMMENT 'create datetime',\n" + + " `log_modified` DATETIME(6) NOT NULL COMMENT 'modify datetime',\n" + + " UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)\n" + + ") ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table';"; + String secondSql = "ALTER TABLE `undo_log` ADD INDEX `ix_log_created` (`log_created`);"; + try ( + Connection ds0Connection = openConnection("demo_ds_0"); + Connection ds1Connection = openConnection("demo_ds_1"); + Connection ds2Connection = openConnection("demo_ds_2")) { + ds0Connection.createStatement().executeUpdate(firstSql); + ds0Connection.createStatement().executeUpdate(secondSql); + ds1Connection.createStatement().executeUpdate(firstSql); + ds1Connection.createStatement().executeUpdate(secondSql); + ds2Connection.createStatement().executeUpdate(firstSql); + ds2Connection.createStatement().executeUpdate(secondSql); + } catch (SQLException e) { + throw new RuntimeException(e); + } + HikariConfig config = new HikariConfig(); + config.setDriverClassName("org.apache.shardingsphere.driver.ShardingSphereDriver"); + config.setJdbcUrl("jdbc:shardingsphere:classpath:test-native/yaml/transactions/base/seata.yaml?placeholder-type=system_props"); + try { + assertThat(System.getProperty(SYSTEM_PROP_KEY_PREFIX + "ds0.jdbc-url"), is(nullValue())); + assertThat(System.getProperty(SYSTEM_PROP_KEY_PREFIX + "ds1.jdbc-url"), is(nullValue())); + assertThat(System.getProperty(SYSTEM_PROP_KEY_PREFIX + "ds2.jdbc-url"), is(nullValue())); + System.setProperty(SYSTEM_PROP_KEY_PREFIX + "ds0.jdbc-url", jdbcUrlPrefix + "demo_ds_0"); + System.setProperty(SYSTEM_PROP_KEY_PREFIX + "ds1.jdbc-url", jdbcUrlPrefix + "demo_ds_1"); + System.setProperty(SYSTEM_PROP_KEY_PREFIX + "ds2.jdbc-url", jdbcUrlPrefix + "demo_ds_2"); + return new HikariDataSource(config); + } finally { + System.clearProperty(SYSTEM_PROP_KEY_PREFIX + "ds0.jdbc-url"); + System.clearProperty(SYSTEM_PROP_KEY_PREFIX + "ds1.jdbc-url"); + System.clearProperty(SYSTEM_PROP_KEY_PREFIX + "ds2.jdbc-url"); + } + } +} 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..0c4e4d7a7d0fe0 --- /dev/null +++ b/test/native/src/test/resources/test-native/yaml/transactions/base/seata.yaml @@ -0,0 +1,82 @@ +# +# 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: com.mysql.cj.jdbc.Driver + jdbcUrl: $${fixture.test-native.yaml.transactions.base.ds0.jdbc-url::} + username: root + password: 123456 + ds_1: + dataSourceClassName: com.zaxxer.hikari.HikariDataSource + driverClassName: com.mysql.cj.jdbc.Driver + jdbcUrl: $${fixture.test-native.yaml.transactions.base.ds1.jdbc-url::} + username: root + password: 123456 + ds_2: + dataSourceClassName: com.zaxxer.hikari.HikariDataSource + driverClassName: com.mysql.cj.jdbc.Driver + jdbcUrl: $${fixture.test-native.yaml.transactions.base.ds2.jdbc-url::} + username: root + password: 123456 + +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