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

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

@ -37,7 +37,7 @@ public interface IHikariConnectionProxy extends Connection, IBagManagable
* @param houseKeepingTimer the timer to run the leak detection task with
*/
void captureStack(long leakThreshold, Timer houseKeepingTimer);
/**
* Check if the provided SQLException contains a SQLSTATE that indicates
* a disconnection from the server.
@ -45,21 +45,28 @@ public interface IHikariConnectionProxy extends Connection, IBagManagable
* @param sqle the SQLException to check
*/
void checkException(SQLException sqle);
/**
* Get the creation timestamp of the connection.
*
* @return the creation timestamp
*/
long getCreationTime();
/**
* Get the last access timestamp of the connection.
*
* @return the last access timestamp
*/
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
* a broken connection, this method will return true, otherwise false.
@ -67,7 +74,7 @@ public interface IHikariConnectionProxy extends Connection, IBagManagable
* @return the broken state of the connection
*/
boolean isBrokenConnection();
/**
* Actually close the underlying delegate Connection.
*
@ -81,12 +88,12 @@ public interface IHikariConnectionProxy extends Connection, IBagManagable
* @throws SQLException thrown if there is an error resetting any of the state
*/
void resetConnectionState() throws SQLException;
/**
* Make the Connection available for use again by marking it as not closed.
*/
void unclose();
/**
* Called by Statement and its subclasses when they are closed to remove them
* from the tracking list.

Loading…
Cancel
Save