Skip to content

Commit 86b0b7f

Browse files
SNOW-1943242: Recover from shutdown HTTP connection manager
1 parent dcb60b2 commit 86b0b7f

2 files changed

Lines changed: 58 additions & 2 deletions

File tree

src/main/java/net/snowflake/client/core/HttpUtil.java

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import java.io.InputStream;
1818
import java.io.PrintWriter;
1919
import java.io.StringWriter;
20+
import java.lang.reflect.Field;
2021
import java.net.InetSocketAddress;
2122
import java.net.Proxy;
2223
import java.net.Socket;
@@ -105,9 +106,11 @@ public class HttpUtil {
105106
new ConcurrentHashMap<>();
106107

107108
/** Handle on the static connection manager, to gather statistics mainly */
109+
// TODO SNOW-1943242 consider making it HttpClientSettingsKey bound
108110
private static PoolingHttpClientConnectionManager connectionManager = null;
109111

110112
/** default request configuration, to be copied on individual requests. */
113+
// TODO SNOW-1943242 consider making it HttpClientSettingsKey bound
111114
private static RequestConfig DefaultRequestConfig = null;
112115

113116
private static boolean socksProxyDisabled = false;
@@ -350,6 +353,8 @@ public static CloseableHttpClient buildHttpClient(
350353
.build();
351354

352355
// Build a connection manager with enough connections
356+
// TODO SNOW-1943242 consider not creating connection manager if already exists and/or
357+
// synchronization
353358
connectionManager =
354359
new PoolingHttpClientConnectionManager(
355360
registry, null, null, null, timeToLive, TimeUnit.SECONDS);
@@ -497,8 +502,33 @@ public static CloseableHttpClient initHttpClientWithoutDecompression(
497502
*/
498503
public static CloseableHttpClient initHttpClient(HttpClientSettingsKey key, File ocspCacheFile) {
499504
updateRoutePlanner(key);
500-
return httpClient.computeIfAbsent(
501-
key, k -> buildHttpClient(key, ocspCacheFile, key.getGzipDisabled()));
505+
CloseableHttpClient closeableHttpClient =
506+
httpClient.computeIfAbsent(
507+
key, k -> buildHttpClient(key, ocspCacheFile, key.getGzipDisabled()));
508+
if (detectClosedConnectionManager(closeableHttpClient)) {
509+
// TODO SNOW-1943242 consider dropping connectionManager
510+
httpClient.remove(key);
511+
return httpClient.computeIfAbsent(
512+
key, k -> buildHttpClient(key, ocspCacheFile, key.getGzipDisabled()));
513+
}
514+
return closeableHttpClient;
515+
}
516+
517+
private static boolean detectClosedConnectionManager(CloseableHttpClient closeableHttpClient)
518+
throws RuntimeException {
519+
// There is no simple way to detect is the connection manager is closed...
520+
try {
521+
Field connManagerField = closeableHttpClient.getClass().getDeclaredField("connManager");
522+
connManagerField.setAccessible(true);
523+
PoolingHttpClientConnectionManager connectionManager =
524+
(PoolingHttpClientConnectionManager) connManagerField.get(closeableHttpClient);
525+
Field isShutdownField = connectionManager.getClass().getDeclaredField("isShutDown");
526+
isShutdownField.setAccessible(true);
527+
AtomicBoolean isShutdown = (AtomicBoolean) isShutdownField.get(connectionManager);
528+
return isShutdown.get();
529+
} catch (NoSuchFieldException | IllegalAccessException e) {
530+
throw new RuntimeException(e);
531+
}
502532
}
503533

504534
/**

src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
import net.snowflake.client.category.TestTags;
5959
import net.snowflake.client.core.HttpClientSettingsKey;
6060
import net.snowflake.client.core.HttpUtil;
61+
import net.snowflake.client.core.OCSPMode;
6162
import net.snowflake.client.core.ObjectMapperFactory;
6263
import net.snowflake.client.core.QueryStatus;
6364
import net.snowflake.client.core.SFSession;
@@ -1662,4 +1663,29 @@ public void testDisableOCSPChecksMode() throws SQLException {
16621663
assertThat(
16631664
thrown.getErrorCode(), anyOf(is(INVALID_CONNECTION_INFO_CODE), is(BAD_REQUEST_GS_CODE)));
16641665
}
1666+
1667+
/** Added after version 3.22.0 */
1668+
@Test
1669+
public void testRecoverFromClosedHttpConnectionManager() throws SQLException {
1670+
try (Connection connection = getConnection();
1671+
Statement statement = connection.createStatement()) {
1672+
HttpUtil.httpClient
1673+
.values()
1674+
.forEach(
1675+
closeableHttpClient -> {
1676+
try {
1677+
closeableHttpClient.close();
1678+
} catch (IOException e) {
1679+
throw new RuntimeException(e);
1680+
}
1681+
});
1682+
// next line to override single static HttpUtil.connectionManager
1683+
HttpUtil.getHttpClient(new HttpClientSettingsKey(OCSPMode.DISABLE_OCSP_CHECKS));
1684+
try (ResultSet resultSet = statement.executeQuery("Select 1")) {
1685+
assertTrue(resultSet.next());
1686+
assertEquals(1, resultSet.getInt(1));
1687+
assertFalse(resultSet.next());
1688+
}
1689+
}
1690+
}
16651691
}

0 commit comments

Comments
 (0)