Skip to content

Commit

Permalink
Fix prometheus histogram metric tracker for multiple pools (#1692)
Browse files Browse the repository at this point in the history
* Add support for multiple prometheus histogram metric trackers

* Add tests
  • Loading branch information
vaIgarashi committed Jan 12, 2021
1 parent 0561309 commit 28cb7c0
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 61 deletions.
Expand Up @@ -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
* <p>
Expand Down Expand Up @@ -56,23 +62,32 @@ private static Histogram registerHistogram(String name, String help, double buck
.create();
}

private final static Map<CollectorRegistry, RegistrationStatus> 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);
this.elapsedCreationHistogramChild = ELAPSED_CREATION_HISTOGRAM.labels(poolName);
}

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
Expand All @@ -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);
}
}
Expand Up @@ -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.*;

/**
* <pre>{@code
* HikariConfig config = new HikariConfig();
Expand All @@ -29,16 +36,18 @@
*/
public class PrometheusHistogramMetricsTrackerFactory implements MetricsTrackerFactory {

private HikariCPCollector collector;
private final static Map<CollectorRegistry, RegistrationStatus> 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);
}

/**
Expand All @@ -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;
}
}
Expand Up @@ -49,9 +49,9 @@ public class PrometheusMetricsTrackerFactory implements MetricsTrackerFactory

private final CollectorRegistry collectorRegistry;

public enum RegistrationStatus
enum RegistrationStatus
{
REGISTERED;
REGISTERED
}

/**
Expand Down
Expand Up @@ -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)) {
Expand All @@ -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));
Expand All @@ -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);
Expand Down

0 comments on commit 28cb7c0

Please sign in to comment.