3939import com .clickhouse .client .config .ClickHouseClientOption ;
4040import com .clickhouse .client .config .ClickHouseDefaults ;
4141import com .clickhouse .client .http .ClickHouseHttpProto ;
42+ import com .clickhouse .client .http .config .ClickHouseHttpOption ;
4243import com .clickhouse .data .ClickHouseColumn ;
4344import com .clickhouse .data .ClickHouseFormat ;
4445import com .clickhouse .data .format .BinaryStreamUtils ;
46+ import org .apache .hc .client5 .http .ConnectTimeoutException ;
4547import org .apache .hc .core5 .concurrent .DefaultThreadFactory ;
4648import org .apache .hc .core5 .http .ClassicHttpResponse ;
49+ import org .apache .hc .core5 .http .ConnectionRequestTimeoutException ;
4750import org .apache .hc .core5 .http .HttpStatus ;
4851import org .apache .hc .core5 .http .NoHttpResponseException ;
4952import org .slf4j .Logger ;
@@ -220,17 +223,17 @@ public Builder() {
220223 public Builder addEndpoint (String endpoint ) {
221224 try {
222225 URL endpointURL = new java .net .URL (endpoint );
223- if (!(endpointURL .getProtocol ().equalsIgnoreCase ("https" ) ||
224- endpointURL .getProtocol ().equalsIgnoreCase ("http" ))) {
226+
227+ if (endpointURL .getProtocol ().equalsIgnoreCase ("https" )) {
228+ addEndpoint (Protocol .HTTP , endpointURL .getHost (), endpointURL .getPort (), true );
229+ } else if (endpointURL .getProtocol ().equalsIgnoreCase ("http" )) {
230+ addEndpoint (Protocol .HTTP , endpointURL .getHost (), endpointURL .getPort (), false );
231+ } else {
225232 throw new IllegalArgumentException ("Only HTTP and HTTPS protocols are supported" );
226233 }
227234 } catch (java .net .MalformedURLException e ) {
228235 throw new IllegalArgumentException ("Endpoint should be a valid URL string" , e );
229236 }
230- if (endpoint .startsWith ("https://" )) {
231- this .configuration .put (ClickHouseClientOption .SSL .getKey (), "true" );
232- }
233- this .endpoints .add (endpoint );
234237 return this ;
235238 }
236239
@@ -252,7 +255,7 @@ public Builder addEndpoint(Protocol protocol, String host, int port, boolean sec
252255 this .configuration .put (ClickHouseClientOption .SSL .getKey (), "true" );
253256 }
254257 String endpoint = String .format ("%s%s://%s:%d" , protocol .toString ().toLowerCase (), secure ? "s" : "" , host , port );
255- this .addEndpoint (endpoint );
258+ this .endpoints . add (endpoint );
256259 return this ;
257260 }
258261
@@ -302,7 +305,15 @@ public Builder setAccessToken(String accessToken) {
302305 return this ;
303306 }
304307
305- // SOCKET SETTINGS
308+ /**
309+ * Configures client to use build-in connection pool
310+ * @param enable - if connection pool should be enabled
311+ * @return
312+ */
313+ public Builder enableConnectionPool (boolean enable ) {
314+ this .configuration .put ("connection_pool_enabled" , String .valueOf (enable ));
315+ return this ;
316+ }
306317
307318 /**
308319 * Default connection timeout in milliseconds. Timeout is applied to establish a connection.
@@ -324,6 +335,72 @@ public Builder setConnectTimeout(long timeout, ChronoUnit unit) {
324335 return this .setConnectTimeout (Duration .of (timeout , unit ).toMillis ());
325336 }
326337
338+ /**
339+ * Set timeout for waiting a free connection from a pool when all connections are leased.
340+ * This configuration is important when need to fail fast in high concurrent scenarios.
341+ * Default is 10 s.
342+ * @param timeout - connection timeout in milliseconds
343+ * @param unit - time unit
344+ */
345+ public Builder setConnectionRequestTimeout (long timeout , ChronoUnit unit ) {
346+ this .configuration .put ("connection_request_timeout" , String .valueOf (Duration .of (timeout , unit ).toMillis ()));
347+ return this ;
348+ }
349+
350+ /**
351+ * Sets the maximum number of connections that can be opened at the same time to a single server. Limit is not
352+ * a hard stop. It is done to prevent threads stuck inside a connection pool waiting for a connection.
353+ * Default is 10. It is recommended to set a higher value for a high concurrent applications. It will let
354+ * more threads to get a connection and execute a query.
355+ *
356+ * @param maxConnections - maximum number of connections
357+ */
358+ public Builder setMaxConnections (int maxConnections ) {
359+ this .configuration .put (ClickHouseHttpOption .MAX_OPEN_CONNECTIONS .getKey (), String .valueOf (maxConnections ));
360+ return this ;
361+ }
362+
363+ /**
364+ * Sets how long any connection would be considered as active and able for a lease.
365+ * After this time connection will be marked for sweep and will not be returned from a pool.
366+ * Has more effect than keep-alive timeout.
367+ * @param timeout - time in unit
368+ * @param unit - time unit
369+ * @return
370+ */
371+ public Builder setConnectionTTL (long timeout , ChronoUnit unit ) {
372+ this .configuration .put (ClickHouseClientOption .CONNECTION_TTL .getKey (), String .valueOf (Duration .of (timeout , unit ).toMillis ()));
373+ return this ;
374+ }
375+
376+ /**
377+ * Sets keep alive timeout for a connection to override server value. If set to -1 then server value will be used.
378+ * Default is -1.
379+ * Doesn't override connection TTL value.
380+ * {@see Client#setConnectionTTL}
381+ * @param timeout - time in unit
382+ * @param unit - time unit
383+ * @return
384+ */
385+ public Builder setKeepAliveTimeout (long timeout , ChronoUnit unit ) {
386+ this .configuration .put (ClickHouseHttpOption .KEEP_ALIVE_TIMEOUT .getKey (), String .valueOf (Duration .of (timeout , unit ).toMillis ()));
387+ return this ;
388+ }
389+
390+ /**
391+ * Sets strategy of how connections are reuse.
392+ * Default is {@link ConnectionReuseStrategy#FIFO} to evenly distribute load between them.
393+ *
394+ * @param strategy - strategy for connection reuse
395+ * @return
396+ */
397+ public Builder setConnectionReuseStrategy (ConnectionReuseStrategy strategy ) {
398+ this .configuration .put ("connection_reuse_strategy" , strategy .name ());
399+ return this ;
400+ }
401+
402+ // SOCKET SETTINGS
403+
327404 /**
328405 * Default socket timeout in milliseconds. Timeout is applied to read and write operations.
329406 *
@@ -485,8 +562,8 @@ public Builder setProxyCredentials(String user, String pass) {
485562 * @param timeUnit
486563 * @return
487564 */
488- public Builder setExecutionTimeout (long timeout , TimeUnit timeUnit ) {
489- this .configuration .put (ClickHouseClientOption .MAX_EXECUTION_TIME .getKey (), String .valueOf (timeUnit .toMillis (timeout )));
565+ public Builder setExecutionTimeout (long timeout , ChronoUnit timeUnit ) {
566+ this .configuration .put (ClickHouseClientOption .MAX_EXECUTION_TIME .getKey (), String .valueOf (Duration . of ( timeout , timeUnit ) .toMillis ()));
490567 return this ;
491568 }
492569
@@ -719,6 +796,26 @@ private Map<String, String> setDefaults(Map<String, String> userConfig) {
719796 userConfig .put (ClickHouseClientOption .ASYNC .getKey (), "false" );
720797 }
721798
799+ if (!userConfig .containsKey (ClickHouseHttpOption .MAX_OPEN_CONNECTIONS .getKey ())) {
800+ userConfig .put (ClickHouseHttpOption .MAX_OPEN_CONNECTIONS .getKey (), "10" );
801+ }
802+
803+ if (!userConfig .containsKey ("connection_request_timeout" )) {
804+ userConfig .put ("connection_request_timeout" , "10000" );
805+ }
806+
807+ if (!userConfig .containsKey ("connection_reuse_strategy" )) {
808+ userConfig .put ("connection_reuse_strategy" , ConnectionReuseStrategy .FIFO .name ());
809+ }
810+
811+ if (!userConfig .containsKey ("connection_pool_enabled" )) {
812+ userConfig .put ("connection_pool_enabled" , "true" );
813+ }
814+
815+ if (!userConfig .containsKey ("connection_ttl" )) {
816+ userConfig .put ("connection_ttl" , "-1" );
817+ }
818+
722819 return userConfig ;
723820 }
724821 }
@@ -1212,6 +1309,8 @@ public CompletableFuture<QueryResponse> query(String sqlQuery, Map<String, Objec
12121309 return new QueryResponse (httpResponse , finalSettings .getFormat (), finalSettings , metrics );
12131310 } catch (ClientException e ) {
12141311 throw e ;
1312+ } catch (ConnectionRequestTimeoutException | ConnectTimeoutException e ) {
1313+ throw new ConnectionInitiationException ("Failed to get connection" , e );
12151314 } catch (Exception e ) {
12161315 throw new ClientException ("Failed to execute query" , e );
12171316 }
0 commit comments