Refactor/fix Prometheus metrics for multiple connection pools, add unit tests ()

* Refactor/fix Prometheus metrics for multiple connection pools

Changes:
* Fix "Collector already registered that provides name:
hikaricp_connection_timeout_total" error
* Register only one HikariCPCollector instance in one CollectorRegistry
instance
* Add ability to remove metrics when connection pool is shutting down
* Add/update unit tests

* Refactor/add unit tests - metrics package

* Re-format curly braces to be inline with the whole code base
pull/1401/head
Aleksandr Podkutin committed by Brett Wooldridge
parent c509ec1a3f
commit 086bf18389

@ -5,7 +5,8 @@ import com.zaxxer.hikari.metrics.MetricsTrackerFactory;
import com.zaxxer.hikari.metrics.PoolStats;
import io.micrometer.core.instrument.MeterRegistry;
public class MicrometerMetricsTrackerFactory implements MetricsTrackerFactory {
public class MicrometerMetricsTrackerFactory implements MetricsTrackerFactory
{
private final MeterRegistry registry;

@ -12,13 +12,14 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
*/
package com.zaxxer.hikari.metrics.prometheus;
import com.zaxxer.hikari.metrics.PoolStats;
import io.prometheus.client.Collector;
import io.prometheus.client.GaugeMetricFamily;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -26,14 +27,16 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
class HikariCPCollector extends Collector {
class HikariCPCollector extends Collector
{
private static final List<String> LABEL_NAMES = Collections.singletonList("pool");
private final Map<String, PoolStats> poolStatsMap = new ConcurrentHashMap<>();
@Override
public List<MetricFamilySamples> collect() {
public List<MetricFamilySamples> collect()
{
return Arrays.asList(
createGauge("hikaricp_active_connections", "Active connections",
PoolStats::getActiveConnections),
@ -50,13 +53,19 @@ class HikariCPCollector extends Collector {
);
}
protected HikariCPCollector add(String name, PoolStats poolStats) {
void add(String name, PoolStats poolStats)
{
poolStatsMap.put(name, poolStats);
return this;
}
void remove(String name)
{
poolStatsMap.remove(name);
}
private GaugeMetricFamily createGauge(String metric, String help,
Function<PoolStats, Integer> metricValueFunction) {
Function<PoolStats, Integer> metricValueFunction)
{
GaugeMetricFamily metricFamily = new GaugeMetricFamily(metric, help, LABEL_NAMES);
poolStatsMap.forEach((k, v) -> metricFamily.addMetric(
Collections.singletonList(k),

@ -12,66 +12,69 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
*/
package com.zaxxer.hikari.metrics.prometheus;
import com.zaxxer.hikari.metrics.IMetricsTracker;
import com.zaxxer.hikari.metrics.prometheus.PrometheusMetricsTrackerFactory.RegistrationStatus;
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.Counter;
import io.prometheus.client.Summary;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import static com.zaxxer.hikari.metrics.prometheus.PrometheusMetricsTrackerFactory.RegistrationStatus.REGISTERED;
class PrometheusMetricsTracker implements IMetricsTracker
{
private final Counter CONNECTION_TIMEOUT_COUNTER = Counter.build()
private final static Counter CONNECTION_TIMEOUT_COUNTER = Counter.build()
.name("hikaricp_connection_timeout_total")
.labelNames("pool")
.help("Connection timeout total count")
.create();
private final Summary ELAPSED_ACQUIRED_SUMMARY =
registerSummary("hikaricp_connection_acquired_nanos", "Connection acquired time (ns)");
private final static Summary ELAPSED_ACQUIRED_SUMMARY =
createSummary("hikaricp_connection_acquired_nanos", "Connection acquired time (ns)");
private final Summary ELAPSED_BORROWED_SUMMARY =
registerSummary("hikaricp_connection_usage_millis", "Connection usage (ms)");
private final static Summary ELAPSED_USAGE_SUMMARY =
createSummary("hikaricp_connection_usage_millis", "Connection usage (ms)");
private final Summary ELAPSED_CREATION_SUMMARY =
registerSummary("hikaricp_connection_creation_millis", "Connection creation (ms)");
private final static Summary ELAPSED_CREATION_SUMMARY =
createSummary("hikaricp_connection_creation_millis", "Connection creation (ms)");
private final Counter.Child connectionTimeoutCounterChild;
private final static Map<CollectorRegistry, RegistrationStatus> registrationStatuses = new ConcurrentHashMap<>();
private Summary registerSummary(String name, String help) {
return Summary.build()
.name(name)
.labelNames("pool")
.help(help)
.quantile(0.5, 0.05)
.quantile(0.95, 0.01)
.quantile(0.99, 0.001)
.maxAgeSeconds(TimeUnit.MINUTES.toSeconds(5))
.ageBuckets(5)
.create();
}
private final String poolName;
private final HikariCPCollector hikariCPCollector;
private final Counter.Child connectionTimeoutCounterChild;
private final Summary.Child elapsedAcquiredSummaryChild;
private final Summary.Child elapsedBorrowedSummaryChild;
private final Summary.Child elapsedUsageSummaryChild;
private final Summary.Child elapsedCreationSummaryChild;
PrometheusMetricsTracker(String poolName, CollectorRegistry collectorRegistry) {
PrometheusMetricsTracker(String poolName, CollectorRegistry collectorRegistry, HikariCPCollector hikariCPCollector)
{
registerMetrics(collectorRegistry);
this.poolName = poolName;
this.hikariCPCollector = hikariCPCollector;
this.connectionTimeoutCounterChild = CONNECTION_TIMEOUT_COUNTER.labels(poolName);
this.elapsedAcquiredSummaryChild = ELAPSED_ACQUIRED_SUMMARY.labels(poolName);
this.elapsedBorrowedSummaryChild = ELAPSED_BORROWED_SUMMARY.labels(poolName);
this.elapsedUsageSummaryChild = ELAPSED_USAGE_SUMMARY.labels(poolName);
this.elapsedCreationSummaryChild = ELAPSED_CREATION_SUMMARY.labels(poolName);
}
private void registerMetrics(CollectorRegistry collectorRegistry){
CONNECTION_TIMEOUT_COUNTER.register(collectorRegistry);
ELAPSED_ACQUIRED_SUMMARY.register(collectorRegistry);
ELAPSED_BORROWED_SUMMARY.register(collectorRegistry);
ELAPSED_CREATION_SUMMARY.register(collectorRegistry);
private void registerMetrics(CollectorRegistry collectorRegistry)
{
if (registrationStatuses.putIfAbsent(collectorRegistry, REGISTERED) == null) {
CONNECTION_TIMEOUT_COUNTER.register(collectorRegistry);
ELAPSED_ACQUIRED_SUMMARY.register(collectorRegistry);
ELAPSED_USAGE_SUMMARY.register(collectorRegistry);
ELAPSED_CREATION_SUMMARY.register(collectorRegistry);
}
}
@Override
@ -83,7 +86,7 @@ class PrometheusMetricsTracker implements IMetricsTracker
@Override
public void recordConnectionUsageMillis(long elapsedBorrowedMillis)
{
elapsedBorrowedSummaryChild.observe(elapsedBorrowedMillis);
elapsedUsageSummaryChild.observe(elapsedBorrowedMillis);
}
@Override
@ -97,4 +100,28 @@ class PrometheusMetricsTracker implements IMetricsTracker
{
connectionTimeoutCounterChild.inc();
}
private static Summary createSummary(String name, String help)
{
return Summary.build()
.name(name)
.labelNames("pool")
.help(help)
.quantile(0.5, 0.05)
.quantile(0.95, 0.01)
.quantile(0.99, 0.001)
.maxAgeSeconds(TimeUnit.MINUTES.toSeconds(5))
.ageBuckets(5)
.create();
}
@Override
public void close()
{
hikariCPCollector.remove(poolName);
CONNECTION_TIMEOUT_COUNTER.remove(poolName);
ELAPSED_ACQUIRED_SUMMARY.remove(poolName);
ELAPSED_USAGE_SUMMARY.remove(poolName);
ELAPSED_CREATION_SUMMARY.remove(poolName);
}
}

@ -12,59 +12,78 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
*/
package com.zaxxer.hikari.metrics.prometheus;
import com.zaxxer.hikari.metrics.IMetricsTracker;
import com.zaxxer.hikari.metrics.MetricsTrackerFactory;
import com.zaxxer.hikari.metrics.PoolStats;
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.REGISTERED;
/**
* <pre>{@code
* HikariConfig config = new HikariConfig();
* config.setMetricsTrackerFactory(new PrometheusMetricsTrackerFactory());
* }</pre>
* or
* <pre>{@code
* config.setMetricsTrackerFactory(new PrometheusMetricsTrackerFactory(new CollectorRegistry()));
* }</pre>
*
* Note: the internal {@see io.prometheus.client.Summary} requires heavy locks. Consider using
* {@see PrometheusHistogramMetricsTrackerFactory} if performance plays a role and you don't need the summary per se.
*/
public class PrometheusMetricsTrackerFactory implements MetricsTrackerFactory {
public class PrometheusMetricsTrackerFactory implements MetricsTrackerFactory
{
private HikariCPCollector collector;
private final static Map<CollectorRegistry, RegistrationStatus> registrationStatuses = new ConcurrentHashMap<>();
private CollectorRegistry collectorRegistry;
private final HikariCPCollector collector = new HikariCPCollector();
private final CollectorRegistry collectorRegistry;
public enum RegistrationStatus
{
REGISTERED;
}
/**
* Default Constructor. The Hikari metrics are registered to the default
* collector registry ({@code CollectorRegistry.defaultRegistry}).
*/
public PrometheusMetricsTrackerFactory() {
this.collectorRegistry = CollectorRegistry.defaultRegistry;
public PrometheusMetricsTrackerFactory()
{
this(CollectorRegistry.defaultRegistry);
}
/**
* Constructor that allows to pass in a {@link CollectorRegistry} to which the
* Hikari metrics are registered.
*/
public PrometheusMetricsTrackerFactory(CollectorRegistry collectorRegistry) {
public PrometheusMetricsTrackerFactory(CollectorRegistry collectorRegistry)
{
this.collectorRegistry = collectorRegistry;
}
@Override
public IMetricsTracker create(String poolName, PoolStats poolStats) {
getCollector().add(poolName, poolStats);
return new PrometheusMetricsTracker(poolName, this.collectorRegistry);
public IMetricsTracker create(String poolName, PoolStats poolStats)
{
registerCollector(this.collector, this.collectorRegistry);
this.collector.add(poolName, poolStats);
return new PrometheusMetricsTracker(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;
}
}

@ -1,18 +1,18 @@
package com.zaxxer.hikari.metrics.dropwizard;
import static org.mockito.Mockito.verify;
import com.zaxxer.hikari.metrics.PoolStats;
import com.codahale.metrics.MetricRegistry;
import com.zaxxer.hikari.mocks.StubPoolStats;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import com.codahale.metrics.MetricRegistry;
import static org.mockito.Mockito.verify;
@RunWith(MockitoJUnitRunner.class)
public class CodaHaleMetricsTrackerTest {
public class CodaHaleMetricsTrackerTest
{
@Mock
public MetricRegistry mockMetricRegistry;
@ -20,12 +20,14 @@ public class CodaHaleMetricsTrackerTest {
private CodaHaleMetricsTracker testee;
@Before
public void setup() {
testee = new CodaHaleMetricsTracker("mypool", poolStats(), mockMetricRegistry);
public void setup()
{
testee = new CodaHaleMetricsTracker("mypool", new StubPoolStats(0), mockMetricRegistry);
}
@Test
public void close() throws Exception {
public void close()
{
testee.close();
verify(mockMetricRegistry).remove("mypool.pool.Wait");
@ -39,13 +41,4 @@ public class CodaHaleMetricsTrackerTest {
verify(mockMetricRegistry).remove("mypool.pool.MaxConnections");
verify(mockMetricRegistry).remove("mypool.pool.MinConnections");
}
private PoolStats poolStats() {
return new PoolStats(0) {
@Override
protected void update() {
// do nothing
}
};
}
}

@ -1,30 +1,28 @@
package com.zaxxer.hikari.metrics.micrometer;
import com.zaxxer.hikari.metrics.PoolStats;
import com.zaxxer.hikari.mocks.StubPoolStats;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class MicrometerMetricsTrackerTest {
public class MicrometerMetricsTrackerTest
{
private MeterRegistry mockMeterRegistry = new SimpleMeterRegistry();
private MicrometerMetricsTracker testee;
@Before
public void setup(){
testee = new MicrometerMetricsTracker("mypool", new PoolStats(1000L) {
@Override
protected void update() {
// nothing
}
}, mockMeterRegistry);
public void setup()
{
testee = new MicrometerMetricsTracker("mypool", new StubPoolStats(1000L), mockMeterRegistry);
}
@Test
public void close() throws Exception {
public void close()
{
Assert.assertNotNull(mockMeterRegistry.find("hikaricp.connections.acquire").tag("pool", "mypool").timer());
Assert.assertNotNull(mockMeterRegistry.find("hikaricp.connections.usage").tag("pool", "mypool").timer());
Assert.assertNotNull(mockMeterRegistry.find("hikaricp.connections.creation").tag("pool", "mypool").timer());

@ -19,10 +19,14 @@ package com.zaxxer.hikari.metrics.prometheus;
import static com.zaxxer.hikari.pool.TestElf.newHikariConfig;
import static com.zaxxer.hikari.util.UtilityElf.quietlySleep;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import java.sql.Connection;
import java.util.List;
import com.zaxxer.hikari.metrics.PoolStats;
import io.prometheus.client.Collector;
import org.junit.Before;
import org.junit.Test;
@ -32,18 +36,20 @@ import com.zaxxer.hikari.mocks.StubConnection;
import io.prometheus.client.CollectorRegistry;
public class HikariCPCollectorTest {
public class HikariCPCollectorTest
{
private CollectorRegistry collectorRegistry;
@Before
public void setupCollectorRegistry(){
public void setupCollectorRegistry()
{
this.collectorRegistry = new CollectorRegistry();
}
@Test
public void noConnection() throws Exception {
public void noConnection()
{
HikariConfig config = newHikariConfig();
config.setMinimumIdle(0);
config.setMetricsTrackerFactory(new PrometheusMetricsTrackerFactory(this.collectorRegistry));
@ -64,7 +70,8 @@ public class HikariCPCollectorTest {
}
@Test
public void noConnectionWithoutPoolName() throws Exception {
public void noConnectionWithoutPoolName()
{
HikariConfig config = new HikariConfig();
config.setMinimumIdle(0);
config.setMetricsTrackerFactory(new PrometheusMetricsTrackerFactory(this.collectorRegistry));
@ -86,7 +93,8 @@ public class HikariCPCollectorTest {
}
@Test
public void connection1() throws Exception {
public void connection1() throws Exception
{
HikariConfig config = newHikariConfig();
config.setMetricsTrackerFactory(new PrometheusMetricsTrackerFactory(this.collectorRegistry));
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
@ -111,7 +119,8 @@ public class HikariCPCollectorTest {
}
@Test
public void connectionClosed() throws Exception {
public void connectionClosed() throws Exception
{
HikariConfig config = newHikariConfig();
config.setMetricsTrackerFactory(new PrometheusMetricsTrackerFactory(this.collectorRegistry));
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
@ -135,10 +144,78 @@ public class HikariCPCollectorTest {
}
}
private double getValue(String name, String poolName) {
@Test
public void poolStatsRemovedAfterShutDown() throws Exception
{
HikariConfig config = new HikariConfig();
config.setPoolName("shutDownPool");
config.setMetricsTrackerFactory(new PrometheusMetricsTrackerFactory(this.collectorRegistry));
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
config.setMaximumPoolSize(1);
StubConnection.slowCreate = true;
try (HikariDataSource ds = new HikariDataSource(config)) {
try (Connection connection1 = ds.getConnection()) {
// close immediately
}
assertThat(getValue("hikaricp_active_connections", "shutDownPool"), is(0.0));
assertThat(getValue("hikaricp_idle_connections", "shutDownPool"), is(1.0));
assertThat(getValue("hikaricp_pending_threads", "shutDownPool"), is(0.0));
assertThat(getValue("hikaricp_connections", "shutDownPool"), is(1.0));
assertThat(getValue("hikaricp_max_connections", "shutDownPool"), is(1.0));
assertThat(getValue("hikaricp_min_connections", "shutDownPool"), is(1.0));
}
finally {
StubConnection.slowCreate = false;
}
assertNull(getValue("hikaricp_active_connections", "shutDownPool"));
assertNull(getValue("hikaricp_idle_connections", "shutDownPool"));
assertNull(getValue("hikaricp_pending_threads", "shutDownPool"));
assertNull(getValue("hikaricp_connections", "shutDownPool"));
assertNull(getValue("hikaricp_max_connections", "shutDownPool"));
assertNull(getValue("hikaricp_min_connections", "shutDownPool"));
}
@Test
public void testHikariCPCollectorGaugesMetricsInitialization()
{
HikariCPCollector hikariCPCollector = new HikariCPCollector();
hikariCPCollector.add("collectorTestPool", poolStatsWithPredefinedValues());
List<Collector.MetricFamilySamples> metrics = hikariCPCollector.collect();
hikariCPCollector.register(collectorRegistry);
assertThat(metrics.size(), is(6));
assertThat(metrics.stream().filter(metricFamilySamples -> metricFamilySamples.type == Collector.Type.GAUGE).count(), is(6L));
assertThat(getValue("hikaricp_active_connections", "collectorTestPool"), is(58.0));
assertThat(getValue("hikaricp_idle_connections", "collectorTestPool"), is(42.0));
assertThat(getValue("hikaricp_pending_threads", "collectorTestPool"), is(1.0));
assertThat(getValue("hikaricp_connections", "collectorTestPool"), is(100.0));
assertThat(getValue("hikaricp_max_connections", "collectorTestPool"), is(100.0));
assertThat(getValue("hikaricp_min_connections", "collectorTestPool"), is(3.0));
}
private Double getValue(String name, String poolName)
{
String[] labelNames = {"pool"};
String[] labelValues = {poolName};
return this.collectorRegistry.getSampleValue(name, labelNames, labelValues);
}
private PoolStats poolStatsWithPredefinedValues()
{
return new PoolStats(0) {
@Override
protected void update() {
totalConnections = 100;
idleConnections = 42;
activeConnections = 58;
pendingThreads = 1;
maxConnections = 100;
minConnections = 3;
}
};
}
}

@ -1,6 +1,7 @@
package com.zaxxer.hikari.metrics.prometheus;
import com.zaxxer.hikari.metrics.PoolStats;
import com.zaxxer.hikari.mocks.StubPoolStats;
import io.prometheus.client.Collector;
import io.prometheus.client.CollectorRegistry;
import org.junit.After;
@ -13,30 +14,35 @@ import java.util.List;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class PrometheusMetricsTrackerFactoryTest {
public class PrometheusMetricsTrackerFactoryTest
{
@After
public void clearCollectorRegistry()
{
CollectorRegistry.defaultRegistry.clear();
}
@Test
public void registersToProvidedCollectorRegistry() {
public void registersToProvidedCollectorRegistry()
{
CollectorRegistry collectorRegistry = new CollectorRegistry();
PrometheusMetricsTrackerFactory factory = new PrometheusMetricsTrackerFactory(collectorRegistry);
factory.create("testpool-1", poolStats());
factory.create("testpool-1", new StubPoolStats(0));
assertHikariMetricsAreNotPresent(CollectorRegistry.defaultRegistry);
assertHikariMetricsArePresent(collectorRegistry);
}
@Test
public void registersToDefaultCollectorRegistry() {
public void registersToDefaultCollectorRegistry()
{
PrometheusMetricsTrackerFactory factory = new PrometheusMetricsTrackerFactory();
factory.create("testpool-2", poolStats());
factory.create("testpool-2", new StubPoolStats(0));
assertHikariMetricsArePresent(CollectorRegistry.defaultRegistry);
}
@After
public void clearCollectorRegistry(){
CollectorRegistry.defaultRegistry.clear();
}
private void assertHikariMetricsArePresent(CollectorRegistry collectorRegistry) {
private void assertHikariMetricsArePresent(CollectorRegistry collectorRegistry)
{
List<String> registeredMetrics = toMetricNames(collectorRegistry.metricFamilySamples());
assertTrue(registeredMetrics.contains("hikaricp_active_connections"));
assertTrue(registeredMetrics.contains("hikaricp_idle_connections"));
@ -46,7 +52,8 @@ public class PrometheusMetricsTrackerFactoryTest {
assertTrue(registeredMetrics.contains("hikaricp_min_connections"));
}
private void assertHikariMetricsAreNotPresent(CollectorRegistry collectorRegistry) {
private void assertHikariMetricsAreNotPresent(CollectorRegistry collectorRegistry)
{
List<String> registeredMetrics = toMetricNames(collectorRegistry.metricFamilySamples());
assertFalse(registeredMetrics.contains("hikaricp_active_connections"));
assertFalse(registeredMetrics.contains("hikaricp_idle_connections"));
@ -56,21 +63,12 @@ public class PrometheusMetricsTrackerFactoryTest {
assertFalse(registeredMetrics.contains("hikaricp_min_connections"));
}
private List<String> toMetricNames(Enumeration<Collector.MetricFamilySamples> enumeration) {
private List<String> toMetricNames(Enumeration<Collector.MetricFamilySamples> enumeration)
{
List<String> list = new ArrayList<>();
while (enumeration.hasMoreElements()) {
list.add(enumeration.nextElement().name);
}
return list;
}
private PoolStats poolStats() {
return new PoolStats(0) {
@Override
protected void update() {
// do nothing
}
};
}
}

@ -12,12 +12,14 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
*/
package com.zaxxer.hikari.metrics.prometheus;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.metrics.IMetricsTracker;
import com.zaxxer.hikari.mocks.StubPoolStats;
import io.prometheus.client.CollectorRegistry;
import org.junit.Before;
import org.junit.Test;
@ -28,31 +30,37 @@ import java.sql.SQLTransientConnectionException;
import static com.zaxxer.hikari.pool.TestElf.newHikariConfig;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
public class PrometheusMetricsTrackerTest {
public class PrometheusMetricsTrackerTest
{
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};
private static final String QUANTILE_LABEL_NAME = "quantile";
private static final String[] QUANTILE_LABEL_VALUES = new String[]{"0.5", "0.95", "0.99"};
@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 {
public void recordConnectionTimeout() throws Exception
{
HikariConfig config = newHikariConfig();
config.setMetricsTrackerFactory(new PrometheusMetricsTrackerFactory(collectorRegistry));
config.setMetricsTrackerFactory(new PrometheusMetricsTrackerFactory(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)) {
@ -63,88 +71,229 @@ public class PrometheusMetricsTrackerTest {
}
}
Double total = collectorRegistry.getSampleValue(
"hikaricp_connection_timeout_total",
labelNames,
labelValues
Double total = defaultCollectorRegistry.getSampleValue(
"hikaricp_connection_timeout_total", LABEL_NAMES, labelValues
);
assertThat(total, is(1.0));
}
}
@Test
public void connectionAcquisitionMetrics() {
public void connectionAcquisitionMetrics()
{
checkSummaryMetricFamily("hikaricp_connection_acquired_nanos");
}
@Test
public void connectionUsageMetrics() {
public void connectionUsageMetrics()
{
checkSummaryMetricFamily("hikaricp_connection_usage_millis");
}
@Test
public void connectionCreationMetrics() {
public void connectionCreationMetrics()
{
checkSummaryMetricFamily("hikaricp_connection_creation_millis");
}
@Test
public void testMultiplePoolName() throws Exception {
String[] labelNames = {POOL_LABEL_NAME};
public void testMultiplePoolNameWithOneCollectorRegistry()
{
HikariConfig configFirstPool = newHikariConfig();
configFirstPool.setMetricsTrackerFactory(new PrometheusMetricsTrackerFactory(defaultCollectorRegistry));
configFirstPool.setPoolName("first");
configFirstPool.setJdbcUrl("jdbc:h2:mem:");
configFirstPool.setMaximumPoolSize(2);
configFirstPool.setConnectionTimeout(250);
HikariConfig config = newHikariConfig();
config.setMetricsTrackerFactory(new PrometheusMetricsTrackerFactory(collectorRegistry));
config.setPoolName("first");
config.setJdbcUrl("jdbc:h2:mem:");
config.setMaximumPoolSize(2);
config.setConnectionTimeout(250);
String[] labelValues1 = {config.getPoolName()};
HikariConfig configSecondPool = newHikariConfig();
configSecondPool.setMetricsTrackerFactory(new PrometheusMetricsTrackerFactory(defaultCollectorRegistry));
configSecondPool.setPoolName("second");
configSecondPool.setJdbcUrl("jdbc:h2:mem:");
configSecondPool.setMaximumPoolSize(4);
configSecondPool.setConnectionTimeout(250);
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 PrometheusMetricsTrackerFactory(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));
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));
}
}
}
@Test
public void testMultiplePoolNameWithDifferentCollectorRegistries()
{
HikariConfig configFirstPool = newHikariConfig();
configFirstPool.setMetricsTrackerFactory(new PrometheusMetricsTrackerFactory(defaultCollectorRegistry));
configFirstPool.setPoolName("first");
configFirstPool.setJdbcUrl("jdbc:h2:mem:");
configFirstPool.setMaximumPoolSize(2);
configFirstPool.setConnectionTimeout(250);
HikariConfig configSecondPool = newHikariConfig();
configSecondPool.setMetricsTrackerFactory(new PrometheusMetricsTrackerFactory(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));
}
}
}
@Test
public void testMetricsRemovedAfterShutDown()
{
HikariConfig configFirstPool = newHikariConfig();
configFirstPool.setMetricsTrackerFactory(new PrometheusMetricsTrackerFactory(defaultCollectorRegistry));
configFirstPool.setPoolName("first");
configFirstPool.setJdbcUrl("jdbc:h2:mem:");
configFirstPool.setMaximumPoolSize(2);
configFirstPool.setConnectionTimeout(250);
HikariConfig configSecondPool = newHikariConfig();
configSecondPool.setMetricsTrackerFactory(new PrometheusMetricsTrackerFactory(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));
}
assertNull(defaultCollectorRegistry.getSampleValue(
"hikaricp_connection_timeout_total", LABEL_NAMES, labelValuesSecondPool));
assertThat(defaultCollectorRegistry.getSampleValue(
"hikaricp_connection_timeout_total", LABEL_NAMES, labelValuesFirstPool),
is(0.0));
}
}
private void checkSummaryMetricFamily(String metricName) {
@Test
public void testCloseMethod()
{
String[] labelValues = {"testPool"};
PrometheusMetricsTrackerFactory prometheusFactory = new PrometheusMetricsTrackerFactory(defaultCollectorRegistry);
IMetricsTracker prometheusTracker = prometheusFactory.create("testPool", new StubPoolStats(0));
prometheusTracker.recordConnectionTimeout();
prometheusTracker.recordConnectionAcquiredNanos(42L);
prometheusTracker.recordConnectionUsageMillis(111L);
prometheusTracker.recordConnectionCreatedMillis(101L);
assertThat(defaultCollectorRegistry.getSampleValue(
"hikaricp_connection_timeout_total", LABEL_NAMES, labelValues),
is(1.0));
assertThat(defaultCollectorRegistry.getSampleValue(
"hikaricp_connection_acquired_nanos_sum", LABEL_NAMES, labelValues),
is(42.0));
assertThat(defaultCollectorRegistry.getSampleValue(
"hikaricp_connection_usage_millis_sum", LABEL_NAMES, labelValues),
is(111.0));
assertThat(defaultCollectorRegistry.getSampleValue(
"hikaricp_connection_creation_millis_sum", LABEL_NAMES, labelValues),
is(101.0));
assertThat(defaultCollectorRegistry.getSampleValue(
"hikaricp_active_connections", LABEL_NAMES, labelValues),
is(0.0));
assertThat(defaultCollectorRegistry.getSampleValue(
"hikaricp_idle_connections", LABEL_NAMES, labelValues),
is(0.0));
assertThat(defaultCollectorRegistry.getSampleValue(
"hikaricp_pending_threads", LABEL_NAMES, labelValues),
is(0.0));
assertThat(defaultCollectorRegistry.getSampleValue(
"hikaricp_connections", LABEL_NAMES, labelValues),
is(0.0));
assertThat(defaultCollectorRegistry.getSampleValue(
"hikaricp_max_connections", LABEL_NAMES, labelValues),
is(0.0));
assertThat(defaultCollectorRegistry.getSampleValue(
"hikaricp_min_connections", LABEL_NAMES, labelValues),
is(0.0));
prometheusTracker.close();
assertNull(defaultCollectorRegistry.getSampleValue(
"hikaricp_connection_timeout_total", LABEL_NAMES, labelValues));
assertNull(defaultCollectorRegistry.getSampleValue(
"hikaricp_connection_acquired_nanos_sum", LABEL_NAMES, labelValues));
assertNull(defaultCollectorRegistry.getSampleValue(
"hikaricp_connection_usage_millis_sum", LABEL_NAMES, labelValues));
assertNull(defaultCollectorRegistry.getSampleValue(
"hikaricp_connection_creation_millis_sum", LABEL_NAMES, labelValues));
assertNull(defaultCollectorRegistry.getSampleValue(
"hikaricp_active_connections", LABEL_NAMES, labelValues));
assertNull(defaultCollectorRegistry.getSampleValue(
"hikaricp_idle_connections", LABEL_NAMES, labelValues));
assertNull(defaultCollectorRegistry.getSampleValue(
"hikaricp_pending_threads", LABEL_NAMES, labelValues));
assertNull(defaultCollectorRegistry.getSampleValue(
"hikaricp_connections", LABEL_NAMES, labelValues));
assertNull(defaultCollectorRegistry.getSampleValue(
"hikaricp_connections", LABEL_NAMES, labelValues));
assertNull(defaultCollectorRegistry.getSampleValue(
"hikaricp_max_connections", LABEL_NAMES, labelValues));
assertNull(defaultCollectorRegistry.getSampleValue(
"hikaricp_min_connections", LABEL_NAMES, labelValues));
}
private void checkSummaryMetricFamily(String metricName)
{
HikariConfig config = newHikariConfig();
config.setMetricsTrackerFactory(new PrometheusMetricsTrackerFactory(collectorRegistry));
config.setMetricsTrackerFactory(new PrometheusMetricsTrackerFactory(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);
for (String quantileLabelValue : QUANTILE_LABEL_VALUES) {
Double quantileValue = collectorRegistry.getSampleValue(
Double quantileValue = defaultCollectorRegistry.getSampleValue(
metricName,
new String[]{POOL_LABEL_NAME, QUANTILE_LABEL_NAME},
new String[]{config.getPoolName(), quantileLabelValue}

@ -0,0 +1,20 @@
package com.zaxxer.hikari.mocks;
import com.zaxxer.hikari.metrics.PoolStats;
public class StubPoolStats extends PoolStats
{
public StubPoolStats(long timeoutMs)
{
super(timeoutMs);
}
@Override
protected void update()
{
// Do nothing
}
}
Loading…
Cancel
Save