From 4d970c2cffc406b2b42a65f893dd72fd78608020 Mon Sep 17 00:00:00 2001 From: Stathis Souris Date: Thu, 29 Oct 2026 11:30:57 +0000 Subject: [PATCH] test(jdbc-v2): reproduce socket timeout loss after validation Add a delayed-response test for the JDBC timeout save/set/restore sequence used by pools. The test intentionally fails on upstream because restoring the reported zero timeout disables the configured socket timeout. Keep the reproduction separate from any proposed fix. Refs #3260 Co-authored-by: codex (0.153.2) --- .../jdbc/ConnectionNetworkTimeoutTest.java | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 jdbc-v2/src/test/java/com/clickhouse/jdbc/ConnectionNetworkTimeoutTest.java diff --git a/jdbc-v2/src/test/java/com/clickhouse/jdbc/ConnectionNetworkTimeoutTest.java b/jdbc-v2/src/test/java/com/clickhouse/jdbc/ConnectionNetworkTimeoutTest.java new file mode 100644 index 000000000..e28875121 --- /dev/null +++ b/jdbc-v2/src/test/java/com/clickhouse/jdbc/ConnectionNetworkTimeoutTest.java @@ -0,0 +1,63 @@ +package com.clickhouse.jdbc; + +import com.clickhouse.jdbc.internal.ExceptionUtils; +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.core.WireMockConfiguration; +import org.testng.annotations.Test; + +import java.net.SocketTimeoutException; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Properties; +import java.util.concurrent.Executor; + +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.expectThrows; + +public class ConnectionNetworkTimeoutTest { + @Test(timeOut = 15000) + public void testRestoringNetworkTimeoutPreservesConfiguredSocketTimeout() throws Exception { + WireMockServer server = new WireMockServer(WireMockConfiguration.options() + .bindAddress("127.0.0.1").dynamicPort()); + server.start(); + try { + // RowBinaryWithNamesAndTypes: one UInt8 column named "1", containing one row with value 1. + server.stubFor(WireMock.post(WireMock.anyUrl()) + .withRequestBody(WireMock.equalTo("SELECT 1")) + .willReturn(WireMock.aResponse().withFixedDelay(1800) + .withHeader("X-ClickHouse-Format", "RowBinaryWithNamesAndTypes") + .withHeader("X-ClickHouse-Summary", "{\"read_rows\":\"1\",\"read_bytes\":\"1\"}") + .withBody(new byte[] {1, 1, '1', 5, 'U', 'I', 'n', 't', '8', 1}))); + + Properties properties = new Properties(); + properties.setProperty("socket_timeout", "500"); + properties.setProperty("server_time_zone", "UTC"); + properties.setProperty("user", "default"); + properties.setProperty("password", "local-test"); + properties.setProperty("retry", "0"); + properties.setProperty("compress", "false"); + try (Connection connection = new ConnectionImpl( + "jdbc:clickhouse://127.0.0.1:" + server.port() + "/default", properties)) { + Executor executor = Runnable::run; + int originalTimeout = connection.getNetworkTimeout(); + connection.setNetworkTimeout(executor, 5000); + connection.setNetworkTimeout(executor, originalTimeout); + + try (Statement statement = connection.createStatement()) { + SQLException error = expectThrows(SQLException.class, () -> { + try (ResultSet result = statement.executeQuery("SELECT 1")) { + result.next(); + } + }); + assertTrue(ExceptionUtils.getRootCause(error) instanceof SocketTimeoutException, + "Expected a socket timeout, got: " + error); + } + } + } finally { + server.stop(); + } + } +}