Skip to content

Commit f085ddd

Browse files
authored
Merge pull request #2090 from ClickHouse/v2_reduce_init_time
[client-v2] Reduce client init time
2 parents 8ca05b3 + 1acf39d commit f085ddd

File tree

8 files changed

+70
-61
lines changed

8 files changed

+70
-61
lines changed

.github/workflows/build.yml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -247,12 +247,6 @@ jobs:
247247
EOF
248248
- name: Install Java client
249249
run: mvn --also-make --batch-mode --no-transfer-progress -DskipTests install
250-
# - name: Generate JWT
251-
# env:
252-
# JWT_K_PEM: ${{ secrets.INTEGRATIONS_TEAM_TESTS_CLOUD_JWT_PRIVATE_KEY }}
253-
# run: |
254-
# npm install jsonwebtoken &&
255-
# echo "CLIENT_JWT=$(node jwt-gen.js)" >> "$GITHUB_ENV"
256250
- name: Test http client
257251
env:
258252
CLICKHOUSE_CLOUD_HOST: ${{ secrets.INTEGRATIONS_TEAM_TESTS_CLOUD_HOST_SMT }}

client-v2/src/main/java/com/clickhouse/client/api/Client.java

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ public class Client implements AutoCloseable {
130130
private static final Logger LOG = LoggerFactory.getLogger(Client.class);
131131
private final ExecutorService sharedOperationExecutor;
132132

133-
private final boolean isSharedOpExecuterorOwned;
133+
private final boolean isSharedOpExecutorOwned;
134134

135135
private final Map<String, ClientStatisticsHolder> globalClientStats = new ConcurrentHashMap<>();
136136

@@ -141,39 +141,43 @@ public class Client implements AutoCloseable {
141141

142142
// Server context
143143
private String serverVersion;
144-
private Object metrics;
144+
private Object metricsRegistry;
145145

146146
private Client(Set<String> endpoints, Map<String,String> configuration, boolean useNewImplementation,
147147
ExecutorService sharedOperationExecutor, ColumnToMethodMatchingStrategy columnToMethodMatchingStrategy) {
148148
this(endpoints, configuration, useNewImplementation, sharedOperationExecutor, columnToMethodMatchingStrategy, null);
149149
}
150150

151151
private Client(Set<String> endpoints, Map<String,String> configuration, boolean useNewImplementation,
152-
ExecutorService sharedOperationExecutor, ColumnToMethodMatchingStrategy columnToMethodMatchingStrategy, Object metrics) {
152+
ExecutorService sharedOperationExecutor, ColumnToMethodMatchingStrategy columnToMethodMatchingStrategy, Object metricsRegistry) {
153153
this.endpoints = endpoints;
154154
this.configuration = configuration;
155155
this.readOnlyConfig = Collections.unmodifiableMap(this.configuration);
156156
this.endpoints.forEach(endpoint -> {
157157
this.serverNodes.add(ClickHouseNode.of(endpoint, this.configuration));
158158
});
159-
this.metrics = metrics;
159+
this.metricsRegistry = metricsRegistry;
160160
this.serializers = new ConcurrentHashMap<>();
161161
this.deserializers = new ConcurrentHashMap<>();
162162

163163
boolean isAsyncEnabled = MapUtils.getFlag(this.configuration, ClientConfigProperties.ASYNC_OPERATIONS.getKey(), false);
164164
if (isAsyncEnabled && sharedOperationExecutor == null) {
165-
this.isSharedOpExecuterorOwned = true;
165+
this.isSharedOpExecutorOwned = true;
166166
this.sharedOperationExecutor = Executors.newCachedThreadPool(new DefaultThreadFactory("chc-operation"));
167167
} else {
168-
this.isSharedOpExecuterorOwned = false;
168+
this.isSharedOpExecutorOwned = false;
169169
this.sharedOperationExecutor = sharedOperationExecutor;
170170
}
171-
this.httpClientHelper = new HttpAPIClientHelper(configuration, metrics);
171+
boolean initSslContext = getEndpoints().stream().anyMatch(s -> s.toLowerCase().contains("https://"));
172+
this.httpClientHelper = new HttpAPIClientHelper(configuration, metricsRegistry, initSslContext);
172173
this.columnToMethodMatchingStrategy = columnToMethodMatchingStrategy;
173-
updateServerContext();
174174
}
175175

176-
private void updateServerContext() {
176+
/**
177+
* Loads essential information about a server. Should be called after client creation.
178+
*
179+
*/
180+
public void loadServerInfo() {
177181
try (QueryResponse response = this.query("SELECT currentUser() AS user, timezone() AS timezone, version() AS version LIMIT 1").get()) {
178182
try (ClickHouseBinaryFormatReader reader = this.newBinaryFormatReader(response)) {
179183
if (reader.next() != null) {
@@ -183,13 +187,10 @@ private void updateServerContext() {
183187
}
184188
}
185189
} catch (Exception e) {
186-
LOG.error("Failed to get server info", e);
190+
throw new ClientException("Failed to get server info", e);
187191
}
188192
}
189193

190-
191-
192-
193194
/**
194195
* Returns default database name that will be used by operations if not specified.
195196
*
@@ -208,7 +209,7 @@ public String getDefaultDatabase() {
208209
*/
209210
@Override
210211
public void close() {
211-
if (isSharedOpExecuterorOwned) {
212+
if (isSharedOpExecutorOwned) {
212213
try {
213214
if (sharedOperationExecutor != null && !sharedOperationExecutor.isShutdown()) {
214215
this.sharedOperationExecutor.shutdownNow();
@@ -235,7 +236,7 @@ public static class Builder {
235236

236237
private ExecutorService sharedOperationExecutor = null;
237238
private ColumnToMethodMatchingStrategy columnToMethodMatchingStrategy;
238-
private Object metric = null;
239+
private Object metricRegistry = null;
239240
public Builder() {
240241
this.endpoints = new HashSet<>();
241242
this.configuration = new HashMap<String, String>();
@@ -960,7 +961,7 @@ public Builder useBearerTokenAuth(String bearerToken) {
960961
* @return same instance of the builder
961962
*/
962963
public Builder registerClientMetrics(Object registry, String name) {
963-
this.metric = registry;
964+
this.metricRegistry = registry;
964965
this.configuration.put(ClientConfigProperties.METRICS_GROUP_NAME.getKey(), name);
965966
return this;
966967
}
@@ -1024,7 +1025,7 @@ public Client build() {
10241025
}
10251026

10261027
return new Client(this.endpoints, this.configuration, this.useNewImplementation, this.sharedOperationExecutor,
1027-
this.columnToMethodMatchingStrategy, this.metric);
1028+
this.columnToMethodMatchingStrategy, this.metricRegistry);
10281029
}
10291030

10301031

client-v2/src/main/java/com/clickhouse/client/api/internal/HttpAPIClientHelper.java

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.apache.hc.client5.http.io.HttpClientConnectionManager;
2727
import org.apache.hc.client5.http.protocol.HttpClientContext;
2828
import org.apache.hc.client5.http.socket.ConnectionSocketFactory;
29+
import org.apache.hc.client5.http.socket.LayeredConnectionSocketFactory;
2930
import org.apache.hc.client5.http.socket.PlainConnectionSocketFactory;
3031
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
3132
import org.apache.hc.core5.http.ClassicHttpResponse;
@@ -44,6 +45,7 @@
4445
import org.apache.hc.core5.http.impl.io.DefaultHttpResponseParserFactory;
4546
import org.apache.hc.core5.http.io.SocketConfig;
4647
import org.apache.hc.core5.http.io.entity.EntityTemplate;
48+
import org.apache.hc.core5.http.protocol.HttpContext;
4749
import org.apache.hc.core5.io.CloseMode;
4850
import org.apache.hc.core5.io.IOCallback;
4951
import org.apache.hc.core5.net.URIBuilder;
@@ -62,6 +64,7 @@
6264
import java.net.ConnectException;
6365
import java.net.InetSocketAddress;
6466
import java.net.NoRouteToHostException;
67+
import java.net.Socket;
6568
import java.net.SocketTimeoutException;
6669
import java.net.URI;
6770
import java.net.URISyntaxException;
@@ -97,10 +100,10 @@ public class HttpAPIClientHelper {
97100

98101
private String defaultUserAgent;
99102
private Object metricsRegistry;
100-
public HttpAPIClientHelper(Map<String, String> configuration, Object metricsRegistry) {
103+
public HttpAPIClientHelper(Map<String, String> configuration, Object metricsRegistry, boolean initSslContext) {
101104
this.chConfiguration = configuration;
102105
this.metricsRegistry = metricsRegistry;
103-
this.httpClient = createHttpClient();
106+
this.httpClient = createHttpClient(initSslContext);
104107

105108
RequestConfig.Builder reqConfBuilder = RequestConfig.custom();
106109
MapUtils.applyLong(chConfiguration, "connection_request_timeout",
@@ -175,11 +178,10 @@ private ConnectionConfig createConnectionConfig() {
175178
return connConfig.build();
176179
}
177180

178-
private HttpClientConnectionManager basicConnectionManager(SSLContext sslContext, SocketConfig socketConfig) {
181+
private HttpClientConnectionManager basicConnectionManager(LayeredConnectionSocketFactory sslConnectionSocketFactory, SocketConfig socketConfig) {
179182
RegistryBuilder<ConnectionSocketFactory> registryBuilder = RegistryBuilder.create();
180183
registryBuilder.register("http", PlainConnectionSocketFactory.getSocketFactory());
181-
registryBuilder.register("https", new SSLConnectionSocketFactory(sslContext));
182-
184+
registryBuilder.register("https", sslConnectionSocketFactory);
183185

184186
BasicHttpClientConnectionManager connManager = new BasicHttpClientConnectionManager(registryBuilder.build());
185187
connManager.setConnectionConfig(createConnectionConfig());
@@ -188,7 +190,7 @@ private HttpClientConnectionManager basicConnectionManager(SSLContext sslContext
188190
return connManager;
189191
}
190192

191-
private HttpClientConnectionManager poolConnectionManager(SSLContext sslContext, SocketConfig socketConfig) {
193+
private HttpClientConnectionManager poolConnectionManager(LayeredConnectionSocketFactory sslConnectionSocketFactory, SocketConfig socketConfig) {
192194
PoolingHttpClientConnectionManagerBuilder connMgrBuilder = PoolingHttpClientConnectionManagerBuilder.create()
193195
.setPoolConcurrencyPolicy(PoolConcurrencyPolicy.LAX);
194196

@@ -221,7 +223,7 @@ private HttpClientConnectionManager poolConnectionManager(SSLContext sslContext,
221223
DefaultHttpResponseParserFactory.INSTANCE);
222224

223225
connMgrBuilder.setConnectionFactory(connectionFactory);
224-
connMgrBuilder.setSSLSocketFactory(new SSLConnectionSocketFactory(sslContext));
226+
connMgrBuilder.setSSLSocketFactory(sslConnectionSocketFactory);
225227
connMgrBuilder.setDefaultSocketConfig(socketConfig);
226228
PoolingHttpClientConnectionManager phccm = connMgrBuilder.build();
227229
if (metricsRegistry != null ) {
@@ -238,12 +240,12 @@ private HttpClientConnectionManager poolConnectionManager(SSLContext sslContext,
238240
return phccm;
239241
}
240242

241-
public CloseableHttpClient createHttpClient() {
242-
243+
public CloseableHttpClient createHttpClient(boolean initSslContext) {
243244
// Top Level builders
244245
HttpClientBuilder clientBuilder = HttpClientBuilder.create();
245-
SSLContext sslContext = createSSLContext();
246-
246+
SSLContext sslContext = initSslContext ? createSSLContext() : null;
247+
LayeredConnectionSocketFactory sslConnectionSocketFactory = sslContext == null ? new DummySSLConnectionSocketFactory()
248+
: new SSLConnectionSocketFactory(sslContext);
247249
// Socket configuration
248250
SocketConfig.Builder soCfgBuilder = SocketConfig.custom();
249251
MapUtils.applyInt(chConfiguration, ClientConfigProperties.SOCKET_OPERATION_TIMEOUT.getKey(),
@@ -287,9 +289,9 @@ public CloseableHttpClient createHttpClient() {
287289
// Connection manager
288290
boolean isConnectionPooling = MapUtils.getFlag(chConfiguration, "connection_pool_enabled");
289291
if (isConnectionPooling) {
290-
clientBuilder.setConnectionManager(poolConnectionManager(sslContext, socketConfig));
292+
clientBuilder.setConnectionManager(poolConnectionManager(sslConnectionSocketFactory, socketConfig));
291293
} else {
292-
clientBuilder.setConnectionManager(basicConnectionManager(sslContext, socketConfig));
294+
clientBuilder.setConnectionManager(basicConnectionManager(sslConnectionSocketFactory, socketConfig));
293295
}
294296
long keepAliveTimeout = MapUtils.getLong(chConfiguration, ClientConfigProperties.HTTP_KEEP_ALIVE_TIMEOUT.getKey());
295297
if (keepAliveTimeout > 0) {
@@ -748,4 +750,26 @@ private String buildDefaultUserAgent() {
748750
public void close() {
749751
httpClient.close(CloseMode.IMMEDIATE);
750752
}
753+
754+
755+
/**
756+
* This factory is used only when no ssl connections are required (no https endpoints).
757+
* Internally http client would create factory and spend time if no supplied.
758+
*/
759+
private static class DummySSLConnectionSocketFactory implements LayeredConnectionSocketFactory {
760+
@Override
761+
public Socket createLayeredSocket(Socket socket, String target, int port, HttpContext context) throws IOException {
762+
return null;
763+
}
764+
765+
@Override
766+
public Socket createSocket(HttpContext context) throws IOException {
767+
return null;
768+
}
769+
770+
@Override
771+
public Socket connectSocket(TimeValue connectTimeout, Socket socket, HttpHost host, InetSocketAddress remoteAddress, InetSocketAddress localAddress, HttpContext context) throws IOException {
772+
return null;
773+
}
774+
}
751775
}

client-v2/src/test/java/com/clickhouse/client/ClientTests.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.clickhouse.client;
22

33
import com.clickhouse.client.api.Client;
4-
import com.clickhouse.client.api.ClientConfigProperties;
54
import com.clickhouse.client.api.ClientException;
65
import com.clickhouse.client.api.enums.Protocol;
76
import com.clickhouse.client.api.query.GenericRecord;
@@ -17,7 +16,6 @@
1716

1817
import java.net.ConnectException;
1918
import java.util.HashMap;
20-
import java.util.List;
2119
import java.util.Map;
2220
import java.util.Optional;
2321
import java.util.concurrent.ExecutorService;
@@ -153,6 +151,18 @@ public void testProvidedExecutor() throws Exception {
153151
Assert.assertFalse(flag.get());
154152
}
155153

154+
@Test
155+
public void testLoadingServerContext() throws Exception {
156+
long start = System.nanoTime();
157+
try (Client client = newClient().build()) {
158+
long initTime = (System.nanoTime() - start) / 1_000_000;
159+
Assert.assertTrue(initTime < 100);
160+
Assert.assertNull(client.getServerVersion());
161+
client.loadServerInfo();
162+
Assert.assertNotNull(client.getServerVersion());
163+
}
164+
}
165+
156166
protected Client.Builder newClient() {
157167
ClickHouseNode node = getServer(ClickHouseProtocol.HTTP);
158168
boolean isSecure = isCloud();

client-v2/src/test/java/com/clickhouse/client/metrics/MetricsTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ public void testRegisterMetrics() throws Exception {
4949
.registerClientMetrics(meterRegistry, "pool-test")
5050
.build()) {
5151

52+
client.ping();
5253
Gauge totalMax = meterRegistry.get("httpcomponents.httpclient.pool.total.max").gauge();
5354
Gauge available = meterRegistry.get("httpcomponents.httpclient.pool.total.connections").tags("state", "available").gauge();
5455
Gauge leased = meterRegistry.get("httpcomponents.httpclient.pool.total.connections").tags("state", "leased").gauge();

jdbc-v2/src/main/java/com/clickhouse/jdbc/ConnectionImpl.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
package com.clickhouse.jdbc;
22

33
import com.clickhouse.client.api.Client;
4-
import com.clickhouse.client.api.ClientConfigProperties;
54
import com.clickhouse.client.api.internal.ServerSettings;
65
import com.clickhouse.client.api.query.GenericRecord;
76
import com.clickhouse.client.api.query.QuerySettings;
87
import com.clickhouse.data.ClickHouseDataType;
98
import com.clickhouse.jdbc.internal.ClientInfoProperties;
109
import com.clickhouse.jdbc.internal.JdbcConfiguration;
1110
import com.clickhouse.jdbc.internal.ExceptionUtils;
12-
import com.clickhouse.jdbc.internal.JdbcConfiguration;
1311
import com.clickhouse.jdbc.internal.JdbcUtils;
1412
import org.slf4j.Logger;
1513
import org.slf4j.LoggerFactory;
@@ -32,8 +30,6 @@
3230
import java.sql.ShardingKey;
3331
import java.sql.Statement;
3432
import java.sql.Struct;
35-
import java.sql.Types;
36-
import java.time.temporal.ChronoUnit;
3733
import java.util.HashSet;
3834
import java.util.List;
3935
import java.util.Map;
@@ -76,6 +72,7 @@ public ConnectionImpl(String url, Properties info) throws SQLException {
7672
this.client = this.config.applyClientProperties(new Client.Builder())
7773
.setClientName(clientName)
7874
.build();
75+
this.client.loadServerInfo();
7976
this.schema = client.getDefaultDatabase();
8077
this.defaultQuerySettings = new QuerySettings()
8178
.serverSetting(ServerSettings.ASYNC_INSERT, "0")

jdbc-v2/src/test/java/com/clickhouse/jdbc/DriverTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public void testConnect() {
2525
Driver driver = new Driver();
2626
Properties props = new Properties();
2727
props.put(ClientConfigProperties.USER.getKey(), ClientConfigProperties.USER.getDefaultValue());
28-
props.put(ClientConfigProperties.PASSWORD.getKey(), ClientConfigProperties.PASSWORD.getDefaultValue());
28+
props.put(ClientConfigProperties.PASSWORD.getKey(), getPassword());
2929
Assert.assertNotNull(driver.connect(getEndpointString(), props));
3030
} catch (SQLException e) {
3131
Assert.fail("Failed to connect to ClickHouse", e);

jwt-gen.js

Lines changed: 0 additions & 18 deletions
This file was deleted.

0 commit comments

Comments
 (0)