This is an automated email from the ASF dual-hosted git repository.
stoty pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite-avatica.git
The following commit(s) were added to refs/heads/main by this push:
new 4b9c8231b [CALCITE-6209] Long queries are failing with
"java.net.SocketTimeoutException: Read timed out" after 3 minutes
4b9c8231b is described below
commit 4b9c8231b923fa7d7ea57131f0c5cb1dd2099613
Author: Istvan Toth <[email protected]>
AuthorDate: Mon Jan 22 15:53:12 2024 +0100
[CALCITE-6209] Long queries are failing with
"java.net.SocketTimeoutException: Read timed out" after 3 minutes
make socket timeout configurable via the new 'http_response_timeout' URL
option
---
.../calcite/avatica/BuiltInConnectionProperty.java | 6 ++++
.../apache/calcite/avatica/ConnectionConfig.java | 2 ++
.../calcite/avatica/ConnectionConfigImpl.java | 4 +++
.../remote/AvaticaCommonsHttpClientImpl.java | 1 +
.../avatica/remote/AvaticaServersForTest.java | 32 ++++++++++++++++++++--
.../calcite/avatica/remote/RemoteMetaTest.java | 32 ++++++++++++++++++++++
site/_docs/client_reference.md | 10 ++++++-
7 files changed, 83 insertions(+), 4 deletions(-)
diff --git
a/core/src/main/java/org/apache/calcite/avatica/BuiltInConnectionProperty.java
b/core/src/main/java/org/apache/calcite/avatica/BuiltInConnectionProperty.java
index d68d39dbf..9500f8bea 100644
---
a/core/src/main/java/org/apache/calcite/avatica/BuiltInConnectionProperty.java
+++
b/core/src/main/java/org/apache/calcite/avatica/BuiltInConnectionProperty.java
@@ -127,6 +127,12 @@ public enum BuiltInConnectionProperty implements
ConnectionProperty {
* HTTP Connection Timeout in milliseconds.
*/
HTTP_CONNECTION_TIMEOUT("http_connection_timeout",
+ Type.NUMBER, Timeout.ofMinutes(3).toMilliseconds(), false),
+
+ /**
+ * HTTP Response Timeout (socket timeout) in milliseconds.
+ */
+ HTTP_RESPONSE_TIMEOUT("http_response_timeout",
Type.NUMBER, Timeout.ofMinutes(3).toMilliseconds(), false);
private final String camelName;
diff --git
a/core/src/main/java/org/apache/calcite/avatica/ConnectionConfig.java
b/core/src/main/java/org/apache/calcite/avatica/ConnectionConfig.java
index adb98339b..9698d13e9 100644
--- a/core/src/main/java/org/apache/calcite/avatica/ConnectionConfig.java
+++ b/core/src/main/java/org/apache/calcite/avatica/ConnectionConfig.java
@@ -81,6 +81,8 @@ public interface ConnectionConfig {
long getLBConnectionFailoverSleepTime();
/** @see BuiltInConnectionProperty#HTTP_CONNECTION_TIMEOUT **/
long getHttpConnectionTimeout();
+ /** @see BuiltInConnectionProperty#HTTP_RESPONSE_TIMEOUT **/
+ long getHttpResponseTimeout();
}
// End ConnectionConfig.java
diff --git
a/core/src/main/java/org/apache/calcite/avatica/ConnectionConfigImpl.java
b/core/src/main/java/org/apache/calcite/avatica/ConnectionConfigImpl.java
index 9ae4446e7..773fb24d6 100644
--- a/core/src/main/java/org/apache/calcite/avatica/ConnectionConfigImpl.java
+++ b/core/src/main/java/org/apache/calcite/avatica/ConnectionConfigImpl.java
@@ -169,6 +169,10 @@ public class ConnectionConfigImpl implements
ConnectionConfig {
return
BuiltInConnectionProperty.HTTP_CONNECTION_TIMEOUT.wrap(properties).getLong();
}
+ public long getHttpResponseTimeout() {
+ return
BuiltInConnectionProperty.HTTP_RESPONSE_TIMEOUT.wrap(properties).getLong();
+ }
+
/** Converts a {@link Properties} object containing (name, value)
* pairs into a map whose keys are
* {@link org.apache.calcite.avatica.InternalProperty} objects.
diff --git
a/core/src/main/java/org/apache/calcite/avatica/remote/AvaticaCommonsHttpClientImpl.java
b/core/src/main/java/org/apache/calcite/avatica/remote/AvaticaCommonsHttpClientImpl.java
index 1c5327f13..39217c05c 100644
---
a/core/src/main/java/org/apache/calcite/avatica/remote/AvaticaCommonsHttpClientImpl.java
+++
b/core/src/main/java/org/apache/calcite/avatica/remote/AvaticaCommonsHttpClientImpl.java
@@ -104,6 +104,7 @@ public class AvaticaCommonsHttpClientImpl implements
AvaticaHttpClient, HttpClie
RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
RequestConfig requestConfig = requestConfigBuilder
.setConnectTimeout(config.getHttpConnectionTimeout(),
TimeUnit.MILLISECONDS)
+ .setResponseTimeout(config.getHttpResponseTimeout(),
TimeUnit.MILLISECONDS)
.build();
HttpClientBuilder httpClientBuilder =
HttpClients.custom().setConnectionManager(pool)
.setDefaultRequestConfig(requestConfig);
diff --git
a/server/src/test/java/org/apache/calcite/avatica/remote/AvaticaServersForTest.java
b/server/src/test/java/org/apache/calcite/avatica/remote/AvaticaServersForTest.java
index 1dfd535b0..d778349ad 100644
---
a/server/src/test/java/org/apache/calcite/avatica/remote/AvaticaServersForTest.java
+++
b/server/src/test/java/org/apache/calcite/avatica/remote/AvaticaServersForTest.java
@@ -18,6 +18,7 @@ package org.apache.calcite.avatica.remote;
import org.apache.calcite.avatica.ConnectionSpec;
import org.apache.calcite.avatica.Meta;
+import org.apache.calcite.avatica.NoSuchStatementException;
import org.apache.calcite.avatica.jdbc.JdbcMeta;
import org.apache.calcite.avatica.metrics.MetricsSystemConfiguration;
import org.apache.calcite.avatica.remote.Driver.Serialization;
@@ -29,6 +30,10 @@ import org.apache.calcite.avatica.server.HandlerFactory;
import org.apache.calcite.avatica.server.HttpServer;
import org.apache.calcite.avatica.server.Main;
+import org.mockito.Mockito;
+import org.mockito.internal.stubbing.answers.CallsRealMethods;
+import org.mockito.invocation.InvocationOnMock;
+
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
@@ -37,7 +42,11 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.matches;
/**
* Utility class which encapsulates the setup required to write Avatica tests
that run against
* servers using each serialization approach.
@@ -186,10 +195,27 @@ public class AvaticaServersForTest {
static JdbcMeta getInstance() {
if (instance == null) {
try {
- instance = new JdbcMeta(CONNECTION_SPEC.url,
CONNECTION_SPEC.username,
+ JdbcMeta realInstance = new JdbcMeta(CONNECTION_SPEC.url,
CONNECTION_SPEC.username,
CONNECTION_SPEC.password);
- } catch (SQLException e) {
- throw new RuntimeException(e);
+ // Add a configurable delay to the Statement.execute() method based
on a DELAY SQL comment
+ instance = Mockito.spy(realInstance);
+ Mockito.doAnswer(new CallsRealMethods() {
+ @Override
+ public Object answer(InvocationOnMock invocation) throws Throwable
{
+ Pattern pattern = Pattern.compile("/\\* DELAY=(\\d+) \\*/");
+ Matcher matcher = pattern.matcher((String)
invocation.getArgument(1));
+ if (matcher.find()) {
+ long delay = Long.parseLong(matcher.group(1));
+ Thread.sleep(delay);
+ }
+ return super.answer(invocation);
+ }
+ })
+
.when(instance).prepareAndExecute(any(Meta.StatementHandle.class),
+ matches("/\\* DELAY=(\\d+) \\*/"),
+ any(long.class), any(int.class),
any(Meta.PrepareCallback.class));
+ } catch (SQLException | NoSuchStatementException e) {
+ throw new RuntimeException("Error when applying test DELAY via
Mockito", e);
}
}
return instance;
diff --git
a/server/src/test/java/org/apache/calcite/avatica/remote/RemoteMetaTest.java
b/server/src/test/java/org/apache/calcite/avatica/remote/RemoteMetaTest.java
index 0d34fb433..00450a3fb 100644
--- a/server/src/test/java/org/apache/calcite/avatica/remote/RemoteMetaTest.java
+++ b/server/src/test/java/org/apache/calcite/avatica/remote/RemoteMetaTest.java
@@ -37,6 +37,7 @@ import com.google.common.base.Throwables;
import com.google.common.cache.Cache;
import org.junit.AfterClass;
+import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -795,6 +796,37 @@ public class RemoteMetaTest {
}
}
}
+
+ @Test public void testNoTimeout() throws Exception {
+ ConnectionSpec.getDatabaseLock().lock();
+ try (AvaticaConnection conn = (AvaticaConnection)
DriverManager.getConnection(url)) {
+ final AvaticaStatement statement = conn.createStatement();
+ // Well within the default 180 seconds
+ statement.execute(
+ "select * from (values ('a', 1), ('b', 2)) /* DELAY=5000 */");
+ statement.getResultSet();
+ // We only care whether it times out
+ statement.close();
+ conn.close();
+ } finally {
+ ConnectionSpec.getDatabaseLock().unlock();
+ }
+ }
+
+ @Test public void testTimeout() throws Exception {
+ ConnectionSpec.getDatabaseLock().lock();
+ try (AvaticaConnection conn = (AvaticaConnection)
DriverManager.getConnection(
+ url + ";http_response_timeout=1000")) {
+ final AvaticaStatement statement = conn.createStatement();
+ statement.execute(
+ "select * from (values ('a', 1), ('b', 2)) /* DELAY=5000 */");
+ Assert.fail("Should have timed out");
+ } catch (SQLException e) {
+ // Expected outcome
+ } finally {
+ ConnectionSpec.getDatabaseLock().unlock();
+ }
+ }
}
// End RemoteMetaTest.java
diff --git a/site/_docs/client_reference.md b/site/_docs/client_reference.md
index 4f5785002..c88199475 100644
--- a/site/_docs/client_reference.md
+++ b/site/_docs/client_reference.md
@@ -254,7 +254,15 @@ failover retry.
<strong><a name="http_connection_timeout"
href="#http_connection_timeout">http_connection_timeout</a></strong>
-: _Description_: Timeout in milliseconds for the connection between the
Avatica HTTP client and server.
+: _Description_: Timeout in milliseconds for establishing the connection
between the Avatica HTTP client and server.
+
+: _Default_: `180000` (3 minutes).
+
+: _Required_: No.
+
+<strong><a name="http_response_timeout"
href="#http_response_timeout">http_response_timeout</a></strong>
+
+: _Description_: Socket Timeout in milliseconds for the connection between the
Avatica HTTP client and server.
: _Default_: `180000` (3 minutes).