From 32c65d7b606180523a2520e338bfa0f79e0cbfd1 Mon Sep 17 00:00:00 2001 From: Alex Castillo Date: Sat, 7 Nov 2026 00:37:47 -0400 Subject: [PATCH] fix(client-v2): record connection times only when metrics are enabled --- .../api/internal/HttpAPIClientHelper.java | 11 ++++++ .../MeteredConnectionFactoryTest.java | 38 +++++++++++++++++++ history/latest/3253.md | 4 ++ 3 files changed, 53 insertions(+) create mode 100644 client-v2/src/test/java/com/clickhouse/client/api/internal/MeteredConnectionFactoryTest.java create mode 100644 history/latest/3253.md diff --git a/client-v2/src/main/java/com/clickhouse/client/api/internal/HttpAPIClientHelper.java b/client-v2/src/main/java/com/clickhouse/client/api/internal/HttpAPIClientHelper.java index 07f8e4f54..20f33ce53 100644 --- a/client-v2/src/main/java/com/clickhouse/client/api/internal/HttpAPIClientHelper.java +++ b/client-v2/src/main/java/com/clickhouse/client/api/internal/HttpAPIClientHelper.java @@ -297,6 +297,7 @@ private HttpClientConnectionManager poolConnectionManager(LayeredConnectionSocke PoolingHttpClientConnectionManager phccm = connMgrBuilder.build(); poolControl = phccm; if (metricsRegistry != null) { + connectionFactory.enableMetrics(); try { String mGroupName = ClientConfigProperties.METRICS_GROUP_NAME.getOrDefault(configuration); Class micrometerLoader = getClass().getClassLoader().loadClass("com.clickhouse.client.api.metrics.MicrometerLoader"); @@ -1380,9 +1381,19 @@ public MeteredManagedHttpClientConnectionFactory(Http1Config http1Config, CharCo ConcurrentLinkedQueue times = new ConcurrentLinkedQueue<>(); + // Connection times are drained only by the metrics gauge (see getTime()), so they are recorded + // only when a metrics registry is attached. Otherwise the queue would grow with every new connection. + private volatile boolean metricsEnabled = false; + + public void enableMetrics() { + this.metricsEnabled = true; + } @Override public ManagedHttpClientConnection createConnection(Socket socket) throws IOException { + if (!metricsEnabled) { + return super.createConnection(socket); + } long startT = System.currentTimeMillis(); try { return super.createConnection(socket); diff --git a/client-v2/src/test/java/com/clickhouse/client/api/internal/MeteredConnectionFactoryTest.java b/client-v2/src/test/java/com/clickhouse/client/api/internal/MeteredConnectionFactoryTest.java new file mode 100644 index 000000000..48bbc96a4 --- /dev/null +++ b/client-v2/src/test/java/com/clickhouse/client/api/internal/MeteredConnectionFactoryTest.java @@ -0,0 +1,38 @@ +package com.clickhouse.client.api.internal; + +import org.apache.hc.core5.http.config.CharCodingConfig; +import org.apache.hc.core5.http.config.Http1Config; +import org.apache.hc.core5.http.impl.io.DefaultHttpResponseParserFactory; +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.net.Socket; + +public class MeteredConnectionFactoryTest { + + private static HttpAPIClientHelper.MeteredManagedHttpClientConnectionFactory newFactory() { + return new HttpAPIClientHelper.MeteredManagedHttpClientConnectionFactory( + Http1Config.DEFAULT, CharCodingConfig.DEFAULT, DefaultHttpResponseParserFactory.INSTANCE); + } + + @Test(groups = {"unit"}) + public void testConnectionTimesNotRecordedWithoutMetrics() throws Exception { + HttpAPIClientHelper.MeteredManagedHttpClientConnectionFactory factory = newFactory(); + for (int i = 0; i < 10; i++) { + factory.createConnection(new Socket()); + } + Assert.assertEquals(factory.times.size(), 0, "Connection times must not accumulate when metrics are disabled"); + } + + @Test(groups = {"unit"}) + public void testConnectionTimesRecordedAndDrainedWithMetrics() throws Exception { + HttpAPIClientHelper.MeteredManagedHttpClientConnectionFactory factory = newFactory(); + factory.enableMetrics(); + for (int i = 0; i < 10; i++) { + factory.createConnection(new Socket()); + } + Assert.assertEquals(factory.times.size(), 10); + factory.getTime(); + Assert.assertEquals(factory.times.size(), 0); + } +} diff --git a/history/latest/3253.md b/history/latest/3253.md new file mode 100644 index 000000000..0c722a0c5 --- /dev/null +++ b/history/latest/3253.md @@ -0,0 +1,4 @@ +- **[client-v2]** Fixed a memory leak where every new pooled HTTP connection added one entry to an internal queue of + connection times that was only drained by the metrics gauge. Without a metrics registry the queue was never emptied + and grew for the lifetime of the client. Connection times are now recorded only when metrics are enabled. `jdbc-v2` + is affected as well because it uses `client-v2`. (https://github.com/ClickHouse/clickhouse-java/issues/3253)