Skip to content

Commit 65ccf78

Browse files
committed
[KYUUBI #3152] Introduce JDBC parameters to control connection timeout
### _Why are the changes needed?_ There are two concepts `connect timeout` and `so timeout` in the socket, currently, the Kyuubi Hive JDBC driver uses `DriverManager#loginTimeout` as both of them, it does not make sense, and will cause unexpected "read timeout" exceptions if `DriverManager#loginTimeout` was set a small value, e.g. Spring Boot set it to 30s in default. This PR proposes to introduce two dedicated JDBC parameters to control them. See also apache/hive#3379 ### _How was this patch tested?_ - [ ] Add some test cases that check the changes thoroughly including negative and positive cases if possible - [ ] Add screenshots for manual tests if appropriate - [x] [Run test](https://kyuubi.apache.org/docs/latest/develop_tools/testing.html#running-tests) locally before make a pull request Closes #3152 from pan3793/timeout. Closes #3152 5dcae66 [Cheng Pan] nit 816a851 [Cheng Pan] nit 3b5e6ce [Cheng Pan] fix npe 4cb73df [Cheng Pan] Introduce JDBC parameters to control connection timeout Authored-by: Cheng Pan <chengpan@apache.org> Signed-off-by: Cheng Pan <chengpan@apache.org>
1 parent 24b9384 commit 65ccf78

File tree

3 files changed

+55
-32
lines changed

3 files changed

+55
-32
lines changed

kyuubi-hive-jdbc/src/main/java/org/apache/kyuubi/jdbc/hive/KyuubiConnection.java

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
import java.sql.*;
3838
import java.util.*;
3939
import java.util.Map.Entry;
40-
import java.util.concurrent.TimeUnit;
4140
import java.util.concurrent.locks.ReentrantLock;
4241
import javax.net.ssl.KeyManagerFactory;
4342
import javax.net.ssl.SSLContext;
@@ -51,6 +50,7 @@
5150
import org.apache.http.NoHttpResponseException;
5251
import org.apache.http.client.CookieStore;
5352
import org.apache.http.client.ServiceUnavailableRetryStrategy;
53+
import org.apache.http.client.config.RequestConfig;
5454
import org.apache.http.config.Registry;
5555
import org.apache.http.config.RegistryBuilder;
5656
import org.apache.http.conn.socket.ConnectionSocketFactory;
@@ -95,7 +95,8 @@ public class KyuubiConnection implements SQLConnection, KyuubiLoggable {
9595
private SQLWarning warningChain = null;
9696
private TSessionHandle sessHandle = null;
9797
private final List<TProtocolVersion> supportedProtocols = new LinkedList<>();
98-
private int loginTimeout = 0;
98+
private int connectTimeout = 0;
99+
private int socketTimeout = 0;
99100
private TProtocolVersion protocol;
100101
private int fetchSize = KyuubiStatement.DEFAULT_FETCH_SIZE;
101102
private String initFile = null;
@@ -123,13 +124,12 @@ public static List<JdbcConnectionParams> getAllUrls(String zookeeperBasedHS2Url)
123124
}
124125

125126
public KyuubiConnection(String uri, Properties info) throws SQLException {
126-
setupLoginTimeout();
127+
isBeeLineMode = Boolean.parseBoolean(info.getProperty(BEELINE_MODE_PROPERTY));
127128
try {
128129
connParams = Utils.parseURL(uri, info);
129130
} catch (ZooKeeperHiveClientException e) {
130131
throw new KyuubiSQLException(e);
131132
}
132-
isBeeLineMode = Boolean.parseBoolean(info.getProperty(BEELINE_MODE_PROPERTY));
133133
jdbcUriString = connParams.getJdbcUriString();
134134
// JDBC URL: jdbc:hive2://<host>:<port>/dbName;sess_var_list?hive_conf_list#hive_var_list
135135
// each list: <key1>=<val1>;<key2>=<val2> and so on
@@ -140,6 +140,8 @@ public KyuubiConnection(String uri, Properties info) throws SQLException {
140140
port = connParams.getPort();
141141
sessConfMap = connParams.getSessionVars();
142142

143+
setupTimeout();
144+
143145
if (sessConfMap.containsKey(FETCH_SIZE)) {
144146
fetchSize = Integer.parseInt(sessConfMap.get(FETCH_SIZE));
145147
}
@@ -153,17 +155,7 @@ public KyuubiConnection(String uri, Properties info) throws SQLException {
153155
}
154156

155157
// add supported protocols
156-
supportedProtocols.add(TProtocolVersion.HIVE_CLI_SERVICE_PROTOCOL_V1);
157-
supportedProtocols.add(TProtocolVersion.HIVE_CLI_SERVICE_PROTOCOL_V2);
158-
supportedProtocols.add(TProtocolVersion.HIVE_CLI_SERVICE_PROTOCOL_V3);
159-
supportedProtocols.add(TProtocolVersion.HIVE_CLI_SERVICE_PROTOCOL_V4);
160-
supportedProtocols.add(TProtocolVersion.HIVE_CLI_SERVICE_PROTOCOL_V5);
161-
supportedProtocols.add(TProtocolVersion.HIVE_CLI_SERVICE_PROTOCOL_V6);
162-
supportedProtocols.add(TProtocolVersion.HIVE_CLI_SERVICE_PROTOCOL_V7);
163-
supportedProtocols.add(TProtocolVersion.HIVE_CLI_SERVICE_PROTOCOL_V8);
164-
supportedProtocols.add(TProtocolVersion.HIVE_CLI_SERVICE_PROTOCOL_V9);
165-
supportedProtocols.add(TProtocolVersion.HIVE_CLI_SERVICE_PROTOCOL_V10);
166-
supportedProtocols.add(TProtocolVersion.HIVE_CLI_SERVICE_PROTOCOL_V11);
158+
Collections.addAll(supportedProtocols, TProtocolVersion.values());
167159

168160
int maxRetries = 1;
169161
try {
@@ -471,6 +463,15 @@ private CloseableHttpClient getHttpClient(Boolean useSsl) throws SQLException {
471463
customCookies);
472464
}
473465
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
466+
467+
// Set timeout
468+
RequestConfig config =
469+
RequestConfig.custom()
470+
.setConnectTimeout(connectTimeout)
471+
.setSocketTimeout(socketTimeout)
472+
.build();
473+
httpClientBuilder.setDefaultRequestConfig(config);
474+
474475
// Configure http client for cookie based authentication
475476
if (isCookieEnabled) {
476477
// Create a http client with a retry mechanism when the server returns a status code of 401.
@@ -576,15 +577,15 @@ private TTransport createUnderlyingTransport() throws TTransportException {
576577
String sslTrustStorePassword = sessConfMap.get(SSL_TRUST_STORE_PASSWORD);
577578

578579
if (sslTrustStore == null || sslTrustStore.isEmpty()) {
579-
transport = ThriftUtils.getSSLSocket(host, port, loginTimeout);
580+
transport = ThriftUtils.getSSLSocket(host, port, connectTimeout, socketTimeout);
580581
} else {
581582
transport =
582583
ThriftUtils.getSSLSocket(
583-
host, port, loginTimeout, sslTrustStore, sslTrustStorePassword);
584+
host, port, connectTimeout, socketTimeout, sslTrustStore, sslTrustStorePassword);
584585
}
585586
} else {
586587
// get non-SSL socket transport
587-
transport = ThriftUtils.getSocketTransport(host, port, loginTimeout);
588+
transport = ThriftUtils.getSocketTransport(host, port, connectTimeout, socketTimeout);
588589
}
589590
return transport;
590591
}
@@ -856,13 +857,24 @@ private String getSessionValue(String varName, String varDefault) {
856857
return varValue;
857858
}
858859

859-
// copy loginTimeout from driver manager. Thrift timeout needs to be in millis
860-
private void setupLoginTimeout() {
861-
long timeOut = TimeUnit.SECONDS.toMillis(DriverManager.getLoginTimeout());
862-
if (timeOut > Integer.MAX_VALUE) {
863-
loginTimeout = Integer.MAX_VALUE;
864-
} else {
865-
loginTimeout = (int) timeOut;
860+
private void setupTimeout() {
861+
if (sessConfMap.containsKey(CONNECT_TIMEOUT)) {
862+
String connectTimeoutStr = sessConfMap.get(CONNECT_TIMEOUT);
863+
try {
864+
long connectTimeoutMs = Long.parseLong(connectTimeoutStr);
865+
connectTimeout = (int) Math.max(0, Math.min(connectTimeoutMs, Integer.MAX_VALUE));
866+
} catch (NumberFormatException e) {
867+
LOG.info("Failed to parse connectTimeout of value " + connectTimeoutStr);
868+
}
869+
}
870+
if (sessConfMap.containsKey(SOCKET_TIMEOUT)) {
871+
String socketTimeoutStr = sessConfMap.get(SOCKET_TIMEOUT);
872+
try {
873+
long socketTimeoutMs = Long.parseLong(socketTimeoutStr);
874+
socketTimeout = (int) Math.max(0, Math.min(socketTimeoutMs, Integer.MAX_VALUE));
875+
} catch (NumberFormatException e) {
876+
LOG.info("Failed to parse socketTimeout of value " + socketTimeoutStr);
877+
}
866878
}
867879
}
868880

kyuubi-hive-jdbc/src/main/java/org/apache/kyuubi/jdbc/hive/Utils.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ public static class JdbcConnectionParams {
126126
// Cookie prefix
127127
static final String HTTP_COOKIE_PREFIX = "http.cookie.";
128128

129+
static final String CONNECT_TIMEOUT = "connectTimeout";
130+
static final String SOCKET_TIMEOUT = "socketTimeout";
131+
129132
// We support ways to specify application name modeled after some existing DBs, since
130133
// there's no standard approach.
131134
// MSSQL: applicationName

kyuubi-hive-jdbc/src/main/java/org/apache/kyuubi/jdbc/hive/auth/ThriftUtils.java

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,27 +29,35 @@
2929
* given configuration as well as helps with authenticating requests.
3030
*/
3131
public class ThriftUtils {
32-
public static TTransport getSocketTransport(String host, int port, int loginTimeout) {
33-
return new TSocket(host, port, loginTimeout);
32+
public static TTransport getSocketTransport(
33+
String host, int port, int connectTimeout, int socketTimeout) {
34+
return new TSocket(host, port, socketTimeout, connectTimeout);
3435
}
3536

36-
public static TTransport getSSLSocket(String host, int port, int loginTimeout)
37-
throws TTransportException {
37+
public static TTransport getSSLSocket(
38+
String host, int port, int connectTimeout, int socketTimeout) throws TTransportException {
3839
// The underlying SSLSocket object is bound to host:port with the given SO_TIMEOUT
39-
TSocket tSSLSocket = TSSLTransportFactory.getClientSocket(host, port, loginTimeout);
40+
TSocket tSSLSocket = TSSLTransportFactory.getClientSocket(host, port, socketTimeout);
41+
tSSLSocket.setConnectTimeout(connectTimeout);
4042
return getSSLSocketWithHttps(tSSLSocket);
4143
}
4244

4345
public static TTransport getSSLSocket(
44-
String host, int port, int loginTimeout, String trustStorePath, String trustStorePassWord)
46+
String host,
47+
int port,
48+
int connectTimeout,
49+
int socketTimeout,
50+
String trustStorePath,
51+
String trustStorePassWord)
4552
throws TTransportException {
4653
TSSLTransportFactory.TSSLTransportParameters params =
4754
new TSSLTransportFactory.TSSLTransportParameters();
4855
params.setTrustStore(trustStorePath, trustStorePassWord);
4956
params.requireClientAuth(true);
5057
// The underlying SSLSocket object is bound to host:port with the given SO_TIMEOUT and
5158
// SSLContext created with the given params
52-
TSocket tSSLSocket = TSSLTransportFactory.getClientSocket(host, port, loginTimeout, params);
59+
TSocket tSSLSocket = TSSLTransportFactory.getClientSocket(host, port, socketTimeout, params);
60+
tSSLSocket.setConnectTimeout(connectTimeout);
5361
return getSSLSocketWithHttps(tSSLSocket);
5462
}
5563

0 commit comments

Comments
 (0)