From ab0ed3d581059a0ff18117f30fa48db75f8f5e3b Mon Sep 17 00:00:00 2001 From: Ling Hengqian Date: Thu, 22 Feb 2024 10:19:25 +0800 Subject: [PATCH] Add GraalVM Reachability Metadata and corresponding nativeTest for Seata integration (#30138) --- .../graalvm-native-image/_index.cn.md | 54 ++++++- .../graalvm-native-image/_index.en.md | 63 ++++++-- .../special-api/transaction/seata.cn.md | 8 +- .../special-api/transaction/seata.en.md | 8 +- .../caffeine/2.9.3/reflect-config.json | 21 ++- .../reflect-config.json | 10 ++ .../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 | 146 ++++++++++++++++++ .../resource-config.json | 12 ++ test/native/src/test/resources/file.conf | 24 +++ test/native/src/test/resources/registry.conf | 30 ++++ test/native/src/test/resources/seata.conf | 21 +++ .../yaml/transactions/base/seata.yaml | 76 +++++++++ 17 files changed, 519 insertions(+), 26 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 ba5877f4750cb..283ccba524810 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:${shardingsphere.version}' + implementation(group: 'org.graalvm.buildtools', name: 'graalvm-reachability-metadata', version: '0.10.0', classifier: 'repository', ext: 'zip') } 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 + ${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 52dbadae44c3d..adfca28f289c3 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:${shardingsphere.version}' + implementation 'org.apache.shardingsphere:shardingsphere-jdbc:${shardingsphere.version}' + implementation(group: 'org.graalvm.buildtools', name: 'graalvm-reachability-metadata', version: '0.10.0', classifier: 'repository', ext: 'zip') } 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 + ${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/docs/document/content/user-manual/shardingsphere-jdbc/special-api/transaction/seata.cn.md b/docs/document/content/user-manual/shardingsphere-jdbc/special-api/transaction/seata.cn.md index 81ecb0034336d..3aba211b51b89 100644 --- a/docs/document/content/user-manual/shardingsphere-jdbc/special-api/transaction/seata.cn.md +++ b/docs/document/content/user-manual/shardingsphere-jdbc/special-api/transaction/seata.cn.md @@ -89,7 +89,7 @@ ALTER TABLE `undo_log` ADD INDEX `ix_log_created` (`log_created`); ```conf sharding.transaction.seata.at.enable = true -sharding.transaction.seata.tx.timeout = 30 +sharding.transaction.seata.tx.timeout = 60 client { application.id = example @@ -117,9 +117,11 @@ ShardingSphere 的 Seata 集成将获取到的 Seata 全局事务置入线程的 3. 在函数上使用 Jakarta EE 9/10 的 `jakarta.transaction.Transactional` 注解,这是被允许的。 -4. 在函数上使用 `io.seata.spring.annotation.GlobalTransactional` 注解,这是不被允许的。 +4. 在函数上使用 Spring Framework 的 `org.springframework.transaction.annotation.Transactional` 注解,这是被允许的。 -5. 手动从 `io.seata.tm.api.GlobalTransactionContext ` 创建 `io.seata.tm.api.GlobalTransaction` 实例, +5. 在函数上使用 `io.seata.spring.annotation.GlobalTransactional` 注解,这是不被允许的。 + +6. 手动从 `io.seata.tm.api.GlobalTransactionContext ` 创建 `io.seata.tm.api.GlobalTransaction` 实例, 调用 `io.seata.tm.api.GlobalTransaction` 实例的 `begin()`, `commit()` 和 `rollback()` 方法,这是不被允许的。 长话短说,在使用 ShardingSphere 的 Seata 集成时,你不应该使用 Seata Java API。 diff --git a/docs/document/content/user-manual/shardingsphere-jdbc/special-api/transaction/seata.en.md b/docs/document/content/user-manual/shardingsphere-jdbc/special-api/transaction/seata.en.md index 3bc47267962ee..22d58d09d5fa4 100644 --- a/docs/document/content/user-manual/shardingsphere-jdbc/special-api/transaction/seata.en.md +++ b/docs/document/content/user-manual/shardingsphere-jdbc/special-api/transaction/seata.en.md @@ -90,7 +90,7 @@ A fully configured `seata.conf` is as follows, ```conf sharding.transaction.seata.at.enable = true -sharding.transaction.seata.tx.timeout = 30 +sharding.transaction.seata.tx.timeout = 60 client { application.id = example @@ -118,9 +118,11 @@ For ShardingSphere data source, discuss 5 situations, 3. Using Jakarta EE 9/10’s `jakarta.transaction.Transactional` annotation on functions is allowed. -4. Using the `io.seata.spring.annotation.GlobalTransactional` annotation on the function is not allowed. +4. Using Spring Framework’s `org.springframework.transaction.annotation.Transactional` annotation on functions is allowed. -5. Manually create `io.seata.tm.api.GlobalTransaction` instance from `io.seata.tm.api.GlobalTransactionContext`, +5. Using the `io.seata.spring.annotation.GlobalTransactional` annotation on the function is not allowed. + +6. Manually create `io.seata.tm.api.GlobalTransaction` instance from `io.seata.tm.api.GlobalTransactionContext`, calling the `begin()`, `commit()` and `rollback()` methods of an `io.seata.tm.api.GlobalTransaction` instance is not allowed. Long story short, you should not use the Seata Java API when using ShardingSphere's Seata integration. 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 995222a3c6b51..410db67b1c2d9 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 56e1291380d1a..d2dbe70ffa6a2 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 @@ -1166,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/test/native/native-image-filter/extra-filter.json b/test/native/native-image-filter/extra-filter.json index facc03118b9fb..197076d15ed76 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 f97bdef87791b..c83344a4996bf 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 501c19efdcbbf..8e734f8d956e6 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 9923409a72983..c561a26c9b008 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 4dcf0c7e2aa88..20dae55798024 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 0000000000000..24c845fd3825f --- /dev/null +++ b/test/native/src/test/java/org/apache/shardingsphere/test/natived/jdbc/transactions/base/SeataTest.java @@ -0,0 +1,146 @@ +/* + * 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.hc.client5.http.classic.methods.HttpGet; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.core5.http.HttpStatus; +import org.apache.hc.core5.http.io.entity.EntityUtils; +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 javax.sql.DataSource; +import java.io.IOException; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.time.Duration; +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) + .withExposedPorts(7091)) { + container.start(); + DataSource dataSource = createDataSource(container.getMappedPort(7091)); + 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 { + final 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(final Integer seataServerHealthCheckPort) { + Awaitility.await().atMost(Duration.ofMinutes(1)).ignoreExceptions() + .until(() -> verifySeataServerRunning(seataServerHealthCheckPort)); + final 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" + + ");"; + final String secondSql = "CREATE INDEX ix_log_created ON undo_log(log_created);"; + final String thirdSql = "COMMENT ON TABLE public.undo_log IS 'AT transaction mode undo table';"; + final String fourthSql = "COMMENT ON COLUMN public.undo_log.branch_id IS 'branch transaction id';"; + final String fifthSql = "COMMENT ON COLUMN public.undo_log.xid IS 'global transaction id';"; + final String sixthSql = "COMMENT ON COLUMN public.undo_log.context IS 'undo_log context,such as serialization';"; + final String seventhSql = "COMMENT ON COLUMN public.undo_log.rollback_info IS 'rollback info';"; + final String eighthSql = "COMMENT ON COLUMN public.undo_log.log_status IS '0:normal status,1:defense status';"; + final String ninthSql = "COMMENT ON COLUMN public.undo_log.log_created IS 'create datetime';"; + final String tenthSql = "COMMENT ON COLUMN public.undo_log.log_modified IS 'modify datetime';"; + final 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); + } + + /** + * Further processing of this class awaits apache/incubator-seata#6356. + * + * @param seataServerHealthCheckPort The port of the host corresponding to port `7091` inside the container + * @return Returns true if Seata Server is running normally. + * @throws IOException Signals that an I/O exception to some sort has occurred. + */ + private static boolean verifySeataServerRunning(final Integer seataServerHealthCheckPort) throws IOException { + boolean flag = false; + HttpGet httpGet = new HttpGet("http://localhost:" + seataServerHealthCheckPort + "/health"); + try ( + CloseableHttpClient httpclient = HttpClients.createDefault(); + CloseableHttpResponse response = httpclient.execute(httpGet)) { + if (HttpStatus.SC_UNAUTHORIZED == response.getCode()) { + flag = true; + } + EntityUtils.consume(response.getEntity()); + } + return flag; + } + + 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/resource-config.json b/test/native/src/test/resources/META-INF/native-image/shardingsphere-test-native-test-metadata/resource-config.json index 9722265d59c63..7930e72de9862 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 @@ -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 0000000000000..7e3b7556951e4 --- /dev/null +++ b/test/native/src/test/resources/file.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. +# + +# TODO Due to limitations of `io.seata:seata-all:1.8.0`, +# there is no ability to dynamically define `service.default.grouplist` without using Spring Boot. +# This requires further investigation. +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 0000000000000..961283a7f82fa --- /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 0000000000000..9774285f1faf7 --- /dev/null +++ b/test/native/src/test/resources/seata.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. +# + +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 0000000000000..7dab3be115bc7 --- /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