Merge branch 'wholesail-dropwizard5-metrics' into dev

pull/2238/head
Brett Wooldridge 3 months ago
commit f6efe91bd1
No known key found for this signature in database
GPG Key ID: 4CC08E7F47C3EC76

@ -4,6 +4,8 @@ Changes in 6.0.1
* change default maxLifetime variance from 2.5% to 25% to further avoid mass connection die-off dips
* Dropwizard 5 metrics are now supported via the setMetricRegistry() method in HikariConfig and in HikariDataSource
Changes in 6.0.0
* fixed #2152 duplicate connection in try with resources clause caused close() being called twice on each connection

@ -31,12 +31,13 @@
<docker.maven.plugin.fabric8.version>0.45.0</docker.maven.plugin.fabric8.version>
<felix.bundle.plugin.version>5.1.1</felix.bundle.plugin.version>
<felix.version>6.0.1</felix.version>
<felix.version>7.0.5</felix.version>
<hibernate.version>5.4.24.Final</hibernate.version>
<javassist.version>3.29.2-GA</javassist.version>
<jndi.version>0.11.4.1</jndi.version>
<maven.release.version>3.0.1</maven.release.version>
<metrics.version>3.2.5</metrics.version>
<metrics5.version>5.0.0-rc17</metrics5.version>
<micrometer.version>1.5.10</micrometer.version>
<simpleclient.version>0.16.0</simpleclient.version>
<mockito.version>3.7.7</mockito.version>
@ -195,6 +196,13 @@
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.dropwizard.metrics5</groupId>
<artifactId>metrics-core</artifactId>
<version>${metrics5.version}</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient</artifactId>
@ -581,6 +589,7 @@
javax.sql.rowset.spi,
com.codahale.metrics;resolution:=optional,
com.codahale.metrics.health;resolution:=optional,
io.dropwizard.metrics5;resolution:=optional,
io.micrometer.core.instrument;resolution:=optional,
org.slf4j;version="[1.6,2)",
org.hibernate;resolution:=optional,

@ -693,8 +693,10 @@ public class HikariConfig implements HikariConfigMXBean
metricRegistry = getObjectOrPerformJndiLookup(metricRegistry);
if (!safeIsAssignableFrom(metricRegistry, "com.codahale.metrics.MetricRegistry")
&& !(safeIsAssignableFrom(metricRegistry, "io.dropwizard.metrics5.MetricRegistry"))
&& !(safeIsAssignableFrom(metricRegistry, "io.micrometer.core.instrument.MeterRegistry"))) {
throw new IllegalArgumentException("Class must be instance of com.codahale.metrics.MetricRegistry or io.micrometer.core.instrument.MeterRegistry");
throw new IllegalArgumentException("Class must be instance of com.codahale.metrics.MetricRegistry, " +
"io.dropwizard.metrics5.MetricRegistry, or io.micrometer.core.instrument.MeterRegistry");
}
}

@ -26,6 +26,18 @@ import com.codahale.metrics.Timer;
import com.zaxxer.hikari.metrics.IMetricsTracker;
import com.zaxxer.hikari.metrics.PoolStats;
import static com.zaxxer.hikari.metrics.dropwizard.DropwizardCommon.METRIC_CATEGORY;
import static com.zaxxer.hikari.metrics.dropwizard.DropwizardCommon.METRIC_NAME_ACTIVE_CONNECTIONS;
import static com.zaxxer.hikari.metrics.dropwizard.DropwizardCommon.METRIC_NAME_CONNECT;
import static com.zaxxer.hikari.metrics.dropwizard.DropwizardCommon.METRIC_NAME_IDLE_CONNECTIONS;
import static com.zaxxer.hikari.metrics.dropwizard.DropwizardCommon.METRIC_NAME_MAX_CONNECTIONS;
import static com.zaxxer.hikari.metrics.dropwizard.DropwizardCommon.METRIC_NAME_MIN_CONNECTIONS;
import static com.zaxxer.hikari.metrics.dropwizard.DropwizardCommon.METRIC_NAME_PENDING_CONNECTIONS;
import static com.zaxxer.hikari.metrics.dropwizard.DropwizardCommon.METRIC_NAME_TIMEOUT_RATE;
import static com.zaxxer.hikari.metrics.dropwizard.DropwizardCommon.METRIC_NAME_TOTAL_CONNECTIONS;
import static com.zaxxer.hikari.metrics.dropwizard.DropwizardCommon.METRIC_NAME_USAGE;
import static com.zaxxer.hikari.metrics.dropwizard.DropwizardCommon.METRIC_NAME_WAIT;
public final class CodaHaleMetricsTracker implements IMetricsTracker
{
private final String poolName;
@ -35,18 +47,6 @@ public final class CodaHaleMetricsTracker implements IMetricsTracker
private final Meter connectionTimeoutMeter;
private final MetricRegistry registry;
private static final String METRIC_CATEGORY = "pool";
private static final String METRIC_NAME_WAIT = "Wait";
private static final String METRIC_NAME_USAGE = "Usage";
private static final String METRIC_NAME_CONNECT = "ConnectionCreation";
private static final String METRIC_NAME_TIMEOUT_RATE = "ConnectionTimeoutRate";
private static final String METRIC_NAME_TOTAL_CONNECTIONS = "TotalConnections";
private static final String METRIC_NAME_IDLE_CONNECTIONS = "IdleConnections";
private static final String METRIC_NAME_ACTIVE_CONNECTIONS = "ActiveConnections";
private static final String METRIC_NAME_PENDING_CONNECTIONS = "PendingConnections";
private static final String METRIC_NAME_MAX_CONNECTIONS = "MaxConnections";
private static final String METRIC_NAME_MIN_CONNECTIONS = "MinConnections";
CodaHaleMetricsTracker(final String poolName, final PoolStats poolStats, final MetricRegistry registry)
{
this.poolName = poolName;

@ -0,0 +1,118 @@
package com.zaxxer.hikari.metrics.dropwizard;
import java.util.concurrent.TimeUnit;
import com.zaxxer.hikari.metrics.IMetricsTracker;
import com.zaxxer.hikari.metrics.PoolStats;
import io.dropwizard.metrics5.Gauge;
import io.dropwizard.metrics5.Histogram;
import io.dropwizard.metrics5.Meter;
import io.dropwizard.metrics5.MetricRegistry;
import io.dropwizard.metrics5.Timer;
import static com.zaxxer.hikari.metrics.dropwizard.DropwizardCommon.METRIC_CATEGORY;
import static com.zaxxer.hikari.metrics.dropwizard.DropwizardCommon.METRIC_NAME_ACTIVE_CONNECTIONS;
import static com.zaxxer.hikari.metrics.dropwizard.DropwizardCommon.METRIC_NAME_CONNECT;
import static com.zaxxer.hikari.metrics.dropwizard.DropwizardCommon.METRIC_NAME_IDLE_CONNECTIONS;
import static com.zaxxer.hikari.metrics.dropwizard.DropwizardCommon.METRIC_NAME_MAX_CONNECTIONS;
import static com.zaxxer.hikari.metrics.dropwizard.DropwizardCommon.METRIC_NAME_MIN_CONNECTIONS;
import static com.zaxxer.hikari.metrics.dropwizard.DropwizardCommon.METRIC_NAME_PENDING_CONNECTIONS;
import static com.zaxxer.hikari.metrics.dropwizard.DropwizardCommon.METRIC_NAME_TIMEOUT_RATE;
import static com.zaxxer.hikari.metrics.dropwizard.DropwizardCommon.METRIC_NAME_TOTAL_CONNECTIONS;
import static com.zaxxer.hikari.metrics.dropwizard.DropwizardCommon.METRIC_NAME_USAGE;
import static com.zaxxer.hikari.metrics.dropwizard.DropwizardCommon.METRIC_NAME_WAIT;
public class Dropwizard5MetricsTracker implements IMetricsTracker
{
private final String poolName;
private final Timer connectionObtainTimer;
private final Histogram connectionUsage;
private final Histogram connectionCreation;
private final Meter connectionTimeoutMeter;
private final MetricRegistry registry;
Dropwizard5MetricsTracker(final String poolName, final PoolStats poolStats, final MetricRegistry registry)
{
this.poolName = poolName;
this.registry = registry;
this.connectionObtainTimer = registry.timer(MetricRegistry.name(poolName, METRIC_CATEGORY, METRIC_NAME_WAIT));
this.connectionUsage = registry.histogram(MetricRegistry.name(poolName, METRIC_CATEGORY, METRIC_NAME_USAGE));
this.connectionCreation = registry.histogram(MetricRegistry.name(poolName, METRIC_CATEGORY, METRIC_NAME_CONNECT));
this.connectionTimeoutMeter = registry.meter(MetricRegistry.name(poolName, METRIC_CATEGORY, METRIC_NAME_TIMEOUT_RATE));
registry.register(MetricRegistry.name(poolName, METRIC_CATEGORY, METRIC_NAME_TOTAL_CONNECTIONS),
(Gauge<Integer>) poolStats::getTotalConnections);
registry.register(MetricRegistry.name(poolName, METRIC_CATEGORY, METRIC_NAME_IDLE_CONNECTIONS),
(Gauge<Integer>) poolStats::getIdleConnections);
registry.register(MetricRegistry.name(poolName, METRIC_CATEGORY, METRIC_NAME_ACTIVE_CONNECTIONS),
(Gauge<Integer>) poolStats::getActiveConnections);
registry.register(MetricRegistry.name(poolName, METRIC_CATEGORY, METRIC_NAME_PENDING_CONNECTIONS),
(Gauge<Integer>) poolStats::getPendingThreads);
registry.register(MetricRegistry.name(poolName, METRIC_CATEGORY, METRIC_NAME_MAX_CONNECTIONS),
(Gauge<Integer>) poolStats::getMaxConnections);
registry.register(MetricRegistry.name(poolName, METRIC_CATEGORY, METRIC_NAME_MIN_CONNECTIONS),
(Gauge<Integer>) poolStats::getMinConnections);
}
/** {@inheritDoc} */
@Override
public void close()
{
registry.remove(MetricRegistry.name(poolName, METRIC_CATEGORY, METRIC_NAME_WAIT));
registry.remove(MetricRegistry.name(poolName, METRIC_CATEGORY, METRIC_NAME_USAGE));
registry.remove(MetricRegistry.name(poolName, METRIC_CATEGORY, METRIC_NAME_CONNECT));
registry.remove(MetricRegistry.name(poolName, METRIC_CATEGORY, METRIC_NAME_TIMEOUT_RATE));
registry.remove(MetricRegistry.name(poolName, METRIC_CATEGORY, METRIC_NAME_TOTAL_CONNECTIONS));
registry.remove(MetricRegistry.name(poolName, METRIC_CATEGORY, METRIC_NAME_IDLE_CONNECTIONS));
registry.remove(MetricRegistry.name(poolName, METRIC_CATEGORY, METRIC_NAME_ACTIVE_CONNECTIONS));
registry.remove(MetricRegistry.name(poolName, METRIC_CATEGORY, METRIC_NAME_PENDING_CONNECTIONS));
registry.remove(MetricRegistry.name(poolName, METRIC_CATEGORY, METRIC_NAME_MAX_CONNECTIONS));
registry.remove(MetricRegistry.name(poolName, METRIC_CATEGORY, METRIC_NAME_MIN_CONNECTIONS));
}
/** {@inheritDoc} */
@Override
public void recordConnectionAcquiredNanos(final long elapsedAcquiredNanos)
{
connectionObtainTimer.update(elapsedAcquiredNanos, TimeUnit.NANOSECONDS);
}
/** {@inheritDoc} */
@Override
public void recordConnectionUsageMillis(final long elapsedBorrowedMillis)
{
connectionUsage.update(elapsedBorrowedMillis);
}
@Override
public void recordConnectionTimeout()
{
connectionTimeoutMeter.mark();
}
@Override
public void recordConnectionCreatedMillis(final long connectionCreatedMillis)
{
connectionCreation.update(connectionCreatedMillis);
}
public Timer getConnectionAcquisitionTimer()
{
return connectionObtainTimer;
}
public Histogram getConnectionDurationHistogram()
{
return connectionUsage;
}
public Histogram getConnectionCreationHistogram()
{
return connectionCreation;
}
}

@ -0,0 +1,27 @@
package com.zaxxer.hikari.metrics.dropwizard;
import com.zaxxer.hikari.metrics.IMetricsTracker;
import com.zaxxer.hikari.metrics.MetricsTrackerFactory;
import com.zaxxer.hikari.metrics.PoolStats;
import io.dropwizard.metrics5.MetricRegistry;
public class Dropwizard5MetricsTrackerFactory implements MetricsTrackerFactory
{
private final MetricRegistry registry;
public Dropwizard5MetricsTrackerFactory(final MetricRegistry registry)
{
this.registry = registry;
}
public MetricRegistry getRegistry()
{
return registry;
}
@Override
public IMetricsTracker create(final String poolName, final PoolStats poolStats)
{
return new Dropwizard5MetricsTracker(poolName, poolStats, registry);
}
}

@ -0,0 +1,33 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* 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.dropwizard;
final class DropwizardCommon {
private DropwizardCommon()
{
}
static final String METRIC_CATEGORY = "pool";
static final String METRIC_NAME_WAIT = "Wait";
static final String METRIC_NAME_USAGE = "Usage";
static final String METRIC_NAME_CONNECT = "ConnectionCreation";
static final String METRIC_NAME_TIMEOUT_RATE = "ConnectionTimeoutRate";
static final String METRIC_NAME_TOTAL_CONNECTIONS = "TotalConnections";
static final String METRIC_NAME_IDLE_CONNECTIONS = "IdleConnections";
static final String METRIC_NAME_ACTIVE_CONNECTIONS = "ActiveConnections";
static final String METRIC_NAME_PENDING_CONNECTIONS = "PendingConnections";
static final String METRIC_NAME_MAX_CONNECTIONS = "MaxConnections";
static final String METRIC_NAME_MIN_CONNECTIONS = "MinConnections";
}

@ -24,6 +24,7 @@ import com.zaxxer.hikari.metrics.MetricsTrackerFactory;
import com.zaxxer.hikari.metrics.PoolStats;
import com.zaxxer.hikari.metrics.dropwizard.CodahaleHealthChecker;
import com.zaxxer.hikari.metrics.dropwizard.CodahaleMetricsTrackerFactory;
import com.zaxxer.hikari.metrics.dropwizard.Dropwizard5MetricsTrackerFactory;
import com.zaxxer.hikari.metrics.micrometer.MicrometerMetricsTrackerFactory;
import com.zaxxer.hikari.util.ConcurrentBag;
import com.zaxxer.hikari.util.ConcurrentBag.IBagStateListener;
@ -288,6 +289,9 @@ public final class HikariPool extends PoolBase implements HikariPoolMXBean, IBag
if (metricRegistry != null && safeIsAssignableFrom(metricRegistry, "com.codahale.metrics.MetricRegistry")) {
setMetricsTrackerFactory(new CodahaleMetricsTrackerFactory((MetricRegistry) metricRegistry));
}
else if (metricRegistry != null && safeIsAssignableFrom(metricRegistry, "io.dropwizard.metrics5.MetricRegistry")) {
setMetricsTrackerFactory(new Dropwizard5MetricsTrackerFactory((io.dropwizard.metrics5.MetricRegistry) metricRegistry));
}
else if (metricRegistry != null && safeIsAssignableFrom(metricRegistry, "io.micrometer.core.instrument.MeterRegistry")) {
setMetricsTrackerFactory(new MicrometerMetricsTrackerFactory((MeterRegistry) metricRegistry));
}

@ -8,6 +8,7 @@ module com.zaxxer.hikari
requires static simpleclient;
requires static metrics.core;
requires static metrics.healthchecks;
requires static io.dropwizard.metrics5;
requires static micrometer.core;
requires static org.javassist;

@ -0,0 +1,43 @@
package com.zaxxer.hikari.metrics.dropwizard;
import com.zaxxer.hikari.mocks.StubPoolStats;
import io.dropwizard.metrics5.MetricRegistry;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import static org.mockito.Mockito.verify;
@RunWith(MockitoJUnitRunner.class)
public class Dropwizard5MetricsTrackerTest
{
@Mock
public MetricRegistry mockMetricRegistry;
private Dropwizard5MetricsTracker testee;
@Before
public void setup()
{
testee = new Dropwizard5MetricsTracker("mypool", new StubPoolStats(0), mockMetricRegistry);
}
@Test
public void close()
{
testee.close();
verify(mockMetricRegistry).remove(MetricRegistry.name("mypool.pool.Wait"));
verify(mockMetricRegistry).remove(MetricRegistry.name("mypool.pool.Usage"));
verify(mockMetricRegistry).remove(MetricRegistry.name("mypool.pool.ConnectionCreation"));
verify(mockMetricRegistry).remove(MetricRegistry.name("mypool.pool.ConnectionTimeoutRate"));
verify(mockMetricRegistry).remove(MetricRegistry.name("mypool.pool.TotalConnections"));
verify(mockMetricRegistry).remove(MetricRegistry.name("mypool.pool.IdleConnections"));
verify(mockMetricRegistry).remove(MetricRegistry.name("mypool.pool.ActiveConnections"));
verify(mockMetricRegistry).remove(MetricRegistry.name("mypool.pool.PendingConnections"));
verify(mockMetricRegistry).remove(MetricRegistry.name("mypool.pool.MaxConnections"));
verify(mockMetricRegistry).remove(MetricRegistry.name("mypool.pool.MinConnections"));
}
}

@ -0,0 +1,242 @@
/*
* Copyright (C) 2013, 2014 Brett Wooldridge
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* 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.pool;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.SortedMap;
import java.util.concurrent.TimeUnit;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.Metric;
import com.codahale.metrics.MetricFilter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import com.codahale.metrics.health.HealthCheck;
import com.codahale.metrics.health.HealthCheckRegistry;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.metrics.MetricsTrackerFactory;
import com.zaxxer.hikari.metrics.dropwizard.CodahaleMetricsTrackerFactory;
import com.zaxxer.hikari.util.UtilityElf;
import org.junit.Test;
import static com.zaxxer.hikari.pool.TestElf.newHikariConfig;
import static com.zaxxer.hikari.pool.TestElf.newHikariDataSource;
import static com.zaxxer.hikari.util.UtilityElf.quietlySleep;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeFalse;
/**
* Test HikariCP/CodaHale metrics integration.
*
* @author Brett Wooldridge
*/
public class CodahaleMetricsTest extends TestMetricsBase<MetricRegistry>
{
@Override
protected MetricsTrackerFactory metricsTrackerFactory(final MetricRegistry metricRegistry)
{
return new CodahaleMetricsTrackerFactory(metricRegistry);
}
@Override
protected MetricRegistry metricRegistry()
{
return new MetricRegistry();
}
@Test
public void testHealthChecks() throws Exception
{
MetricRegistry metricRegistry = metricRegistry();
HealthCheckRegistry healthRegistry = new HealthCheckRegistry();
HikariConfig config = newHikariConfig();
config.setMaximumPoolSize(10);
config.setMetricRegistry(metricRegistry);
config.setHealthCheckRegistry(healthRegistry);
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
config.addHealthCheckProperty("connectivityCheckTimeoutMs", "1000");
config.addHealthCheckProperty("expected99thPercentileMs", "100");
try (HikariDataSource ds = new HikariDataSource(config)) {
quietlySleep(TimeUnit.SECONDS.toMillis(2));
try (Connection connection = ds.getConnection()) {
// close immediately
}
try (Connection connection = ds.getConnection()) {
// close immediately
}
SortedMap<String, HealthCheck.Result> healthChecks = healthRegistry.runHealthChecks();
HealthCheck.Result connectivityResult = healthChecks.get("testHealthChecks.pool.ConnectivityCheck");
assertTrue(connectivityResult.isHealthy());
HealthCheck.Result slaResult = healthChecks.get("testHealthChecks.pool.Connection99Percent");
assertTrue(slaResult.isHealthy());
}
}
@Test
public void testSetters1() throws Exception
{
try (HikariDataSource ds = newHikariDataSource()) {
ds.setMaximumPoolSize(1);
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
MetricRegistry metricRegistry = metricRegistry();
HealthCheckRegistry healthRegistry = new HealthCheckRegistry();
try {
try (Connection connection = ds.getConnection()) {
// close immediately
}
// After the pool as started, we can only set them once...
ds.setMetricRegistry(metricRegistry);
ds.setHealthCheckRegistry(healthRegistry);
// and never again...
ds.setMetricRegistry(metricRegistry);
fail("Should not have been allowed to set registry after pool started");
}
catch (IllegalStateException ise) {
// pass
try {
ds.setHealthCheckRegistry(healthRegistry);
fail("Should not have been allowed to set registry after pool started");
}
catch (IllegalStateException ise2) {
// pass
}
}
}
}
@Test
public void testSetters2() throws Exception
{
try (HikariDataSource ds = newHikariDataSource()) {
ds.setMaximumPoolSize(1);
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
MetricRegistry metricRegistry = metricRegistry();
HealthCheckRegistry healthRegistry = new HealthCheckRegistry();
ds.setMetricRegistry(metricRegistry);
ds.setHealthCheckRegistry(healthRegistry);
// before the pool is started, we can set it any number of times...
ds.setMetricRegistry(metricRegistry);
ds.setHealthCheckRegistry(healthRegistry);
try (Connection connection = ds.getConnection()) {
// after the pool is started, we cannot set it any more
ds.setMetricRegistry(metricRegistry);
fail("Should not have been allowed to set registry after pool started");
}
catch (IllegalStateException ise) {
// pass
}
}
}
@Test
public void testMetricWait() throws SQLException
{
MetricRegistry metricRegistry = new MetricRegistry();
HikariConfig config = newHikariConfig();
config.setMinimumIdle(1);
config.setMaximumPoolSize(1);
config.setMetricRegistry(metricRegistry);
config.setInitializationFailTimeout(Long.MAX_VALUE);
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
try (HikariDataSource ds = new HikariDataSource(config)) {
ds.getConnection().close();
Timer timer = metricRegistry.getTimers(new MetricFilter() {
/** {@inheritDoc} */
@Override
public boolean matches(String name, Metric metric)
{
return name.equals(MetricRegistry.name("testMetricWait", "pool", "Wait"));
}
}).values().iterator().next();
assertEquals(1, timer.getCount());
assertTrue(timer.getMeanRate() > 0.0);
}
}
@Test
public void testMetricUsage() throws SQLException
{
assumeFalse(System.getProperty("os.name").contains("Windows"));
MetricRegistry metricRegistry = new MetricRegistry();
HikariConfig config = newHikariConfig();
config.setMinimumIdle(1);
config.setMaximumPoolSize(1);
config.setMetricRegistry(metricRegistry);
config.setInitializationFailTimeout(0);
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
try (HikariDataSource ds = new HikariDataSource(config)) {
try (Connection connection = ds.getConnection()) {
UtilityElf.quietlySleep(250L);
}
Histogram histo = metricRegistry.getHistograms(new MetricFilter() {
/** {@inheritDoc} */
@Override
public boolean matches(String name, Metric metric)
{
return name.equals(MetricRegistry.name("testMetricUsage", "pool", "Usage"));
}
}).values().iterator().next();
assertEquals(1, histo.getCount());
double seventyFifth = histo.getSnapshot().get75thPercentile();
assertTrue("Seventy-fith percentile less than 250ms: " + seventyFifth, seventyFifth >= 250.0);
}
}
@Test
public void testMetricRegistrySubclassIsAllowed()
{
try (HikariDataSource ds = newHikariDataSource()) {
ds.setMaximumPoolSize(1);
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
ds.setMetricRegistry(new MetricRegistry() {
@Override
public Timer timer(String name) {
return super.timer(name);
}
});
}
}
}

@ -0,0 +1,174 @@
package com.zaxxer.hikari.pool;
import java.sql.Connection;
import java.sql.SQLException;
import com.codahale.metrics.health.HealthCheckRegistry;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.metrics.MetricsTrackerFactory;
import com.zaxxer.hikari.metrics.dropwizard.Dropwizard5MetricsTrackerFactory;
import com.zaxxer.hikari.util.UtilityElf;
import io.dropwizard.metrics5.Histogram;
import io.dropwizard.metrics5.Metric;
import io.dropwizard.metrics5.MetricFilter;
import io.dropwizard.metrics5.MetricName;
import io.dropwizard.metrics5.MetricRegistry;
import io.dropwizard.metrics5.Timer;
import org.junit.Test;
import static com.zaxxer.hikari.pool.TestElf.newHikariConfig;
import static com.zaxxer.hikari.pool.TestElf.newHikariDataSource;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeFalse;
/**
* Test HikariCP/Dropwizard 5 metrics integration.
*/
public class Dropwizard5MetricsTest extends TestMetricsBase<MetricRegistry>
{
@Override
protected MetricsTrackerFactory metricsTrackerFactory(final MetricRegistry metricRegistry)
{
return new Dropwizard5MetricsTrackerFactory(metricRegistry);
}
@Override
protected MetricRegistry metricRegistry()
{
return new MetricRegistry();
}
@Test
public void testSetters1() throws Exception
{
try (HikariDataSource ds = newHikariDataSource()) {
ds.setMaximumPoolSize(1);
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
MetricRegistry metricRegistry = metricRegistry();
try {
try (Connection connection = ds.getConnection()) {
// close immediately
}
// After the pool as started, we can only set it once...
ds.setMetricRegistry(metricRegistry);
// and never again...
ds.setMetricRegistry(metricRegistry);
fail("Should not have been allowed to set registry after pool started");
}
catch (IllegalStateException ise) {
// pass
}
}
}
@Test
public void testSetters2() throws Exception
{
try (HikariDataSource ds = newHikariDataSource()) {
ds.setMaximumPoolSize(1);
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
MetricRegistry metricRegistry = metricRegistry();
ds.setMetricRegistry(metricRegistry);
// before the pool is started, we can set it any number of times...
ds.setMetricRegistry(metricRegistry);
try (Connection connection = ds.getConnection()) {
// after the pool is started, we cannot set it any more
ds.setMetricRegistry(metricRegistry);
fail("Should not have been allowed to set registry after pool started");
}
catch (IllegalStateException ise) {
// pass
}
}
}
@Test
public void testMetricWait() throws SQLException
{
MetricRegistry metricRegistry = new MetricRegistry();
HikariConfig config = newHikariConfig();
config.setMinimumIdle(1);
config.setMaximumPoolSize(1);
config.setMetricRegistry(metricRegistry);
config.setInitializationFailTimeout(Long.MAX_VALUE);
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
try (HikariDataSource ds = new HikariDataSource(config)) {
ds.getConnection().close();
Timer timer = metricRegistry.getTimers(new MetricFilter() {
/** {@inheritDoc} */
@Override
public boolean matches(MetricName name, Metric metric)
{
return name.equals(MetricRegistry.name("testMetricWait", "pool", "Wait"));
}
}).values().iterator().next();
assertEquals(1, timer.getCount());
assertTrue(timer.getMeanRate() > 0.0);
}
}
@Test
public void testMetricUsage() throws SQLException
{
assumeFalse(System.getProperty("os.name").contains("Windows"));
MetricRegistry metricRegistry = new MetricRegistry();
HikariConfig config = newHikariConfig();
config.setMinimumIdle(1);
config.setMaximumPoolSize(1);
config.setMetricRegistry(metricRegistry);
config.setInitializationFailTimeout(0);
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
try (HikariDataSource ds = new HikariDataSource(config)) {
try (Connection connection = ds.getConnection()) {
UtilityElf.quietlySleep(250L);
}
Histogram histo = metricRegistry.getHistograms(new MetricFilter() {
/** {@inheritDoc} */
@Override
public boolean matches(MetricName name, Metric metric)
{
return name.equals(MetricRegistry.name("testMetricUsage", "pool", "Usage"));
}
}).values().iterator().next();
assertEquals(1, histo.getCount());
double seventyFifth = histo.getSnapshot().get75thPercentile();
assertTrue("Seventy-fith percentile less than 250ms: " + seventyFifth, seventyFifth >= 250.0);
}
}
@Test
public void testMetricRegistrySubclassIsAllowed()
{
try (HikariDataSource ds = newHikariDataSource()) {
ds.setMaximumPoolSize(1);
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
ds.setMetricRegistry(new MetricRegistry() {
@Override
public Timer timer(String name) {
return super.timer(name);
}
});
}
}
}

@ -16,289 +16,18 @@
package com.zaxxer.hikari.pool;
import static com.zaxxer.hikari.pool.TestElf.newHikariConfig;
import static com.zaxxer.hikari.pool.TestElf.newHikariDataSource;
import static com.zaxxer.hikari.util.UtilityElf.quietlySleep;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeFalse;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.SortedMap;
import java.util.concurrent.TimeUnit;
import com.zaxxer.hikari.HikariDataSource;
import org.junit.Test;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.Metric;
import com.codahale.metrics.MetricFilter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import com.codahale.metrics.health.HealthCheck.Result;
import com.codahale.metrics.health.HealthCheckRegistry;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.metrics.MetricsTrackerFactory;
import com.zaxxer.hikari.metrics.dropwizard.CodahaleMetricsTrackerFactory;
import com.zaxxer.hikari.util.UtilityElf;
import static com.zaxxer.hikari.pool.TestElf.newHikariDataSource;
/**
* Test HikariCP/CodaHale metrics integration.
* Test HikariCP metrics integration.
*
* @author Brett Wooldridge
*/
public class TestMetrics
{
@Test
public void testMetricWait() throws SQLException
{
MetricRegistry metricRegistry = new MetricRegistry();
HikariConfig config = newHikariConfig();
config.setMinimumIdle(1);
config.setMaximumPoolSize(1);
config.setMetricRegistry(metricRegistry);
config.setInitializationFailTimeout(Long.MAX_VALUE);
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
try (HikariDataSource ds = new HikariDataSource(config)) {
ds.getConnection().close();
Timer timer = metricRegistry.getTimers(new MetricFilter() {
/** {@inheritDoc} */
@Override
public boolean matches(String name, Metric metric)
{
return "testMetricWait.pool.Wait".equals(MetricRegistry.name("testMetricWait", "pool", "Wait"));
}
}).values().iterator().next();
assertEquals(1, timer.getCount());
assertTrue(timer.getMeanRate() > 0.0);
}
}
@Test
public void testMetricUsage() throws SQLException
{
assumeFalse(System.getProperty("os.name").contains("Windows"));
MetricRegistry metricRegistry = new MetricRegistry();
HikariConfig config = newHikariConfig();
config.setMinimumIdle(1);
config.setMaximumPoolSize(1);
config.setMetricRegistry(metricRegistry);
config.setInitializationFailTimeout(0);
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
try (HikariDataSource ds = new HikariDataSource(config)) {
try (Connection connection = ds.getConnection()) {
UtilityElf.quietlySleep(250L);
}
Histogram histo = metricRegistry.getHistograms(new MetricFilter() {
/** {@inheritDoc} */
@Override
public boolean matches(String name, Metric metric)
{
return name.equals(MetricRegistry.name("testMetricUsage", "pool", "Usage"));
}
}).values().iterator().next();
assertEquals(1, histo.getCount());
double seventyFifth = histo.getSnapshot().get75thPercentile();
assertTrue("Seventy-fith percentile less than 250ms: " + seventyFifth, seventyFifth >= 250.0);
}
}
@Test
public void testHealthChecks() throws Exception
{
MetricRegistry metricRegistry = new MetricRegistry();
HealthCheckRegistry healthRegistry = new HealthCheckRegistry();
HikariConfig config = newHikariConfig();
config.setMaximumPoolSize(10);
config.setMetricRegistry(metricRegistry);
config.setHealthCheckRegistry(healthRegistry);
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
config.addHealthCheckProperty("connectivityCheckTimeoutMs", "1000");
config.addHealthCheckProperty("expected99thPercentileMs", "100");
try (HikariDataSource ds = new HikariDataSource(config)) {
quietlySleep(TimeUnit.SECONDS.toMillis(2));
try (Connection connection = ds.getConnection()) {
// close immediately
}
try (Connection connection = ds.getConnection()) {
// close immediately
}
SortedMap<String, Result> healthChecks = healthRegistry.runHealthChecks();
Result connectivityResult = healthChecks.get("testHealthChecks.pool.ConnectivityCheck");
assertTrue(connectivityResult.isHealthy());
Result slaResult = healthChecks.get("testHealthChecks.pool.Connection99Percent");
assertTrue(slaResult.isHealthy());
}
}
@Test
public void testSetters1() throws Exception
{
try (HikariDataSource ds = newHikariDataSource()) {
ds.setMaximumPoolSize(1);
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
MetricRegistry metricRegistry = new MetricRegistry();
HealthCheckRegistry healthRegistry = new HealthCheckRegistry();
try {
try (Connection connection = ds.getConnection()) {
// close immediately
}
// After the pool as started, we can only set them once...
ds.setMetricRegistry(metricRegistry);
ds.setHealthCheckRegistry(healthRegistry);
// and never again...
ds.setMetricRegistry(metricRegistry);
fail("Should not have been allowed to set registry after pool started");
}
catch (IllegalStateException ise) {
// pass
try {
ds.setHealthCheckRegistry(healthRegistry);
fail("Should not have been allowed to set registry after pool started");
}
catch (IllegalStateException ise2) {
// pass
}
}
}
}
@Test
public void testSetters2() throws Exception
{
try (HikariDataSource ds = newHikariDataSource()) {
ds.setMaximumPoolSize(1);
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
MetricRegistry metricRegistry = new MetricRegistry();
HealthCheckRegistry healthRegistry = new HealthCheckRegistry();
ds.setMetricRegistry(metricRegistry);
ds.setHealthCheckRegistry(healthRegistry);
// before the pool is started, we can set it any number of times...
ds.setMetricRegistry(metricRegistry);
ds.setHealthCheckRegistry(healthRegistry);
try (Connection connection = ds.getConnection()) {
// after the pool is started, we cannot set it any more
ds.setMetricRegistry(metricRegistry);
fail("Should not have been allowed to set registry after pool started");
}
catch (IllegalStateException ise) {
// pass
}
}
}
@Test
public void testSetters3() throws Exception
{
try (HikariDataSource ds = newHikariDataSource()) {
ds.setMaximumPoolSize(1);
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
MetricRegistry metricRegistry = new MetricRegistry();
MetricsTrackerFactory metricsTrackerFactory = new CodahaleMetricsTrackerFactory(metricRegistry);
try (Connection connection = ds.getConnection()) {
// After the pool as started, we can only set them once...
ds.setMetricsTrackerFactory(metricsTrackerFactory);
// and never again...
ds.setMetricsTrackerFactory(metricsTrackerFactory);
fail("Should not have been allowed to set metricsTrackerFactory after pool started");
}
catch (IllegalStateException ise) {
// pass
try {
// and never again... (even when calling another method)
ds.setMetricRegistry(metricRegistry);
fail("Should not have been allowed to set registry after pool started");
}
catch (IllegalStateException ise2) {
// pass
}
}
}
}
@Test
public void testSetters4() throws Exception
{
try (HikariDataSource ds = newHikariDataSource()) {
ds.setMaximumPoolSize(1);
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
MetricRegistry metricRegistry = new MetricRegistry();
// before the pool is started, we can set it any number of times using either setter
ds.setMetricRegistry(metricRegistry);
ds.setMetricRegistry(metricRegistry);
ds.setMetricRegistry(metricRegistry);
try (Connection connection = ds.getConnection()) {
// after the pool is started, we cannot set it any more
ds.setMetricRegistry(metricRegistry);
fail("Should not have been allowed to set registry after pool started");
}
catch (IllegalStateException ise) {
// pass
}
}
}
@Test
public void testSetters5() throws Exception
{
try (HikariDataSource ds = newHikariDataSource()) {
ds.setMaximumPoolSize(1);
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
MetricRegistry metricRegistry = new MetricRegistry();
MetricsTrackerFactory metricsTrackerFactory = new CodahaleMetricsTrackerFactory(metricRegistry);
// before the pool is started, we can set it any number of times using either setter
ds.setMetricsTrackerFactory(metricsTrackerFactory);
ds.setMetricsTrackerFactory(metricsTrackerFactory);
ds.setMetricsTrackerFactory(metricsTrackerFactory);
try (Connection connection = ds.getConnection()) {
// after the pool is started, we cannot set it any more
ds.setMetricsTrackerFactory(metricsTrackerFactory);
fail("Should not have been allowed to set registry factory after pool started");
}
catch (IllegalStateException ise) {
// pass
}
}
}
@Test(expected = IllegalArgumentException.class)
public void testFakeMetricRegistryThrowsIllegalArgumentException()
{
@ -313,20 +42,4 @@ public class TestMetrics
}
private static class FakeMetricRegistry {}
@Test
public void testMetricRegistrySubclassIsAllowed()
{
try (HikariDataSource ds = newHikariDataSource()) {
ds.setMaximumPoolSize(1);
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
ds.setMetricRegistry(new MetricRegistry() {
@Override
public Timer timer(String name) {
return super.timer(name);
}
});
}
}
}

@ -0,0 +1,147 @@
/*
* Copyright (C) 2013, 2014 Brett Wooldridge
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* 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.pool;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.SortedMap;
import java.util.concurrent.TimeUnit;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.Metric;
import com.codahale.metrics.MetricFilter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import com.codahale.metrics.health.HealthCheck.Result;
import com.codahale.metrics.health.HealthCheckRegistry;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.metrics.MetricsTrackerFactory;
import com.zaxxer.hikari.metrics.dropwizard.CodahaleMetricsTrackerFactory;
import com.zaxxer.hikari.util.UtilityElf;
import org.junit.Test;
import static com.zaxxer.hikari.pool.TestElf.newHikariConfig;
import static com.zaxxer.hikari.pool.TestElf.newHikariDataSource;
import static com.zaxxer.hikari.util.UtilityElf.quietlySleep;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeFalse;
/**
* Test HikariCP/CodaHale/Dropwizard 5 metrics integration.
*
* <p>
* This base test class contains tests common to Codahale metrics testing (pre-5) and Dropwizard 5 metrics testing.
* That's the idea behind the registry type parameterization and abstract methods.
* Include health checks when implemented for Dropwizard 5.
* There's still a bit of duplication between the extending classes.
*
* @author Brett Wooldridge
*/
abstract class TestMetricsBase<M>
{
protected abstract MetricsTrackerFactory metricsTrackerFactory(M metricRegistry);
protected abstract M metricRegistry();
@Test
public void testSetters3() throws Exception
{
try (HikariDataSource ds = newHikariDataSource()) {
ds.setMaximumPoolSize(1);
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
M metricRegistry = metricRegistry();
MetricsTrackerFactory metricsTrackerFactory = metricsTrackerFactory(metricRegistry);
try (Connection connection = ds.getConnection()) {
// After the pool as started, we can only set them once...
ds.setMetricsTrackerFactory(metricsTrackerFactory);
// and never again...
ds.setMetricsTrackerFactory(metricsTrackerFactory);
fail("Should not have been allowed to set metricsTrackerFactory after pool started");
}
catch (IllegalStateException ise) {
// pass
try {
// and never again... (even when calling another method)
ds.setMetricRegistry(metricRegistry);
fail("Should not have been allowed to set registry after pool started");
}
catch (IllegalStateException ise2) {
// pass
}
}
}
}
@Test
public void testSetters4() throws Exception
{
try (HikariDataSource ds = newHikariDataSource()) {
ds.setMaximumPoolSize(1);
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
M metricRegistry = metricRegistry();
// before the pool is started, we can set it any number of times using either setter
ds.setMetricRegistry(metricRegistry);
ds.setMetricRegistry(metricRegistry);
ds.setMetricRegistry(metricRegistry);
try (Connection connection = ds.getConnection()) {
// after the pool is started, we cannot set it any more
ds.setMetricRegistry(metricRegistry);
fail("Should not have been allowed to set registry after pool started");
}
catch (IllegalStateException ise) {
// pass
}
}
}
@Test
public void testSetters5() throws Exception
{
try (HikariDataSource ds = newHikariDataSource()) {
ds.setMaximumPoolSize(1);
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
M metricRegistry = metricRegistry();
MetricsTrackerFactory metricsTrackerFactory = metricsTrackerFactory(metricRegistry);
// before the pool is started, we can set it any number of times using either setter
ds.setMetricsTrackerFactory(metricsTrackerFactory);
ds.setMetricsTrackerFactory(metricsTrackerFactory);
ds.setMetricsTrackerFactory(metricsTrackerFactory);
try (Connection connection = ds.getConnection()) {
// after the pool is started, we cannot set it any more
ds.setMetricsTrackerFactory(metricsTrackerFactory);
fail("Should not have been allowed to set registry factory after pool started");
}
catch (IllegalStateException ise) {
// pass
}
}
}
}
Loading…
Cancel
Save