diff --git a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcWithMiniHS2.java b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcWithMiniHS2.java index afe23f8..3780b4e 100644 --- a/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcWithMiniHS2.java +++ b/itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestJdbcWithMiniHS2.java @@ -966,6 +966,38 @@ public void testHttpHeaderSize() throws Exception { } /** + * Test for jdbc driver retry on NoHttpResponseException + * @throws Exception + */ + @Test + public void testHttpRetryOnServerIdleTimeout() throws Exception { + // Stop HiveServer2 + stopMiniHS2(); + HiveConf conf = new HiveConf(); + conf.set("hive.server2.transport.mode", "http"); + // Set server's idle timeout to a very low value + conf.set("hive.server2.thrift.http.max.idle.time", "5"); + startMiniHS2(conf); + String userName = System.getProperty("user.name"); + Connection conn = getConnection(miniHS2.getJdbcURL(testDbName), userName, "password"); + Statement stmt = conn.createStatement(); + stmt.execute("select from_unixtime(unix_timestamp())"); + // Sleep for longer than server's idletimeout and execute a query + TimeUnit.SECONDS.sleep(10); + try { + stmt.execute("select from_unixtime(unix_timestamp())"); + } catch (Exception e) { + fail("Not expecting exception: " + e); + } finally { + if (conn != null) { + conn.close(); + } + } + // Restore original state + restoreMiniHS2AndConnections(); + } + + /** * Tests that DataNucleus' NucleusContext.classLoaderResolverMap clears cached class objects * (& hence doesn't leak classloaders) on closing any session * diff --git a/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java b/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java index ed899c6..b02fc8c 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java @@ -41,6 +41,7 @@ import org.apache.http.HttpRequestInterceptor; import org.apache.http.HttpResponse; import org.apache.http.client.CookieStore; +import org.apache.http.client.HttpRequestRetryHandler; import org.apache.http.client.ServiceUnavailableRetryStrategy; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; @@ -385,9 +386,9 @@ private CloseableHttpClient getHttpClient(Boolean useSsl) throws SQLException { * Add an interceptor to pass username/password in the header. * In https mode, the entire information is encrypted */ - requestInterceptor = new HttpBasicAuthInterceptor(getUserName(), getPassword(), - cookieStore, cookieName, useSsl, - additionalHttpHeaders); + requestInterceptor = + new HttpBasicAuthInterceptor(getUserName(), getPassword(), cookieStore, cookieName, + useSsl, additionalHttpHeaders); } } // Configure http client for cookie based authentication @@ -420,6 +421,23 @@ public long getRetryInterval() { } else { httpClientBuilder = HttpClientBuilder.create(); } + // In case the server's idletimeout is set to a lower value, it might close it's side of + // connection. However we retry one more time on NoHttpResponseException + httpClientBuilder.setRetryHandler(new HttpRequestRetryHandler() { + @Override + public boolean retryRequest(IOException exception, int executionCount, HttpContext context) { + if (executionCount > 1) { + LOG.info("Retry attempts to connect to server exceeded."); + return false; + } + if (exception instanceof org.apache.http.NoHttpResponseException) { + LOG.info("Could not connect to the server. Retrying one more time."); + return true; + } + return false; + } + }); + // Add the request interceptor to the client builder httpClientBuilder.addInterceptorFirst(requestInterceptor);