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).
 

Reply via email to