This is an automated email from the ASF dual-hosted git repository.
ppa pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new 219b0f5ee53 IGNITE-27134 Jdbc. Added backgroundReconnectInterval
property to connection configuration (#7093)
219b0f5ee53 is described below
commit 219b0f5ee534957070ed231d5faea2750518bbba
Author: Pavel Pereslegin <[email protected]>
AuthorDate: Mon Dec 1 16:54:57 2025 +0300
IGNITE-27134 Jdbc. Added backgroundReconnectInterval property to connection
configuration (#7093)
---
.../developers-guide/clients/jdbc-driver.adoc | 1 +
modules/jdbc/build.gradle | 1 +
.../ignite/jdbc/ItJdbcConnectionFailoverTest.java | 104 ++++++++++++++++++++-
.../ignite/jdbc/ItJdbcConnectionSelfTest.java | 50 ++++++++++
.../ignite/internal/jdbc/ConnectionProperties.java | 9 ++
.../internal/jdbc/ConnectionPropertiesImpl.java | 15 ++-
.../ignite/internal/jdbc/JdbcConnection.java | 3 +-
.../org/apache/ignite/jdbc/IgniteJdbcDriver.java | 12 ++-
.../client/ItThinClientChannelValidatorTest.java | 3 +-
9 files changed, 188 insertions(+), 10 deletions(-)
diff --git a/docs/_docs/developers-guide/clients/jdbc-driver.adoc
b/docs/_docs/developers-guide/clients/jdbc-driver.adoc
index ad6d88092e0..19690ee2be6 100644
--- a/docs/_docs/developers-guide/clients/jdbc-driver.adoc
+++ b/docs/_docs/developers-guide/clients/jdbc-driver.adoc
@@ -72,6 +72,7 @@
jdbc:ignite:thin://host[:port][,host[:port][/schema][[?parameter1=value1][;param
** `partitionAwarenessMetadataCacheSize` - Size of cache to store partition
awareness metadata of queries, in number of entries. Default value: `1024`.
** `queryTimeout` - Number of seconds the driver will wait for a `Statement`
object to execute. 0 means there is no limit. Default value: `0`.
** `connectionTimeout` - Number of milliseconds JDBC client will wait for
server to respond. 0 means there is no limit. Default value: `0`.
+** `backgroundReconnectIntervalMillis` - Specifies the wait time in
milliseconds between runs of the background reconnection procedure. This
procedure is used to restore old and establish new connections to cluster
nodes. The value `0` can be used to disable background reconnection. Default
value is `30000` (30 seconds).
** `username` - username for basic authentication to the cluster.
** `password` - user password for basic authentication to the cluster.
** `sslEnabled` - Determines if SSL is enabled. Possible values: `true`,
`false`. Default value: `false`
diff --git a/modules/jdbc/build.gradle b/modules/jdbc/build.gradle
index a9ab9761e4a..7641f596bd9 100644
--- a/modules/jdbc/build.gradle
+++ b/modules/jdbc/build.gradle
@@ -45,6 +45,7 @@ dependencies {
integrationTestImplementation testFixtures(project(':ignite-runner'))
integrationTestImplementation project(':ignite-transactions')
integrationTestImplementation project(":ignite-runner")
+ integrationTestImplementation project(":ignite-client")
integrationTestImplementation project(":ignite-api")
integrationTestImplementation project(":ignite-system-view-api")
integrationTestImplementation libs.awaitility
diff --git
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcConnectionFailoverTest.java
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcConnectionFailoverTest.java
index c05492e8830..30243d5041b 100644
---
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcConnectionFailoverTest.java
+++
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcConnectionFailoverTest.java
@@ -27,9 +27,13 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
+import java.time.Duration;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.apache.ignite.internal.ClusterPerTestIntegrationTest;
+import org.apache.ignite.internal.jdbc.JdbcConnection;
+import org.apache.ignite.internal.lang.RunnableX;
+import org.awaitility.Awaitility;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@@ -50,7 +54,43 @@ public class ItJdbcConnectionFailoverTest extends
ClusterPerTestIntegrationTest
* <p>Test sequentially restarts each cluster node keeping CMG majority
alive.
*/
@Test
- void testConnectionFailover() throws SQLException {
+ void testBasicQueryForwardedToAliveNode() throws Throwable {
+ int nodesCount = 3;
+ cluster.startAndInit(nodesCount, new int[]{0, 1, 2});
+
+ try (Connection connection = getConnection(nodesCount)) {
+ try (Statement statement = connection.createStatement()) {
+ Awaitility.await().until(() -> channelsCount(connection),
is(nodesCount));
+
+ RunnableX query = () -> {
+ for (int i = 0; i < 100; i++) {
+ assertThat(statement.execute("SELECT " + i), is(true));
+ }
+ };
+
+ query.run();
+
+ cluster.stopNode(0);
+
+ query.run();
+
+ cluster.startNode(0);
+ cluster.stopNode(1);
+ query.run();
+
+ cluster.startNode(1);
+ cluster.stopNode(2);
+ query.run();
+ }
+ }
+ }
+
+ /**
+ * Ensures that the partition aware query is forwarded to the alive node.
+ */
+ @Test
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-27180")
+ void testPartitionAwareQueryForwardedToRandomNode() throws SQLException {
int nodesCount = 3;
cluster.startAndInit(nodesCount, new int[]{0, 1, 2});
@@ -59,6 +99,8 @@ public class ItJdbcConnectionFailoverTest extends
ClusterPerTestIntegrationTest
statement.executeUpdate("CREATE ZONE zone1 (REPLICAS 3)
STORAGE PROFILES ['default']");
statement.executeUpdate("CREATE TABLE t(id INT PRIMARY KEY,
val INT) ZONE zone1");
+ Awaitility.await().until(() -> channelsCount(connection),
is(nodesCount));
+
try (PreparedStatement preparedStatement =
connection.prepareStatement("INSERT INTO t VALUES (?, ?)")) {
performUpdates(preparedStatement, 0, 100);
@@ -82,6 +124,50 @@ public class ItJdbcConnectionFailoverTest extends
ClusterPerTestIntegrationTest
}
}
+ /**
+ * Ensures that the connection to a previously stopped node will be
restored after the specified time interval.
+ *
+ * <p>Note: this test relies on the internal implementation to ensure that
the
+ * JDBC connection property is correctly applied to the
underlying client.
+ */
+ @Test
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-27188")
+ void testConnectionRestoredAfterBackgroundReconnectInterval() throws
Exception {
+ int nodesCount = 3;
+ cluster.startAndInit(nodesCount, new int[]{2});
+ int backgroundReconnectInterval = 300;
+
+ try (Connection connection = getConnection(nodesCount,
"backgroundReconnectIntervalMillis=" + backgroundReconnectInterval)) {
+ Awaitility.await().until(() -> channelsCount(connection),
is(nodesCount));
+
+ cluster.stopNode(0);
+
+ assertThat(channelsCount(connection), is(nodesCount - 1));
+
+ cluster.startNode(0);
+
+
Awaitility.await().atMost(Duration.ofMillis(backgroundReconnectInterval * 2))
+ .until(() -> channelsCount(connection), is(nodesCount));
+ }
+
+ // No background reconnection is expected.
+ try (Connection connection = getConnection(nodesCount,
"backgroundReconnectIntervalMillis=0")) {
+ Awaitility.await().until(() -> channelsCount(connection),
is(nodesCount));
+
+ cluster.stopNode(0);
+
+ assertThat(channelsCount(connection), is(nodesCount - 1));
+
+ cluster.startNode(0);
+
+ // Ensure that no new connections occur during the specified
background reconnect interval.
+
Awaitility.await().during(Duration.ofMillis(backgroundReconnectInterval * 2))
+ .until(() -> channelsCount(connection), is(nodesCount -
1));
+
+ assertThat(channelsCount(connection), is(nodesCount - 1));
+ }
+ }
+
/**
* Checks transparent connection establishment after losing all
connections.
*/
@@ -143,13 +229,19 @@ public class ItJdbcConnectionFailoverTest extends
ClusterPerTestIntegrationTest
}
}
- private static Connection getConnection(int nodesCount) throws
SQLException {
+ private static Connection getConnection(int nodesCount, String ... params)
throws SQLException {
String addresses = IntStream.range(0, nodesCount)
.mapToObj(i -> "127.0.0.1:" + (BASE_CLIENT_PORT + i))
.collect(Collectors.joining(","));
+ return getConnection(addresses, params);
+ }
+
+ private static Connection getConnection(String addresses, String ...
params) throws SQLException {
+ String args = String.join("&", params);
+
//noinspection CallToDriverManagerGetConnection
- return DriverManager.getConnection("jdbc:ignite:thin://" + addresses);
+ return DriverManager.getConnection("jdbc:ignite:thin://" + addresses +
"?" + args);
}
private static void performUpdates(PreparedStatement preparedStatement,
int start, int end) throws SQLException {
@@ -161,4 +253,10 @@ public class ItJdbcConnectionFailoverTest extends
ClusterPerTestIntegrationTest
assertThat(preparedStatement.executeUpdate(), is(1));
}
}
+
+ private static int channelsCount(Connection connection) throws
SQLException {
+ JdbcConnection jdbcConnection =
connection.unwrap(JdbcConnection.class);
+
+ return jdbcConnection.channelsCount();
+ }
}
diff --git
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcConnectionSelfTest.java
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcConnectionSelfTest.java
index 6dd2b956f25..8a733103768 100644
---
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcConnectionSelfTest.java
+++
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcConnectionSelfTest.java
@@ -27,6 +27,7 @@ import static java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT;
import static java.sql.ResultSet.TYPE_FORWARD_ONLY;
import static java.sql.Statement.NO_GENERATED_KEYS;
import static java.sql.Statement.RETURN_GENERATED_KEYS;
+import static org.apache.ignite.internal.lang.IgniteStringFormatter.format;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.is;
@@ -53,6 +54,7 @@ import java.util.Properties;
import java.util.ServiceLoader;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
+import org.apache.ignite.client.IgniteClientConfiguration;
import org.apache.ignite.internal.jdbc.JdbcConnection;
import org.apache.ignite.jdbc.util.JdbcTestUtils;
import org.awaitility.Awaitility;
@@ -987,6 +989,14 @@ public class ItJdbcConnectionSelfTest extends
AbstractJdbcSelfTest {
@Test
public void testChangePartitionAwarenessCacheSize() throws SQLException {
+ // Default value.
+ try (JdbcConnection conn = (JdbcConnection)
DriverManager.getConnection(URL)) {
+ assertEquals(
+
IgniteClientConfiguration.DFLT_SQL_PARTITION_AWARENESS_METADATA_CACHE_SIZE,
+ conn.properties().getPartitionAwarenessMetadataCacheSize()
+ );
+ }
+
String urlPrefix = URL + "?partitionAwarenessMetadataCacheSize";
assertInvalid(urlPrefix + "=A",
@@ -1013,4 +1023,44 @@ public class ItJdbcConnectionSelfTest extends
AbstractJdbcSelfTest {
Awaitility.await().until(() -> ((JdbcConnection)
conn).channelsCount(), is(initialNodes()));
}
+
+ @Test
+ public void testChangeBackgroundReconnectInterval() throws SQLException {
+ String propertyName = "backgroundReconnectIntervalMillis";
+ String urlPrefix = URL + "?" + propertyName;
+
+ SqlThrowingFunction<String, Number> valueGetter = url -> {
+ try (JdbcConnection conn = (JdbcConnection)
DriverManager.getConnection(url)) {
+ return conn.properties().getBackgroundReconnectInterval();
+ }
+ };
+
+ assertThat(valueGetter.apply(URL),
is(IgniteClientConfiguration.DFLT_BACKGROUND_RECONNECT_INTERVAL));
+ assertThat(valueGetter.apply(urlPrefix + "=9223372036854775807"),
is(Long.MAX_VALUE));
+ assertThat(valueGetter.apply(urlPrefix + "=0"), is(0L));
+
+ assertInvalid(urlPrefix + "=A",
+ format("Failed to parse int property [name={}, value=A]",
propertyName));
+
+ assertInvalid(urlPrefix + "=-1",
+ format("Property cannot be lower than 0 [name={}, value=-1]",
propertyName));
+
+ assertInvalid(urlPrefix + "=9223372036854775808",
+ format("Failed to parse int property [name={},
value=9223372036854775808]", propertyName));
+ }
+
+ /**
+ * Function that can throw an {@link SQLException}.
+ */
+ @FunctionalInterface
+ private interface SqlThrowingFunction<T, R> {
+ /**
+ * Applies the function to a given argument.
+ *
+ * @param t Argument.
+ * @return Application result.
+ * @throws SQLException If something goes wrong.
+ */
+ R apply(T t) throws SQLException;
+ }
}
diff --git
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/ConnectionProperties.java
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/ConnectionProperties.java
index 5ea1011f6f1..0e5da1f816d 100644
---
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/ConnectionProperties.java
+++
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/ConnectionProperties.java
@@ -98,6 +98,15 @@ public interface ConnectionProperties {
*/
void setConnectionTimeout(Integer connTimeout) throws SQLException;
+ /**
+ * Gets the background reconnect interval, in milliseconds.
+ *
+ * <p>Value {@code 0} means that background reconnect is disabled.
+ *
+ * <p>Default is {@link
org.apache.ignite.client.IgniteClientConfiguration#DFLT_BACKGROUND_RECONNECT_INTERVAL}.
+ */
+ long getBackgroundReconnectInterval();
+
/**
* SSL enabled.
*
diff --git
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/ConnectionPropertiesImpl.java
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/ConnectionPropertiesImpl.java
index 06cc457b306..ac0a31d69d3 100644
---
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/ConnectionPropertiesImpl.java
+++
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/ConnectionPropertiesImpl.java
@@ -70,6 +70,12 @@ public class ConnectionPropertiesImpl implements
ConnectionProperties, Serializa
+ " Zero means there is no limits.",
0L, false, 0, Integer.MAX_VALUE);
+ /** JDBC background reconnect interval. */
+ private final LongProperty backgroundReconnectInterval = new
LongProperty("backgroundReconnectIntervalMillis",
+ "Sets the background reconnect interval."
+ + " Zero means that background reconnect is disabled.",
+ IgniteClientConfiguration.DFLT_BACKGROUND_RECONNECT_INTERVAL,
false, 0, Long.MAX_VALUE);
+
/** Path to the truststore. */
private final StringProperty trustStorePath = new
StringProperty("trustStorePath",
"Path to trust store", null, null, false, null);
@@ -115,7 +121,8 @@ public class ConnectionPropertiesImpl implements
ConnectionProperties, Serializa
private final ConnectionProperty[] propsArray = {
qryTimeout, connTimeout, trustStorePath, trustStorePassword,
sslEnabled, ciphers, keyStorePath, keyStorePassword,
- username, password, connectionTimeZone,
partitionAwarenessMetadataCacheSize
+ username, password, connectionTimeZone,
partitionAwarenessMetadataCacheSize,
+ backgroundReconnectInterval
};
/** {@inheritDoc} */
@@ -204,6 +211,12 @@ public class ConnectionPropertiesImpl implements
ConnectionProperties, Serializa
connTimeout.setValue(timeout);
}
+ /** {@inheritDoc} */
+ @Override
+ public long getBackgroundReconnectInterval() {
+ return backgroundReconnectInterval.value();
+ }
+
/** {@inheritDoc} */
@Override
public void setTrustStorePath(String trustStorePath) {
diff --git
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcConnection.java
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcConnection.java
index 60bff6ce232..9601848c080 100644
---
a/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcConnection.java
+++
b/modules/jdbc/src/main/java/org/apache/ignite/internal/jdbc/JdbcConnection.java
@@ -47,7 +47,6 @@ import java.util.Properties;
import java.util.concurrent.Executor;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.ignite.client.IgniteClient;
-import org.apache.ignite.internal.client.TcpIgniteClient;
import org.apache.ignite.internal.jdbc.proto.JdbcDatabaseMetadataHandler;
import org.apache.ignite.internal.jdbc.proto.SqlStateCode;
import org.apache.ignite.internal.sql.SqlCommon;
@@ -837,7 +836,7 @@ public class JdbcConnection implements Connection {
@TestOnly
public int channelsCount() {
- return ((TcpIgniteClient) igniteClient).channel().channels().size();
+ return igniteClient.connections().size();
}
private static void checkCursorOptions(
diff --git
a/modules/jdbc/src/main/java/org/apache/ignite/jdbc/IgniteJdbcDriver.java
b/modules/jdbc/src/main/java/org/apache/ignite/jdbc/IgniteJdbcDriver.java
index 2a3ed145359..1277d981d50 100644
--- a/modules/jdbc/src/main/java/org/apache/ignite/jdbc/IgniteJdbcDriver.java
+++ b/modules/jdbc/src/main/java/org/apache/ignite/jdbc/IgniteJdbcDriver.java
@@ -36,7 +36,7 @@ import org.apache.ignite.client.BasicAuthenticator;
import org.apache.ignite.client.IgniteClientAuthenticator;
import org.apache.ignite.client.IgniteClientConfiguration;
import org.apache.ignite.client.IgniteClientConnectionException;
-import org.apache.ignite.client.RetryLimitPolicy;
+import org.apache.ignite.client.RetryReadPolicy;
import org.apache.ignite.client.SslConfiguration;
import org.apache.ignite.internal.client.ChannelValidator;
import org.apache.ignite.internal.client.HostAndPort;
@@ -115,6 +115,12 @@ import org.jetbrains.annotations.Nullable;
* <br>By default no any timeout.</td>
* </tr>
* <tr>
+ * <td>backgroundReconnectIntervalMillis</td>
+ * <td>Background reconnect interval, in milliseconds.
+ * <br>The value {@code 0} can be used to disable background
reconnection.
+ * <br>The default value is {@code 30 000}.</td>
+ * </tr>
+ * <tr>
* <th colspan="2">Basic authentication</th>
* </tr>
* <tr>
@@ -303,11 +309,11 @@ public class IgniteJdbcDriver implements Driver {
null,
addresses,
networkTimeout,
-
IgniteClientConfigurationImpl.DFLT_BACKGROUND_RECONNECT_INTERVAL,
+ connectionProperties.getBackgroundReconnectInterval(),
null,
IgniteClientConfigurationImpl.DFLT_HEARTBEAT_INTERVAL,
IgniteClientConfigurationImpl.DFLT_HEARTBEAT_TIMEOUT,
- new RetryLimitPolicy(),
+ new RetryReadPolicy(),
null,
extractSslConfiguration(connectionProperties),
false,
diff --git
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientChannelValidatorTest.java
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientChannelValidatorTest.java
index 53e3ed5bcf0..6d0ce4d5478 100644
---
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientChannelValidatorTest.java
+++
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientChannelValidatorTest.java
@@ -43,6 +43,7 @@ import org.apache.ignite.client.IgniteClient;
import org.apache.ignite.client.IgniteClientConfiguration;
import org.apache.ignite.client.IgniteClientConnectionException;
import org.apache.ignite.client.RetryLimitPolicy;
+import org.apache.ignite.client.RetryReadPolicy;
import org.apache.ignite.internal.client.ChannelValidator;
import org.apache.ignite.internal.client.IgniteClientConfigurationImpl;
import org.apache.ignite.internal.client.ProtocolContext;
@@ -207,7 +208,7 @@ public class ItThinClientChannelValidatorTest extends
BaseIgniteAbstractTest {
null,
IgniteClientConfigurationImpl.DFLT_HEARTBEAT_INTERVAL,
IgniteClientConfigurationImpl.DFLT_HEARTBEAT_TIMEOUT,
- new RetryLimitPolicy(),
+ new RetryReadPolicy(),
null,
null,
false,