diff --git a/src/main/java/com/zaxxer/hikari/metrics/prometheus/PrometheusHistogramMetricsTracker.java b/src/main/java/com/zaxxer/hikari/metrics/prometheus/PrometheusHistogramMetricsTracker.java index 4b7befce5..d7ee62730 100644 --- a/src/main/java/com/zaxxer/hikari/metrics/prometheus/PrometheusHistogramMetricsTracker.java +++ b/src/main/java/com/zaxxer/hikari/metrics/prometheus/PrometheusHistogramMetricsTracker.java @@ -20,6 +20,12 @@ import io.prometheus.client.Counter; import io.prometheus.client.Histogram; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import static com.zaxxer.hikari.metrics.prometheus.PrometheusMetricsTrackerFactory.*; +import static com.zaxxer.hikari.metrics.prometheus.PrometheusMetricsTrackerFactory.RegistrationStatus.REGISTERED; + /** * Alternative Prometheus metrics tracker using a Histogram instead of Summary *

@@ -56,12 +62,19 @@ private static Histogram registerHistogram(String name, String help, double buck .create(); } + private final static Map registrationStatuses = new ConcurrentHashMap<>(); + + private final String poolName; + private final HikariCPCollector hikariCPCollector; + private final Histogram.Child elapsedAcquiredHistogramChild; private final Histogram.Child elapsedBorrowedHistogramChild; private final Histogram.Child elapsedCreationHistogramChild; - PrometheusHistogramMetricsTracker(String poolName, CollectorRegistry collectorRegistry) { + PrometheusHistogramMetricsTracker(String poolName, CollectorRegistry collectorRegistry, HikariCPCollector hikariCPCollector) { registerMetrics(collectorRegistry); + this.poolName = poolName; + this.hikariCPCollector = hikariCPCollector; this.connectionTimeoutCounterChild = CONNECTION_TIMEOUT_COUNTER.labels(poolName); this.elapsedAcquiredHistogramChild = ELAPSED_ACQUIRED_HISTOGRAM.labels(poolName); this.elapsedBorrowedHistogramChild = ELAPSED_BORROWED_HISTOGRAM.labels(poolName); @@ -69,10 +82,12 @@ private static Histogram registerHistogram(String name, String help, double buck } private void registerMetrics(CollectorRegistry collectorRegistry) { - CONNECTION_TIMEOUT_COUNTER.register(collectorRegistry); - ELAPSED_ACQUIRED_HISTOGRAM.register(collectorRegistry); - ELAPSED_BORROWED_HISTOGRAM.register(collectorRegistry); - ELAPSED_CREATION_HISTOGRAM.register(collectorRegistry); + if (registrationStatuses.putIfAbsent(collectorRegistry, REGISTERED) == null) { + CONNECTION_TIMEOUT_COUNTER.register(collectorRegistry); + ELAPSED_ACQUIRED_HISTOGRAM.register(collectorRegistry); + ELAPSED_BORROWED_HISTOGRAM.register(collectorRegistry); + ELAPSED_CREATION_HISTOGRAM.register(collectorRegistry); + } } @Override @@ -94,4 +109,13 @@ public void recordConnectionCreatedMillis(long connectionCreatedMillis) { public void recordConnectionTimeout() { connectionTimeoutCounterChild.inc(); } + + @Override + public void close() { + hikariCPCollector.remove(poolName); + CONNECTION_TIMEOUT_COUNTER.remove(poolName); + ELAPSED_ACQUIRED_HISTOGRAM.remove(poolName); + ELAPSED_BORROWED_HISTOGRAM.remove(poolName); + ELAPSED_CREATION_HISTOGRAM.remove(poolName); + } } diff --git a/src/main/java/com/zaxxer/hikari/metrics/prometheus/PrometheusHistogramMetricsTrackerFactory.java b/src/main/java/com/zaxxer/hikari/metrics/prometheus/PrometheusHistogramMetricsTrackerFactory.java index 6ace160f8..612b4b2ce 100644 --- a/src/main/java/com/zaxxer/hikari/metrics/prometheus/PrometheusHistogramMetricsTrackerFactory.java +++ b/src/main/java/com/zaxxer/hikari/metrics/prometheus/PrometheusHistogramMetricsTrackerFactory.java @@ -19,8 +19,15 @@ import com.zaxxer.hikari.metrics.IMetricsTracker; import com.zaxxer.hikari.metrics.MetricsTrackerFactory; import com.zaxxer.hikari.metrics.PoolStats; +import com.zaxxer.hikari.metrics.prometheus.PrometheusMetricsTrackerFactory.RegistrationStatus; +import io.prometheus.client.Collector; import io.prometheus.client.CollectorRegistry; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import static com.zaxxer.hikari.metrics.prometheus.PrometheusMetricsTrackerFactory.RegistrationStatus.*; + /** *

{@code
  * HikariConfig config = new HikariConfig();
@@ -29,16 +36,18 @@
  */
 public class PrometheusHistogramMetricsTrackerFactory implements MetricsTrackerFactory {
 
-   private HikariCPCollector collector;
+   private final static Map registrationStatuses = new ConcurrentHashMap<>();
+
+   private final HikariCPCollector collector = new HikariCPCollector();
 
-   private CollectorRegistry collectorRegistry;
+   private final CollectorRegistry collectorRegistry;
 
    /**
     * Default Constructor. The Hikari metrics are registered to the default
     * collector registry ({@code CollectorRegistry.defaultRegistry}).
     */
    public PrometheusHistogramMetricsTrackerFactory() {
-      this.collectorRegistry = CollectorRegistry.defaultRegistry;
+      this(CollectorRegistry.defaultRegistry);
    }
 
    /**
@@ -51,17 +60,14 @@ public PrometheusHistogramMetricsTrackerFactory(CollectorRegistry collectorRegis
 
    @Override
    public IMetricsTracker create(String poolName, PoolStats poolStats) {
-      getCollector().add(poolName, poolStats);
-      return new PrometheusHistogramMetricsTracker(poolName, this.collectorRegistry);
+      registerCollector(this.collector, this.collectorRegistry);
+      this.collector.add(poolName, poolStats);
+      return new PrometheusHistogramMetricsTracker(poolName, this.collectorRegistry, this.collector);
    }
 
-   /**
-    * initialize and register collector if it isn't initialized yet
-    */
-   private HikariCPCollector getCollector() {
-      if (collector == null) {
-         collector = new HikariCPCollector().register(this.collectorRegistry);
+   private void registerCollector(Collector collector, CollectorRegistry collectorRegistry) {
+      if (registrationStatuses.putIfAbsent(collectorRegistry, REGISTERED) == null) {
+         collector.register(collectorRegistry);
       }
-      return collector;
    }
 }
diff --git a/src/main/java/com/zaxxer/hikari/metrics/prometheus/PrometheusMetricsTrackerFactory.java b/src/main/java/com/zaxxer/hikari/metrics/prometheus/PrometheusMetricsTrackerFactory.java
index f0dbb6c30..f24ec3595 100644
--- a/src/main/java/com/zaxxer/hikari/metrics/prometheus/PrometheusMetricsTrackerFactory.java
+++ b/src/main/java/com/zaxxer/hikari/metrics/prometheus/PrometheusMetricsTrackerFactory.java
@@ -49,9 +49,9 @@ public class PrometheusMetricsTrackerFactory implements MetricsTrackerFactory
 
    private final CollectorRegistry collectorRegistry;
 
-   public enum RegistrationStatus
+   enum RegistrationStatus
    {
-      REGISTERED;
+      REGISTERED
    }
 
    /**
diff --git a/src/test/java/com/zaxxer/hikari/metrics/prometheus/PrometheusHistogramMetricsTrackerTest.java b/src/test/java/com/zaxxer/hikari/metrics/prometheus/PrometheusHistogramMetricsTrackerTest.java
index 418c317b0..e26940742 100644
--- a/src/test/java/com/zaxxer/hikari/metrics/prometheus/PrometheusHistogramMetricsTrackerTest.java
+++ b/src/test/java/com/zaxxer/hikari/metrics/prometheus/PrometheusHistogramMetricsTrackerTest.java
@@ -32,24 +32,26 @@
 
 public class PrometheusHistogramMetricsTrackerTest {
 
-   private CollectorRegistry collectorRegistry;
+   private CollectorRegistry defaultCollectorRegistry;
+   private CollectorRegistry customCollectorRegistry;
 
    private static final String POOL_LABEL_NAME = "pool";
+   private static final String[] LABEL_NAMES = {POOL_LABEL_NAME};
 
    @Before
-   public void setupCollectorRegistry(){
-      this.collectorRegistry = new CollectorRegistry();
+   public void setupCollectorRegistry() {
+      this.defaultCollectorRegistry = new CollectorRegistry();
+      this.customCollectorRegistry = new CollectorRegistry();
    }
 
    @Test
    public void recordConnectionTimeout() throws Exception {
       HikariConfig config = newHikariConfig();
-      config.setMetricsTrackerFactory(new PrometheusHistogramMetricsTrackerFactory(collectorRegistry));
+      config.setMetricsTrackerFactory(new PrometheusHistogramMetricsTrackerFactory(defaultCollectorRegistry));
       config.setJdbcUrl("jdbc:h2:mem:");
       config.setMaximumPoolSize(2);
       config.setConnectionTimeout(250);
 
-      String[] labelNames = {POOL_LABEL_NAME};
       String[] labelValues = {config.getPoolName()};
 
       try (HikariDataSource hikariDataSource = new HikariDataSource(config)) {
@@ -60,9 +62,9 @@ public void recordConnectionTimeout() throws Exception {
             }
          }
 
-         Double total = collectorRegistry.getSampleValue(
+         Double total = defaultCollectorRegistry.getSampleValue(
             "hikaricp_connection_timeout_total",
-            labelNames,
+            LABEL_NAMES,
             labelValues
          );
          assertThat(total, is(1.0));
@@ -85,57 +87,87 @@ public void connectionCreationMetrics() {
    }
 
    @Test
-   public void testMultiplePoolName() throws Exception {
-      String[] labelNames = {POOL_LABEL_NAME};
-
-      HikariConfig config = newHikariConfig();
-      config.setMetricsTrackerFactory(new PrometheusHistogramMetricsTrackerFactory(collectorRegistry));
-      config.setPoolName("first");
-      config.setJdbcUrl("jdbc:h2:mem:");
-      config.setMaximumPoolSize(2);
-      config.setConnectionTimeout(250);
-      String[] labelValues1 = {config.getPoolName()};
+   public void testMultiplePoolNameWithOneCollectorRegistry()
+   {
+      HikariConfig configFirstPool = newHikariConfig();
+      configFirstPool.setMetricsTrackerFactory(new PrometheusHistogramMetricsTrackerFactory(defaultCollectorRegistry));
+      configFirstPool.setPoolName("first");
+      configFirstPool.setJdbcUrl("jdbc:h2:mem:");
+      configFirstPool.setMaximumPoolSize(2);
+      configFirstPool.setConnectionTimeout(250);
+
+      HikariConfig configSecondPool = newHikariConfig();
+      configSecondPool.setMetricsTrackerFactory(new PrometheusHistogramMetricsTrackerFactory(defaultCollectorRegistry));
+      configSecondPool.setPoolName("second");
+      configSecondPool.setJdbcUrl("jdbc:h2:mem:");
+      configSecondPool.setMaximumPoolSize(4);
+      configSecondPool.setConnectionTimeout(250);
+
+      String[] labelValuesFirstPool = {configFirstPool.getPoolName()};
+      String[] labelValuesSecondPool = {configSecondPool.getPoolName()};
+
+      try (HikariDataSource ignoredFirstPool = new HikariDataSource(configFirstPool)) {
+         assertThat(defaultCollectorRegistry.getSampleValue(
+            "hikaricp_connection_timeout_total", LABEL_NAMES, labelValuesFirstPool),
+            is(0.0));
+
+         try (HikariDataSource ignoredSecondPool = new HikariDataSource(configSecondPool)) {
+            assertThat(defaultCollectorRegistry.getSampleValue(
+               "hikaricp_connection_timeout_total", LABEL_NAMES, labelValuesSecondPool),
+               is(0.0));
+         }
+      }
+   }
 
-      try (HikariDataSource ignored = new HikariDataSource(config)) {
-         assertThat(collectorRegistry.getSampleValue(
-            "hikaricp_connection_timeout_total",
-            labelNames,
-            labelValues1), is(0.0));
-
-         CollectorRegistry collectorRegistry2 = new CollectorRegistry();
-         HikariConfig config2 = newHikariConfig();
-         config2.setMetricsTrackerFactory(new PrometheusHistogramMetricsTrackerFactory(collectorRegistry2));
-         config2.setPoolName("second");
-         config2.setJdbcUrl("jdbc:h2:mem:");
-         config2.setMaximumPoolSize(4);
-         config2.setConnectionTimeout(250);
-         String[] labelValues2 = {config2.getPoolName()};
-
-         try (HikariDataSource ignored2 = new HikariDataSource(config2)) {
-            assertThat(collectorRegistry2.getSampleValue(
-               "hikaricp_connection_timeout_total",
-               labelNames,
-               labelValues2), is(0.0));
+   @Test
+   public void testMultiplePoolNameWithDifferentCollectorRegistries()
+   {
+      HikariConfig configFirstPool = newHikariConfig();
+      configFirstPool.setMetricsTrackerFactory(new PrometheusHistogramMetricsTrackerFactory(defaultCollectorRegistry));
+      configFirstPool.setPoolName("first");
+      configFirstPool.setJdbcUrl("jdbc:h2:mem:");
+      configFirstPool.setMaximumPoolSize(2);
+      configFirstPool.setConnectionTimeout(250);
+
+      HikariConfig configSecondPool = newHikariConfig();
+      configSecondPool.setMetricsTrackerFactory(new PrometheusHistogramMetricsTrackerFactory(customCollectorRegistry));
+      configSecondPool.setPoolName("second");
+      configSecondPool.setJdbcUrl("jdbc:h2:mem:");
+      configSecondPool.setMaximumPoolSize(4);
+      configSecondPool.setConnectionTimeout(250);
+
+      String[] labelValuesFirstPool = {configFirstPool.getPoolName()};
+      String[] labelValuesSecondPool = {configSecondPool.getPoolName()};
+
+      try (HikariDataSource ignoredFirstPool = new HikariDataSource(configFirstPool)) {
+         assertThat(defaultCollectorRegistry.getSampleValue(
+            "hikaricp_connection_timeout_total", LABEL_NAMES, labelValuesFirstPool),
+            is(0.0));
+
+         try (HikariDataSource ignoredSecondPool = new HikariDataSource(configSecondPool)) {
+            assertThat(customCollectorRegistry.getSampleValue(
+               "hikaricp_connection_timeout_total", LABEL_NAMES, labelValuesSecondPool),
+               is(0.0));
          }
       }
    }
 
    private void checkSummaryMetricFamily(String metricName) {
       HikariConfig config = newHikariConfig();
-      config.setMetricsTrackerFactory(new PrometheusHistogramMetricsTrackerFactory(collectorRegistry));
+      config.setMetricsTrackerFactory(new PrometheusHistogramMetricsTrackerFactory(defaultCollectorRegistry));
       config.setJdbcUrl("jdbc:h2:mem:");
 
       try (HikariDataSource ignored = new HikariDataSource(config)) {
-         Double count = collectorRegistry.getSampleValue(
+         Double count = defaultCollectorRegistry.getSampleValue(
             metricName + "_count",
-            new String[]{POOL_LABEL_NAME},
+            LABEL_NAMES,
             new String[]{config.getPoolName()}
          );
          assertNotNull(count);
 
-         Double sum = collectorRegistry.getSampleValue(
+         Double sum = defaultCollectorRegistry.getSampleValue(
             metricName + "_sum",
-            new String[]{POOL_LABEL_NAME},
+            LABEL_NAMES,
             new String[]{config.getPoolName()}
          );
          assertNotNull(sum);