diff --git a/README.md b/README.md index 08c322d62..883349ab7 100644 --- a/README.md +++ b/README.md @@ -575,6 +575,12 @@ public class AWSSecretsManagerPluginSample { } ``` +## Reader Cluster Connection Plugin + +When connecting to an Amazon Aurora database using the reader endpoint, the endpoint will load balance connections between all the available Aurora Replicas. In situations where the AWS JDBC Driver for MySQL needs to create a new connection internally, the new connection may or may not be to the same instance the original connection was made to. This means any processes that require the same instance will result in errors. For example, setting query timeouts may result in errors due to the kill query being sent to the incorrect instance. In these cases, the Reader Cluster Connection Plugin can be used to ensure all new connections are made to the same reader. + +The Reader Cluster Connection Plugin is not enabled by default and can be enabled by using the [`connectionPluginFactories`](#connection-plugin-manager-parameters). + ## Extra Additions ### XML Entity Injection Fix @@ -814,7 +820,6 @@ public class AWSSecretsManagerPluginSample2 { ``` - ## Getting Help and Opening Issues If you encounter a bug with the AWS JDBC Driver for MySQL, we would like to hear about it. Please search the [existing issues](https://github.com/awslabs/aws-mysql-jdbc/issues) and see if others are also experiencing the issue before opening a new issue. When opening a new issue, we will need the version of AWS JDBC Driver for MySQL, Java language version, OS you’re using, and the MySQL database version you're running against. Please include a reproduction case for the issue when appropriate. diff --git a/src/main/core-impl/java/com/mysql/cj/NativeSession.java b/src/main/core-impl/java/com/mysql/cj/NativeSession.java index cae714d7f..84eb1e066 100644 --- a/src/main/core-impl/java/com/mysql/cj/NativeSession.java +++ b/src/main/core-impl/java/com/mysql/cj/NativeSession.java @@ -60,7 +60,7 @@ import com.mysql.cj.exceptions.MysqlErrorNumbers; import com.mysql.cj.exceptions.OperationCancelledException; import com.mysql.cj.interceptors.QueryInterceptor; -import com.mysql.cj.jdbc.ha.ConnectionUtils; +import com.mysql.cj.jdbc.ha.util.ConnectionUtils; import com.mysql.cj.log.Log; import com.mysql.cj.protocol.ColumnDefinition; import com.mysql.cj.protocol.NetworkResources; diff --git a/src/main/user-impl/java/com/mysql/cj/jdbc/ha/FailoverConnectionProxy.java b/src/main/user-impl/java/com/mysql/cj/jdbc/ha/FailoverConnectionProxy.java index 47b6a8e9f..39c2bc403 100644 --- a/src/main/user-impl/java/com/mysql/cj/jdbc/ha/FailoverConnectionProxy.java +++ b/src/main/user-impl/java/com/mysql/cj/jdbc/ha/FailoverConnectionProxy.java @@ -31,6 +31,7 @@ package com.mysql.cj.jdbc.ha; +import com.mysql.cj.jdbc.ha.util.ConnectionUtils; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.sql.SQLException; diff --git a/src/main/user-impl/java/com/mysql/cj/jdbc/ha/plugins/AWSSecretsManagerPlugin.java b/src/main/user-impl/java/com/mysql/cj/jdbc/ha/plugins/AWSSecretsManagerPlugin.java index d31247f12..3e48d7978 100644 --- a/src/main/user-impl/java/com/mysql/cj/jdbc/ha/plugins/AWSSecretsManagerPlugin.java +++ b/src/main/user-impl/java/com/mysql/cj/jdbc/ha/plugins/AWSSecretsManagerPlugin.java @@ -35,7 +35,7 @@ import com.mysql.cj.conf.HostInfo; import com.mysql.cj.conf.PropertySet; import com.mysql.cj.exceptions.CJException; -import com.mysql.cj.jdbc.ha.ConnectionUtils; +import com.mysql.cj.jdbc.ha.util.ConnectionUtils; import com.mysql.cj.log.Log; import com.mysql.cj.util.LRUCache; diff --git a/src/main/user-impl/java/com/mysql/cj/jdbc/ha/plugins/DefaultConnectionPlugin.java b/src/main/user-impl/java/com/mysql/cj/jdbc/ha/plugins/DefaultConnectionPlugin.java index 6e9efcdd8..514ed7084 100644 --- a/src/main/user-impl/java/com/mysql/cj/jdbc/ha/plugins/DefaultConnectionPlugin.java +++ b/src/main/user-impl/java/com/mysql/cj/jdbc/ha/plugins/DefaultConnectionPlugin.java @@ -100,8 +100,9 @@ public void openInitialConnection(ConnectionUrl connectionUrl) throws SQLExcepti return; } - HostInfo mainHostInfo = connectionUrl.getMainHost(); + final HostInfo mainHostInfo = connectionUrl.getMainHost(); JdbcConnection connection = this.connectionProvider.connect(mainHostInfo); + this.currentConnectionProvider.setCurrentConnection(connection, mainHostInfo); } diff --git a/src/main/user-impl/java/com/mysql/cj/jdbc/ha/plugins/ReaderClusterConnectionPlugin.java b/src/main/user-impl/java/com/mysql/cj/jdbc/ha/plugins/ReaderClusterConnectionPlugin.java new file mode 100644 index 000000000..31f89228d --- /dev/null +++ b/src/main/user-impl/java/com/mysql/cj/jdbc/ha/plugins/ReaderClusterConnectionPlugin.java @@ -0,0 +1,143 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2.0 + * (GPLv2), as published by the Free Software Foundation, with the + * following additional permissions: + * + * This program is distributed with certain software that is licensed + * under separate terms, as designated in a particular file or component + * or in the license documentation. Without limiting your rights under + * the GPLv2, the authors of this program hereby grant you an additional + * permission to link the program and your derivative works with the + * separately licensed software that they have included with the program. + * + * Without limiting the foregoing grant of rights under the GPLv2 and + * additional permission as to separately licensed software, this + * program is also subject to the Universal FOSS Exception, version 1.0, + * a copy of which can be found along with its FAQ at + * http://oss.oracle.com/licenses/universal-foss-exception. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License, version 2.0, for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * http://www.gnu.org/licenses/gpl-2.0.html. + */ + +package com.mysql.cj.jdbc.ha.plugins; + +import com.mysql.cj.conf.ConnectionUrl; +import com.mysql.cj.conf.HostInfo; +import com.mysql.cj.conf.PropertyKey; +import com.mysql.cj.jdbc.JdbcConnection; +import com.mysql.cj.jdbc.ha.util.ConnectionUtils; +import com.mysql.cj.jdbc.ha.util.RdsUtils; +import com.mysql.cj.util.StringUtils; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.concurrent.Callable; + +/** + * This connection plugin is used when connecting through reader cluster endpoints, where all new + * connections should be directed to the same reader instance rather than a random reader instance + * for each new connection. + */ +public class ReaderClusterConnectionPlugin implements IConnectionPlugin { + private static final String GET_INSTANCE_QUERY = "SELECT @@aurora_server_id"; + protected IConnectionProvider connectionProvider; + private final ICurrentConnectionProvider currentConnectionProvider; + private final IConnectionPlugin nextPlugin; + + public ReaderClusterConnectionPlugin( + ICurrentConnectionProvider currentConnectionProvider, + IConnectionPlugin nextPlugin) { + + this(currentConnectionProvider, + nextPlugin, + new BasicConnectionProvider()); + } + + public ReaderClusterConnectionPlugin( + ICurrentConnectionProvider currentConnectionProvider, + IConnectionPlugin nextPlugin, + IConnectionProvider connectionProvider) { + if (connectionProvider == null) { + throw new IllegalArgumentException(NullArgumentMessage.getMessage("connectionProvider")); + } + if (currentConnectionProvider == null) { + throw new IllegalArgumentException(NullArgumentMessage.getMessage("currentConnectionProvider")); + } + + this.currentConnectionProvider = currentConnectionProvider; + this.nextPlugin = nextPlugin; + this.connectionProvider = connectionProvider; + } + + @Override + public Object execute( + Class methodInvokeOn, + String methodName, + Callable executeSqlFunc, + Object[] args) + throws Exception { + return this.nextPlugin.execute(methodInvokeOn, methodName, executeSqlFunc, args); + } + + @Override + public void openInitialConnection(ConnectionUrl connectionUrl) throws SQLException { + this.nextPlugin.openInitialConnection(connectionUrl); + + final JdbcConnection currentConnection = this.currentConnectionProvider.getCurrentConnection(); + if (currentConnection != null) { + final HostInfo mainHostInfo = connectionUrl.getMainHost(); + final RdsUtils rdsUtils = new RdsUtils(); + + if (rdsUtils.isReaderClusterDns(mainHostInfo.getHost())) { + final String connectedHostName = getCurrentlyConnectedInstance(currentConnection); + if (!StringUtils.isNullOrEmpty(connectedHostName)) { + final String pattern = + mainHostInfo.getHostProperties().get(PropertyKey.clusterInstanceHostPattern.getKeyName()); + final String instanceEndpoint = !StringUtils.isNullOrEmpty(pattern) ? pattern : + rdsUtils.getRdsInstanceHostPattern(mainHostInfo.getHost()); + final HostInfo instanceHostInfo = + ConnectionUtils.createInstanceHostWithProperties( + instanceEndpoint.replace("?", connectedHostName), + mainHostInfo); + final JdbcConnection hostConnection = this.connectionProvider.connect(instanceHostInfo); + this.currentConnectionProvider.setCurrentConnection(hostConnection, instanceHostInfo); + } + } + } + } + + @Override + public void releaseResources() { + this.nextPlugin.releaseResources(); + } + + @Override + public void transactionBegun() { + this.nextPlugin.transactionBegun(); + } + + @Override + public void transactionCompleted() { + this.nextPlugin.transactionCompleted(); + } + + private String getCurrentlyConnectedInstance(JdbcConnection connection) throws SQLException { + try (final Statement statement = connection.createStatement()) { + final ResultSet rs = statement.executeQuery(GET_INSTANCE_QUERY); + if (rs.next()) { + return rs.getString(1); + } + return null; + } + } +} diff --git a/src/main/user-impl/java/com/mysql/cj/jdbc/ha/plugins/ReaderClusterConnectionPluginFactory.java b/src/main/user-impl/java/com/mysql/cj/jdbc/ha/plugins/ReaderClusterConnectionPluginFactory.java new file mode 100644 index 000000000..3a391a348 --- /dev/null +++ b/src/main/user-impl/java/com/mysql/cj/jdbc/ha/plugins/ReaderClusterConnectionPluginFactory.java @@ -0,0 +1,47 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2.0 + * (GPLv2), as published by the Free Software Foundation, with the + * following additional permissions: + * + * This program is distributed with certain software that is licensed + * under separate terms, as designated in a particular file or component + * or in the license documentation. Without limiting your rights under + * the GPLv2, the authors of this program hereby grant you an additional + * permission to link the program and your derivative works with the + * separately licensed software that they have included with the program. + * + * Without limiting the foregoing grant of rights under the GPLv2 and + * additional permission as to separately licensed software, this + * program is also subject to the Universal FOSS Exception, version 1.0, + * a copy of which can be found along with its FAQ at + * http://oss.oracle.com/licenses/universal-foss-exception. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License, version 2.0, for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * http://www.gnu.org/licenses/gpl-2.0.html. + */ + +package com.mysql.cj.jdbc.ha.plugins; + +import com.mysql.cj.conf.PropertySet; +import com.mysql.cj.log.Log; +import java.sql.SQLException; + +public class ReaderClusterConnectionPluginFactory implements IConnectionPluginFactory { + @Override + public IConnectionPlugin getInstance( + ICurrentConnectionProvider currentConnectionProvider, + PropertySet propertySet, + IConnectionPlugin nextPlugin, + Log logger) throws SQLException { + return new ReaderClusterConnectionPlugin(currentConnectionProvider, nextPlugin); + } +} diff --git a/src/main/user-impl/java/com/mysql/cj/jdbc/ha/plugins/failover/ClusterAwareReaderFailoverHandler.java b/src/main/user-impl/java/com/mysql/cj/jdbc/ha/plugins/failover/ClusterAwareReaderFailoverHandler.java index 27702776b..1d303c47f 100644 --- a/src/main/user-impl/java/com/mysql/cj/jdbc/ha/plugins/failover/ClusterAwareReaderFailoverHandler.java +++ b/src/main/user-impl/java/com/mysql/cj/jdbc/ha/plugins/failover/ClusterAwareReaderFailoverHandler.java @@ -34,7 +34,7 @@ import com.mysql.cj.Messages; import com.mysql.cj.conf.HostInfo; import com.mysql.cj.jdbc.JdbcConnection; -import com.mysql.cj.jdbc.ha.ConnectionUtils; +import com.mysql.cj.jdbc.ha.util.ConnectionUtils; import com.mysql.cj.jdbc.ha.plugins.IConnectionProvider; import com.mysql.cj.log.Log; import com.mysql.cj.log.NullLogger; diff --git a/src/main/user-impl/java/com/mysql/cj/jdbc/ha/plugins/failover/ClusterAwareWriterFailoverHandler.java b/src/main/user-impl/java/com/mysql/cj/jdbc/ha/plugins/failover/ClusterAwareWriterFailoverHandler.java index a2e43f110..4395743c6 100644 --- a/src/main/user-impl/java/com/mysql/cj/jdbc/ha/plugins/failover/ClusterAwareWriterFailoverHandler.java +++ b/src/main/user-impl/java/com/mysql/cj/jdbc/ha/plugins/failover/ClusterAwareWriterFailoverHandler.java @@ -35,7 +35,7 @@ import com.mysql.cj.conf.HostInfo; import com.mysql.cj.exceptions.CJCommunicationsException; import com.mysql.cj.jdbc.JdbcConnection; -import com.mysql.cj.jdbc.ha.ConnectionUtils; +import com.mysql.cj.jdbc.ha.util.ConnectionUtils; import com.mysql.cj.jdbc.ha.plugins.IConnectionProvider; import com.mysql.cj.log.Log; import com.mysql.cj.log.NullLogger; diff --git a/src/main/user-impl/java/com/mysql/cj/jdbc/ha/plugins/failover/FailoverConnectionPlugin.java b/src/main/user-impl/java/com/mysql/cj/jdbc/ha/plugins/failover/FailoverConnectionPlugin.java index a3300dc3a..8745a5c57 100644 --- a/src/main/user-impl/java/com/mysql/cj/jdbc/ha/plugins/failover/FailoverConnectionPlugin.java +++ b/src/main/user-impl/java/com/mysql/cj/jdbc/ha/plugins/failover/FailoverConnectionPlugin.java @@ -47,7 +47,7 @@ import com.mysql.cj.jdbc.exceptions.CommunicationsException; import com.mysql.cj.jdbc.exceptions.SQLError; import com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping; -import com.mysql.cj.jdbc.ha.ConnectionUtils; +import com.mysql.cj.jdbc.ha.util.ConnectionUtils; import com.mysql.cj.jdbc.ha.plugins.BasicConnectionProvider; import com.mysql.cj.jdbc.ha.plugins.IConnectionPlugin; import com.mysql.cj.jdbc.ha.plugins.IConnectionProvider; @@ -65,7 +65,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Properties; import java.util.Set; import java.util.concurrent.Callable; @@ -73,8 +72,6 @@ import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.stream.Stream; /** * A {@link IConnectionPlugin} implementation that provides cluster-aware failover diff --git a/src/main/user-impl/java/com/mysql/cj/jdbc/ha/ConnectionUtils.java b/src/main/user-impl/java/com/mysql/cj/jdbc/ha/util/ConnectionUtils.java similarity index 85% rename from src/main/user-impl/java/com/mysql/cj/jdbc/ha/ConnectionUtils.java rename to src/main/user-impl/java/com/mysql/cj/jdbc/ha/util/ConnectionUtils.java index 086cfd2de..7fe566bb5 100644 --- a/src/main/user-impl/java/com/mysql/cj/jdbc/ha/ConnectionUtils.java +++ b/src/main/user-impl/java/com/mysql/cj/jdbc/ha/util/ConnectionUtils.java @@ -29,7 +29,7 @@ * http://www.gnu.org/licenses/gpl-2.0.html. */ -package com.mysql.cj.jdbc.ha; +package com.mysql.cj.jdbc.ha.util; import com.mysql.cj.Messages; import com.mysql.cj.conf.ConnectionUrl; @@ -157,6 +157,38 @@ public static HostInfo createHostWithProperties(HostInfo baseHost, Map" depend on particular Aurora cluster. + // For example: "" + // + // + // + // Cluster (Writer) Endpoint: .cluster-..rds.amazonaws.com + // Example: test-postgres.cluster-123456789012.us-east-2.rds.amazonaws.com + // + // Cluster Reader Endpoint: .cluster-ro-..rds.amazonaws.com + // Example: test-postgres.cluster-ro-123456789012.us-east-2.rds.amazonaws.com + // + // Cluster Custom Endpoint: .cluster-custom-..rds.amazonaws.com + // Example: test-postgres-alias.cluster-custom-123456789012.us-east-2.rds.amazonaws.com + // + // Instance Endpoint: ...rds.amazonaws.com + // Example: test-postgres-instance-1.123456789012.us-east-2.rds.amazonaws.com + // + // + // + // Similar endpoints for China regions have different structure and are presented below. + // + // Cluster (Writer) Endpoint: .cluster-.rds..amazonaws.com.cn + // Example: test-postgres.cluster-123456789012.rds.cn-northwest-1.amazonaws.com.cn + // + // Cluster Reader Endpoint: .cluster-ro-.rds..amazonaws.com.cn + // Example: test-postgres.cluster-ro-123456789012.rds.cn-northwest-1.amazonaws.com.cn + // + // Cluster Custom Endpoint: .cluster-custom-.rds..amazonaws.com.cn + // Example: test-postgres-alias.cluster-custom-123456789012.rds.cn-northwest-1.amazonaws.com.cn + // + // Instance Endpoint: ..rds..amazonaws.com.cn + // Example: test-postgres-instance-1.123456789012.rds.cn-northwest-1.amazonaws.com.cn + + private static final Pattern AURORA_DNS_PATTERN = + Pattern.compile( + "(?.+)\\." + + "(?proxy-|cluster-|cluster-ro-|cluster-custom-)?" + + "(?[a-zA-Z0-9]+\\.(?[a-zA-Z0-9\\-]+)\\.rds\\.amazonaws\\.com)", + Pattern.CASE_INSENSITIVE); + + private static final Pattern AURORA_CLUSTER_PATTERN = + Pattern.compile( + "(?.+)\\." + + "(?cluster-|cluster-ro-)+" + + "(?[a-zA-Z0-9]+\\.(?[a-zA-Z0-9\\-]+)\\.rds\\.amazonaws\\.com)", + Pattern.CASE_INSENSITIVE); + + private static final Pattern AURORA_CHINA_DNS_PATTERN = + Pattern.compile( + "(?.+)\\." + + "(?proxy-|cluster-|cluster-ro-|cluster-custom-)?" + + "(?[a-zA-Z0-9]+\\.rds\\.(?[a-zA-Z0-9\\-]+)\\.amazonaws\\.com\\.cn)", + Pattern.CASE_INSENSITIVE); + + private static final Pattern AURORA_CHINA_CLUSTER_PATTERN = + Pattern.compile( + "(?.+)\\." + + "(?cluster-|cluster-ro-)+" + + "(?[a-zA-Z0-9]+\\.rds\\.(?[a-zA-Z0-9\\-]+)\\.amazonaws\\.com\\.cn)", + Pattern.CASE_INSENSITIVE); + + private static final String DNS_GROUP = "dns"; + private static final String DOMAIN_GROUP = "domain"; + + public String getRdsInstanceHostPattern(final String host) { + if (StringUtils.isNullOrEmpty(host)) { + return "?"; + } + + final Matcher matcher = AURORA_DNS_PATTERN.matcher(host); + if (matcher.find()) { + return "?." + matcher.group(DOMAIN_GROUP); + } + final Matcher chinaMatcher = AURORA_CHINA_DNS_PATTERN.matcher(host); + if (chinaMatcher.find()) { + return "?." + chinaMatcher.group(DOMAIN_GROUP); + } + return "?"; + } + + public boolean isReaderClusterDns(final String host) { + if (StringUtils.isNullOrEmpty(host)) { + return false; + } + + final Matcher matcher = AURORA_CLUSTER_PATTERN.matcher(host); + if (matcher.find()) { + return "cluster-ro-".equalsIgnoreCase(matcher.group(DNS_GROUP)); + } + final Matcher chinaMatcher = AURORA_CHINA_CLUSTER_PATTERN.matcher(host); + if (chinaMatcher.find()) { + return "cluster-ro-".equalsIgnoreCase(chinaMatcher.group(DNS_GROUP)); + } + return false; + } +} diff --git a/src/main/user-impl/java/com/mysql/cj/jdbc/integration/c3p0/MysqlConnectionTester.java b/src/main/user-impl/java/com/mysql/cj/jdbc/integration/c3p0/MysqlConnectionTester.java index cde41b812..a33ab0b95 100644 --- a/src/main/user-impl/java/com/mysql/cj/jdbc/integration/c3p0/MysqlConnectionTester.java +++ b/src/main/user-impl/java/com/mysql/cj/jdbc/integration/c3p0/MysqlConnectionTester.java @@ -41,7 +41,7 @@ import com.mysql.cj.exceptions.CJCommunicationsException; import com.mysql.cj.jdbc.JdbcConnection; import com.mysql.cj.jdbc.exceptions.CommunicationsException; -import com.mysql.cj.jdbc.ha.ConnectionUtils; +import com.mysql.cj.jdbc.ha.util.ConnectionUtils; /** * ConnectionTester for C3P0 connection pool that uses the more efficient COM_PING method of testing connection 'liveness' for MySQL, and 'sorts' exceptions diff --git a/src/test/java/com/mysql/cj/jdbc/ha/plugins/failover/ClusterAwareTestUtils.java b/src/test/java/com/mysql/cj/jdbc/ha/plugins/failover/ClusterAwareTestUtils.java index 251ab8d55..1563cc6cc 100644 --- a/src/test/java/com/mysql/cj/jdbc/ha/plugins/failover/ClusterAwareTestUtils.java +++ b/src/test/java/com/mysql/cj/jdbc/ha/plugins/failover/ClusterAwareTestUtils.java @@ -36,9 +36,8 @@ import com.mysql.cj.conf.PropertyKey; import com.mysql.cj.jdbc.ha.ConnectionProxyTest; -import com.mysql.cj.jdbc.ha.ConnectionUtils; +import com.mysql.cj.jdbc.ha.util.ConnectionUtils; import com.mysql.cj.util.StringUtils; -import java.sql.Connection; import java.sql.SQLException; import java.util.HashMap; import java.util.Map; diff --git a/src/test/java/com/mysql/cj/util/ConnectionUtilsTest.java b/src/test/java/com/mysql/cj/util/ConnectionUtilsTest.java index f2155e902..f1d4344f4 100644 --- a/src/test/java/com/mysql/cj/util/ConnectionUtilsTest.java +++ b/src/test/java/com/mysql/cj/util/ConnectionUtilsTest.java @@ -34,7 +34,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import com.mysql.cj.conf.PropertyKey; -import com.mysql.cj.jdbc.ha.ConnectionUtils; +import com.mysql.cj.jdbc.ha.util.ConnectionUtils; import java.sql.SQLException; import java.util.Properties; import java.util.stream.Stream; diff --git a/src/test/java/testsuite/integration/container/AuroraMysqlIntegrationTest.java b/src/test/java/testsuite/integration/container/AuroraMysqlIntegrationTest.java index 8fec7dcce..73aee6b59 100644 --- a/src/test/java/testsuite/integration/container/AuroraMysqlIntegrationTest.java +++ b/src/test/java/testsuite/integration/container/AuroraMysqlIntegrationTest.java @@ -32,10 +32,13 @@ package testsuite.integration.container; import com.mysql.cj.conf.PropertyKey; +import com.mysql.cj.jdbc.exceptions.MySQLTimeoutException; +import com.mysql.cj.jdbc.ha.plugins.ReaderClusterConnectionPluginFactory; import com.mysql.cj.jdbc.ha.plugins.failover.IClusterAwareMetricsReporter; +import java.sql.Statement; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.params.ParameterizedTest; @@ -607,4 +610,18 @@ public void test_PreparedStatementHashCodes() throws SQLException, IOException { conn.close(); } + @RepeatedTest(50) + public void test_QueryTimeoutOnReaderClusterConnection() throws Exception { + final Properties props = initDefaultProps(); + props.setProperty("connectionPluginFactories", ReaderClusterConnectionPluginFactory.class.getName()); + try (final Connection conn = connectToInstance(MYSQL_RO_CLUSTER_URL, MYSQL_PORT, props)) { + assertTrue(conn.isValid(5)); + try (final Statement statement = conn.createStatement()) { + statement.setQueryTimeout(1); + statement.execute("SELECT SLEEP(60)"); + } catch (MySQLTimeoutException e) { + // ignore + } + } + } }