Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix prometheus histogram metric tracker for multiple pools #1692

Merged
merged 2 commits into from Jan 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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