Start implementing [optional] metrics for HikariCP.

pull/60/head
Brett Wooldridge 11 years ago
parent 4b0cca092c
commit eeec5415c6

@ -69,8 +69,9 @@ public class HikariConfig implements HikariConfigMBean
private boolean isReadOnly; private boolean isReadOnly;
private boolean isInitializationFailFast; private boolean isInitializationFailFast;
private boolean isJdbc4connectionTest; private boolean isJdbc4connectionTest;
private boolean isRegisterMbeans;
private boolean isIsolateInternalQueries; private boolean isIsolateInternalQueries;
private boolean isRecordMetrics;
private boolean isRegisterMbeans;
private DataSource dataSource; private DataSource dataSource;
private Properties dataSourceProperties; private Properties dataSourceProperties;
private IConnectionCustomizer connectionCustomizer; private IConnectionCustomizer connectionCustomizer;
@ -96,6 +97,7 @@ public class HikariConfig implements HikariConfigMBean
maxPoolSize = 10; maxPoolSize = 10;
maxLifetime = MAX_LIFETIME; maxLifetime = MAX_LIFETIME;
poolName = "HikariPool-" + poolNumber++; poolName = "HikariPool-" + poolNumber++;
isRecordMetrics = false;
transactionIsolation = -1; transactionIsolation = -1;
} }
@ -429,6 +431,16 @@ public class HikariConfig implements HikariConfigMBean
this.isReadOnly = readOnly; this.isReadOnly = readOnly;
} }
public boolean isRecordMetrics()
{
return isRecordMetrics;
}
public void setRecordMetrics(boolean recordMetrics)
{
this.isRecordMetrics = recordMetrics;
}
public boolean isRegisterMbeans() public boolean isRegisterMbeans()
{ {
return isRegisterMbeans; return isRegisterMbeans;

@ -31,6 +31,9 @@ import javax.sql.DataSource;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.zaxxer.hikari.metrics.CodaHaleMetricsTracker;
import com.zaxxer.hikari.metrics.MetricsTracker;
import com.zaxxer.hikari.metrics.MetricsTracker.Context;
import com.zaxxer.hikari.proxy.IHikariConnectionProxy; import com.zaxxer.hikari.proxy.IHikariConnectionProxy;
import com.zaxxer.hikari.proxy.ProxyFactory; import com.zaxxer.hikari.proxy.ProxyFactory;
import com.zaxxer.hikari.util.ConcurrentBag; import com.zaxxer.hikari.util.ConcurrentBag;
@ -55,6 +58,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
private final HikariConfig configuration; private final HikariConfig configuration;
private final ConcurrentBag<IHikariConnectionProxy> connectionBag; private final ConcurrentBag<IHikariConnectionProxy> connectionBag;
private final ThreadPoolExecutor addConnectionExecutor; private final ThreadPoolExecutor addConnectionExecutor;
private final MetricsTracker metricsTracker;
private final boolean isAutoCommit; private final boolean isAutoCommit;
private final boolean isIsolateInternalQueries; private final boolean isIsolateInternalQueries;
@ -103,6 +107,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
this.isJdbc4ConnectionTest = configuration.isJdbc4ConnectionTest(); this.isJdbc4ConnectionTest = configuration.isJdbc4ConnectionTest();
this.leakDetectionThreshold = configuration.getLeakDetectionThreshold(); this.leakDetectionThreshold = configuration.getLeakDetectionThreshold();
this.transactionIsolation = configuration.getTransactionIsolation(); this.transactionIsolation = configuration.getTransactionIsolation();
this.metricsTracker = (configuration.isRecordMetrics() ? new CodaHaleMetricsTracker(configuration.getPoolName()) : new MetricsTracker());
this.dataSource = initializeDataSource(); this.dataSource = initializeDataSource();
@ -132,10 +137,11 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
*/ */
Connection getConnection() throws SQLException Connection getConnection() throws SQLException
{ {
final long start = System.currentTimeMillis();
final Context context = metricsTracker.recordConnectionRequest(start);
long timeout = configuration.getConnectionTimeout();
try try
{ {
long timeout = configuration.getConnectionTimeout();
final long start = System.currentTimeMillis();
do do
{ {
IHikariConnectionProxy connectionProxy = connectionBag.borrow(timeout, TimeUnit.MILLISECONDS); IHikariConnectionProxy connectionProxy = connectionBag.borrow(timeout, TimeUnit.MILLISECONDS);
@ -161,16 +167,17 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
} }
while (timeout > 0); while (timeout > 0);
String msg = String.format("Timeout of %dms encountered waiting for connection.", configuration.getConnectionTimeout());
LOGGER.warn(msg);
logPoolState("Timeout failure "); logPoolState("Timeout failure ");
throw new SQLException(String.format("Timeout of %dms encountered waiting for connection.", configuration.getConnectionTimeout()));
throw new SQLException(msg);
} }
catch (InterruptedException e) catch (InterruptedException e)
{ {
return null; return null;
} }
finally
{
context.stop();
}
} }
/** /**
@ -181,6 +188,8 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
*/ */
public void releaseConnection(IHikariConnectionProxy connectionProxy) public void releaseConnection(IHikariConnectionProxy connectionProxy)
{ {
metricsTracker.recordConnectionUsage(System.currentTimeMillis() - connectionProxy.getLastOpenTime());
if (!connectionProxy.isBrokenConnection() && !isShutdown) if (!connectionProxy.isBrokenConnection() && !isShutdown)
{ {
connectionBag.requite(connectionProxy); connectionBag.requite(connectionProxy);
@ -241,7 +250,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
continue; continue;
} }
if (minIdle == 0) if (minIdle == 0) // This break is here so we only add one connection when demanded
{ {
break; break;
} }
@ -314,7 +323,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
if (totalConnections.incrementAndGet() > configuration.getMaximumPoolSize()) if (totalConnections.incrementAndGet() > configuration.getMaximumPoolSize())
{ {
totalConnections.decrementAndGet(); totalConnections.decrementAndGet();
return false; return true;
} }
connection = (username == null && password == null) ? dataSource.getConnection() : dataSource.getConnection(username, password); connection = (username == null && password == null) ? dataSource.getConnection() : dataSource.getConnection(username, password);

@ -0,0 +1,66 @@
/*
* 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.metrics;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import com.zaxxer.hikari.HikariPool;
public final class CodaHaleMetricsTracker extends MetricsTracker
{
private MetricRegistry registry;
private Timer connectionObtainTimer;
private Histogram connectionUsage;
public CodaHaleMetricsTracker(String poolName)
{
registry = new MetricRegistry();
connectionObtainTimer = registry.timer(MetricRegistry.name(HikariPool.class, "connection", "wait"));
connectionUsage = registry.histogram(MetricRegistry.name(HikariPool.class, "connection", "usage"));
}
@Override
public Context recordConnectionRequest(long requestTime)
{
return new Context(connectionObtainTimer);
}
@Override
public void recordConnectionUsage(long usageMilleseconds)
{
connectionUsage.update(usageMilleseconds);
}
public static final class Context extends MetricsTracker.Context
{
Timer.Context innerContext;
Context(Timer timer)
{
innerContext = timer.time();
}
public void stop()
{
if (innerContext != null)
{
innerContext.stop();
}
}
}
}

@ -0,0 +1,44 @@
/*
* 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.metrics;
/**
* This class does absolutely nothing.
*
* @author Brett Wooldridge
*/
public class MetricsTracker
{
private static final Context NO_CONTEXT = new Context();
public static class Context
{
public void stop()
{
}
}
public Context recordConnectionRequest(long start)
{
return NO_CONTEXT;
}
public void recordConnectionUsage(long usageMilleseconds)
{
}
}

@ -62,6 +62,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
private boolean isReadOnlyDirty; private boolean isReadOnlyDirty;
private boolean isTransactionIsolationDirty; private boolean isTransactionIsolationDirty;
private volatile long lastAccess; private volatile long lastAccess;
private long uncloseTime;
private StackTraceElement[] leakTrace; private StackTraceElement[] leakTrace;
private TimerTask leakTask; private TimerTask leakTask;
@ -163,6 +164,13 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
return lastAccess; return lastAccess;
} }
/** {@inheritDoc} */
@Override
public long getLastOpenTime()
{
return uncloseTime;
}
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public final boolean isBrokenConnection() public final boolean isBrokenConnection()
@ -218,6 +226,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
public final void unclose() public final void unclose()
{ {
isClosed = false; isClosed = false;
uncloseTime = System.currentTimeMillis();
} }
/** {@inheritDoc} */ /** {@inheritDoc} */

@ -60,6 +60,13 @@ public interface IHikariConnectionProxy extends Connection, IBagManagable
*/ */
long getLastAccess(); long getLastAccess();
/**
* Get the timestamp of when the connection was removed from the pool for use.
*
* @return the timestamp the connection started to be used in the most recent request
*/
long getLastOpenTime();
/** /**
* Return the broken state of the connection. If checkException() detected * Return the broken state of the connection. If checkException() detected
* a broken connection, this method will return true, otherwise false. * a broken connection, this method will return true, otherwise false.

Loading…
Cancel
Save