Add MBean support.

pull/1/head
Brett Wooldridge 11 years ago
parent 169bba9a58
commit 8b0911baab

@ -28,34 +28,44 @@ import org.slf4j.LoggerFactory;
* limitations under the License.
*/
public class HikariConfig
public class HikariConfig implements HikariConfigMBean
{
private int minPoolSize;
private int maxPoolSize;
private String connectionUrl;
private int acquireIncrement;
private int acquireRetries;
private long acquireRetryDelay;
private long connectionTimeout;
private static int poolNumber;
private volatile int acquireIncrement;
private volatile int acquireRetries;
private volatile long acquireRetryDelay;
private volatile long connectionTimeout;
private String connectionTestQuery;
private String connectionUrl;
private String dataSourceClassName;
private String proxyFactoryType;
private volatile long idleTimeout;
private boolean isJdbc4connectionTest;
private long maxLifetime;
private long leakDetectionThreshold;
private long idleTimeout;
private volatile long leakDetectionThreshold;
private volatile long maxLifetime;
private volatile int minPoolSize;
private volatile int maxPoolSize;
private String poolName;
private Properties driverProperties;
/**
* Default constructor
*/
public HikariConfig()
{
acquireIncrement = 1;
maxPoolSize = 1;
connectionTimeout = Integer.MAX_VALUE;
idleTimeout = TimeUnit.MINUTES.toMillis(30);
proxyFactoryType = "auto";
driverProperties = new Properties();
acquireIncrement = 5;
acquireRetries = 3;
acquireRetryDelay = 750;
connectionTimeout = 5000;
idleTimeout = TimeUnit.MINUTES.toMillis(10);
isJdbc4connectionTest = true;
minPoolSize = 10;
maxPoolSize = 60;
maxLifetime = TimeUnit.MINUTES.toMillis(30);
poolName = "HikariPool-" + poolNumber++;
}
public HikariConfig(String propertyFileName)
@ -80,11 +90,23 @@ public class HikariConfig
}
}
public void addDriverProperty(String propertyName, String value)
{
driverProperties.put(propertyName, value);
}
public Properties getDriverProperties()
{
return driverProperties;
}
/** {@inheritDoc} */
public int getAcquireIncrement()
{
return acquireIncrement;
}
/** {@inheritDoc} */
public void setAcquireIncrement(int acquireIncrement)
{
if (acquireIncrement < 1)
@ -94,11 +116,13 @@ public class HikariConfig
this.acquireIncrement = acquireIncrement;
}
/** {@inheritDoc} */
public int getAcquireRetries()
{
return acquireRetries;
}
/** {@inheritDoc} */
public void setAcquireRetries(int acquireRetries)
{
if (acquireRetries < 0)
@ -108,11 +132,13 @@ public class HikariConfig
this.acquireRetries = acquireRetries;
}
/** {@inheritDoc} */
public long getAcquireRetryDelay()
{
return acquireRetryDelay;
}
/** {@inheritDoc} */
public void setAcquireRetryDelay(long acquireRetryDelayMs)
{
if (acquireRetryDelayMs < 0)
@ -132,17 +158,23 @@ public class HikariConfig
this.connectionTestQuery = connectionTestQuery;
}
/** {@inheritDoc} */
public long getConnectionTimeout()
{
return connectionTimeout;
}
/** {@inheritDoc} */
public void setConnectionTimeout(long connectionTimeoutMs)
{
if (connectionTimeoutMs < 0)
{
throw new IllegalArgumentException("connectionTimeout cannot be negative");
}
if (connectionTimeoutMs == 0)
{
this.connectionTimeout = Integer.MAX_VALUE;
}
this.connectionTimeout = connectionTimeoutMs;
}
@ -166,11 +198,13 @@ public class HikariConfig
this.dataSourceClassName = className;
}
/** {@inheritDoc} */
public long getIdleTimeout()
{
return idleTimeout;
}
/** {@inheritDoc} */
public void setIdleTimeout(long idleTimeoutMs)
{
this.idleTimeout = idleTimeoutMs;
@ -186,31 +220,37 @@ public class HikariConfig
this.isJdbc4connectionTest = useIsValid;
}
/** {@inheritDoc} */
public long getLeakDetectionThreshold()
{
return leakDetectionThreshold;
}
/** {@inheritDoc} */
public void setLeakDetectionThreshold(long leakDetectionThresholdMs)
{
this.leakDetectionThreshold = leakDetectionThresholdMs;
}
/** {@inheritDoc} */
public long getMaxLifetime()
{
return maxLifetime;
}
/** {@inheritDoc} */
public void setMaxLifetime(long maxLifetimeMs)
{
this.maxLifetime = maxLifetimeMs;
}
/** {@inheritDoc} */
public int getMinimumPoolSize()
{
return minPoolSize;
}
/** {@inheritDoc} */
public void setMinimumPoolSize(int minPoolSize)
{
if (minPoolSize < 0)
@ -220,11 +260,13 @@ public class HikariConfig
this.minPoolSize = minPoolSize;
}
/** {@inheritDoc} */
public int getMaximumPoolSize()
{
return maxPoolSize;
}
/** {@inheritDoc} */
public void setMaximumPoolSize(int maxPoolSize)
{
if (maxPoolSize < 0)
@ -234,14 +276,21 @@ public class HikariConfig
this.maxPoolSize = maxPoolSize;
}
public String getProxyFactoryType()
/** {@inheritDoc} */
public String getPoolName()
{
return proxyFactoryType;
return poolName;
}
public void setProxyFactoryType(String proxyFactoryClassName)
/**
* Set the name of the connection pool. This is primarily used for the MBean
* to uniquely identify the pool configuration.
*
* @param poolName the name of the connection pool to use
*/
public void setPoolName(String poolName)
{
this.proxyFactoryType = proxyFactoryClassName;
this.poolName = poolName;
}
public void validate()
@ -254,16 +303,39 @@ public class HikariConfig
throw new IllegalStateException("Either jdbc4ConnectionTest must be enabled or a connectionTestQuery must be specified.");
}
if (minPoolSize < 0)
{
logger.error("minPoolSize cannot be negative.");
throw new IllegalStateException("minPoolSize cannot be negative.");
}
if (maxLifetime < 0)
{
logger.error("maxLifetime cannot be negative.");
throw new IllegalStateException("maxLifetime cannot be negative.");
}
if (idleTimeout < 0)
{
logger.error("idleTimeout cannot be negative.");
throw new IllegalStateException("idleTimeout cannot be negative.");
}
if (acquireRetryDelay < 0)
{
logger.error("acquireRetryDelay cannot be negative.");
throw new IllegalStateException("acquireRetryDelay cannot be negative.");
}
if (maxPoolSize < minPoolSize)
{
logger.warn("maxPoolSize is less than minPoolSize, forcing them equal.");
maxPoolSize = minPoolSize;
}
if (proxyFactoryType == null)
if (connectionTimeout == Integer.MAX_VALUE)
{
logger.error("proxyFactoryType cannot be null");
throw new IllegalStateException("proxyFactoryType cannot be null");
logger.warn("No connection wait timeout is set, this might cause an infinite wait.");
}
}
}

@ -0,0 +1,200 @@
/*
* Copyright (C) 2013 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;
/**
* The javax.management MBean for a Hikiri pool configuration.
*
* @author Brett Wooldridge
*/
public interface HikariConfigMBean
{
/**
* This property controls the maximum number of connections that are acquired at one time,
* with the exception of pool initialization.
*
* @return the acquire increment
*/
int getAcquireIncrement();
/**
* This property controls the maximum number of connections that are acquired at one time,
* with the exception of pool initialization.
*
* @param acquireIncrement the acquire increment
*/
void setAcquireIncrement(int acquireIncrement);
/**
* This is a per-connection attempt retry count used during new connection creation (acquisition).
* If a connection creation attempt fails there will be a wait of {@link getAcquireRetryDelay} milliseconds
* followed by another attempt, up to the number of retries configured by this property.
*
* @return the acquire retry count
*/
int getAcquireRetries();
/**
* This is a per-connection attempt retry count used during new connection creation (acquisition).
* If a connection creation attempt fails there will be a wait of {@link setAcquireRetryDelay} milliseconds
* followed by another attempt, up to the number of retries configured by this property.
*
* @param acquireRetries the acquire retry count
*/
void setAcquireRetries(int acquireRetries);
/**
* This property controls the number of milliseconds to delay between attempts to acquire a connection
* to the database. If acquireRetries is 0, this property has no effect.
*
* @return the acquire retry delay in milliseconds
*/
long getAcquireRetryDelay();
/**
* This property controls the number of milliseconds to delay between attempts to acquire a connection
* to the database. If acquireRetries is 0, this property has no effect.
*
* @param acquireRetryDelayMs the acquire retry delay in milliseconds
*/
void setAcquireRetryDelay(long acquireRetryDelayMs);
/**
* This is for "legacy" databases that do not support the JDBC4 {@link Connection.isValid()} API. This is the
* query that will be executed just before a connection is given to you from the pool to validate that
* the connection to the database is still alive. It is database dependent and should be a query that
* takes very little processing by the database (eg. "VALUES 1"). See the {@link getJdbc4ConnectionTest} property
* for a more efficent alive test. One of either this property or jdbc4ConnectionTest must be specified.
*
* @return the connection timeout in milliseconds
*/
long getConnectionTimeout();
/**
* This is for "legacy" databases that do not support the JDBC4 Connection.isValid() API. This is the
* query that will be executed just before a connection is given to you from the pool to validate that
* the connection to the database is still alive. It is database dependent and should be a query that
* takes very little processing by the database (eg. "VALUES 1"). See the {@link setJdbc4ConnectionTest} property
* for a more efficent alive test. One of either this property or jdbc4ConnectionTest must be specified.
* @param connectionTimeoutMs the connection timeout in milliseconds
*/
void setConnectionTimeout(long connectionTimeoutMs);
/**
* This property controls the maximum amount of time (in milliseconds) that a connection is allowed to sit
* idle in the pool. Whether a connection is retired as idle or not is subject to a maximum variation of +30
* seconds, and average variation of +15 seconds. A connection will never be retired as idle before this timeout.
* A value of 0 means that idle connections are never removed from the pool.
*
* @return the idle timeout in milliseconds
*/
long getIdleTimeout();
/**
* This property controls the maximum amount of time (in milliseconds) that a connection is allowed to sit
* idle in the pool. Whether a connection is retired as idle or not is subject to a maximum variation of +30
* seconds, and average variation of +15 seconds. A connection will never be retired as idle before this timeout.
* A value of 0 means that idle connections are never removed from the pool.
*
* @param idleTimeoutMs the idle timeout in milliseconds
*/
void setIdleTimeout(long idleTimeoutMs);
/**
* This property controls the amount of time that a connection can be out of the pool before a message is
* logged indicating a possible connection leak. A value of 0 means leak detection is disabled.
*
* @return the connection leak detection threshold in milliseconds
*/
long getLeakDetectionThreshold();
/**
* This property controls the amount of time that a connection can be out of the pool before a message is
* logged indicating a possible connection leak. A value of 0 means leak detection is disabled.
*
* @param leakDetectionThresholdMs the connection leak detection threshold in milliseconds
*/
void setLeakDetectionThreshold(long leakDetectionThresholdMs);
/**
* This property controls the maximum lifetime of a connection in the pool. When a connection reaches this
* timeout, even if recently used, it will be retired from the pool. An in-use connection will never be
* retired, only when it is idle will it be removed.
*
* @return the maximum connection lifetime in milliseconds
*/
long getMaxLifetime();
/**
* This property controls the maximum lifetime of a connection in the pool. When a connection reaches this
* timeout, even if recently used, it will be retired from the pool. An in-use connection will never be
* retired, only when it is idle will it be removed.
*
* @param maxLifetimeMs the maximum connection lifetime in milliseconds
*/
void setMaxLifetime(long maxLifetimeMs);
/**
* The property controls the maximum size that the pool is allowed to reach, including both idle and in-use
* connections. Basically this value will determine the maximum number of actual connections to the database
* backend.
* <p>
* When the pool reaches this size, and no idle connections are available, calls to getConnection() will
* block for up to connectionTimeout milliseconds before timing out.
*
* @return the minimum number of connections in the pool
*/
int getMinimumPoolSize();
/**
* The property controls the minimum number of connections that HikariCP tries to maintain in the pool,
* including both idle and in-use connections. If the connections dip below this value, HikariCP will
* make a best effort to restore them quickly and efficiently.
*
* @param minPoolSize the minimum number of connections in the pool
*/
void setMinimumPoolSize(int minPoolSize);
/**
* The property controls the minimum number of connections that HikariCP tries to maintain in the pool,
* including both idle and in-use connections. If the connections dip below this value, HikariCP will
* make a best effort to restore them quickly and efficiently.
*
* @return the maximum number of connections in the pool
*/
int getMaximumPoolSize();
/**
* The property controls the maximum size that the pool is allowed to reach, including both idle and in-use
* connections. Basically this value will determine the maximum number of actual connections to the database
* backend.
* <p>
* When the pool reaches this size, and no idle connections are available, calls to getConnection() will
* block for up to connectionTimeout milliseconds before timing out.
*
* @param maxPoolSize the maximum number of connections in the pool
*/
void setMaximumPoolSize(int maxPoolSize);
/**
* The name of the connection pool.
*
* @return the name of the connection pool
*/
String getPoolName();
}

@ -16,6 +16,7 @@
package com.zaxxer.hikari;
import java.lang.management.ManagementFactory;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
@ -28,12 +29,14 @@ import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HikariPool
public class HikariPool implements HikariPoolMBean
{
private static final Logger LOGGER = LoggerFactory.getLogger(HikariPool.class);
@ -44,9 +47,6 @@ public class HikariPool
private final AtomicInteger totalConnections;
private final AtomicInteger idleConnectionCount;
private final DataSource dataSource;
private final long maxLifeTime;
private final long leakDetectionThreshold;
private final boolean jdbc4ConnectionTest;
private final Timer houseKeepingTimer;
@ -66,9 +66,7 @@ public class HikariPool
this.idleConnectionCount = new AtomicInteger();
this.idleConnections = new LinkedTransferQueue<IHikariConnectionProxy>();
this.inUseConnections = Collections.newSetFromMap(new ConcurrentHashMap<IHikariConnectionProxy, Boolean>(configuration.getMaximumPoolSize() * 2, 0.75f, 100));
this.leakDetectionThreshold = configuration.getLeakDetectionThreshold();
this.maxLifeTime = configuration.getMaxLifetime();
this.jdbc4ConnectionTest = configuration.isJdbc4ConnectionTest();
try
@ -81,20 +79,17 @@ public class HikariPool
throw new RuntimeException("Could not create datasource class: " + configuration.getDataSourceClassName(), e);
}
registerMBean();
houseKeepingTimer = new Timer("Hikari Housekeeping Timer", true);
long idleTimeout = configuration.getIdleTimeout();
if (idleTimeout > 0 || maxLifeTime > 0 || leakDetectionThreshold > 0)
if (idleTimeout > 0 || configuration.getMaxLifetime() > 0)
{
houseKeepingTimer.scheduleAtFixedRate(new HouseKeeper(), TimeUnit.MINUTES.toMillis(1), TimeUnit.MINUTES.toMillis(1));
houseKeepingTimer.scheduleAtFixedRate(new HouseKeeper(), TimeUnit.SECONDS.toMillis(30), TimeUnit.SECONDS.toMillis(30));
}
System.setProperty("hikariProxyGeneratorType", configuration.getProxyFactoryType());
while (totalConnections.get() < configuration.getMinimumPoolSize())
{
fillPool();
}
fillPool();
}
Connection getConnection() throws SQLException
@ -107,7 +102,7 @@ public class HikariPool
{
if (idleConnectionCount.get() == 0)
{
fillPool();
addConnections();
}
IHikariConnectionProxy connectionProxy = idleConnections.poll(timeout, TimeUnit.MILLISECONDS);
@ -119,9 +114,10 @@ public class HikariPool
idleConnectionCount.decrementAndGet();
if (maxLifeTime > 0 && start - connectionProxy.getCreationTime() > maxLifeTime)
final long maxLifetime = configuration.getMaxLifetime();
if (maxLifetime > 0 && start - connectionProxy.getCreationTime() > maxLifetime)
{
// Throw away the connection
// Throw away the connection that has passed its lifetime
closeConnection(connectionProxy);
timeout -= (System.currentTimeMillis() - start);
continue;
@ -130,18 +126,13 @@ public class HikariPool
Connection connection = (Connection) connectionProxy;
if (!isConnectionAlive(connection, timeout))
{
// Throw away the connection, and nap for a few ms
// Throw away the dead connection
closeConnection(connectionProxy);
Thread.sleep(50l);
timeout -= (System.currentTimeMillis() - start);
if (timeout < 0)
{
throw new SQLException("Timeout of encountered waiting for connection");
}
continue;
}
if (leakDetectionThreshold > 0)
if (configuration.getLeakDetectionThreshold() > 0)
{
connectionProxy.captureStack(configuration.getLeakDetectionThreshold(), houseKeepingTimer);
}
@ -164,34 +155,71 @@ public class HikariPool
if (existing)
{
connection.setLastAccess(System.currentTimeMillis());
idleConnections.put(connection);
idleConnectionCount.incrementAndGet();
idleConnections.put(connection);
}
else
{
// Should never happen, just a precaution
LOGGER.warn("Connection not found in inUseConnections set upon return");
totalConnections.decrementAndGet();
}
}
int getTotalConnectionCount()
/** {@inheritDoc} */
public int getActiveConnections()
{
return Math.min(configuration.getMaximumPoolSize(), totalConnections.get() - idleConnectionCount.get());
}
/** {@inheritDoc} */
public int getIdleConnections()
{
return idleConnectionCount.get();
}
/** {@inheritDoc} */
public int getTotalConnections()
{
return totalConnections.get();
}
int getIdleConnectionCount()
/** {@inheritDoc} */
public int getThreadsAwaitingConnection()
{
return idleConnectionCount.get();
return idleConnections.getWaitingConsumerCount();
}
private synchronized void fillPool()
/** {@inheritDoc} */
public void closeIdleConnections()
{
final int max = configuration.getMaximumPoolSize();
if (totalConnections.get() >= max)
final int idleCount = idleConnectionCount.get();
for (int i = 0; i < idleCount; i++)
{
return;
IHikariConnectionProxy connectionProxy = idleConnections.poll();
if (connectionProxy == null)
{
break;
}
idleConnectionCount.decrementAndGet();
closeConnection(connectionProxy);
}
}
private void fillPool()
{
int maxIters = configuration.getMinimumPoolSize() / configuration.getAcquireIncrement();
while (totalConnections.get() < configuration.getMinimumPoolSize() && maxIters-- > 0)
{
addConnections();
}
}
private synchronized void addConnections()
{
final int max = configuration.getMaximumPoolSize();
final int increment = configuration.getAcquireIncrement();
for (int i = 0; i < increment && totalConnections.get() < max; i++)
{
@ -215,11 +243,14 @@ public class HikariPool
idleConnectionCount.incrementAndGet();
totalConnections.incrementAndGet();
idleConnections.add(proxyConnection);
LOGGER.trace("Added connection");
break;
}
else
{
Thread.sleep(configuration.getAcquireRetryDelay());
}
}
catch (SQLException e)
catch (Exception e)
{
if (retries++ > configuration.getAcquireRetries())
{
@ -279,6 +310,30 @@ public class HikariPool
}
}
private void registerMBean()
{
try
{
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
ObjectName poolConfigName = new ObjectName("com.zaxxer.hikari:type=PoolConfig (" + configuration.getPoolName() + ")");
ObjectName poolName = new ObjectName("com.zaxxer.hikari:type=Pool (" + configuration.getPoolName() + ")");
if (!mBeanServer.isRegistered(poolConfigName))
{
mBeanServer.registerMBean(configuration, poolConfigName);
mBeanServer.registerMBean(this, poolName);
}
else
{
LOGGER.error("You cannot use the same HikariConfig for separate pool instances.");
}
}
catch (Exception e)
{
LOGGER.warn("Unable to register management beans.", e);
}
}
private class HouseKeeper extends TimerTask
{
public void run()
@ -287,6 +342,7 @@ public class HikariPool
final long now = System.currentTimeMillis();
final long idleTimeout = configuration.getIdleTimeout();
final long maxLifetime = configuration.getMaxLifetime();
final int idleCount = idleConnectionCount.get();
for (int i = 0; i < idleCount; i++)
@ -297,25 +353,22 @@ public class HikariPool
break;
}
idleConnectionCount.decrementAndGet();
if ((idleTimeout > 0 && now > connectionProxy.getLastAccess() + idleTimeout)
||
(maxLifeTime > 0 && now > connectionProxy.getCreationTime() + maxLifeTime))
(maxLifetime > 0 && now > connectionProxy.getCreationTime() + maxLifetime))
{
idleConnectionCount.decrementAndGet();
closeConnection(connectionProxy);
}
else
{
idleConnections.add(connectionProxy);
idleConnectionCount.incrementAndGet();
idleConnections.add(connectionProxy);
}
}
int maxIters = configuration.getMinimumPoolSize() / configuration.getAcquireIncrement();
while (totalConnections.get() < configuration.getMinimumPoolSize() && --maxIters > 0)
{
fillPool();
}
addConnections();
}
}
}

@ -0,0 +1,35 @@
/*
* Copyright (C) 2013 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;
/**
* The javax.management MBean for a Hikiri pool instance.
*
* @author Brett Wooldridge
*/
public interface HikariPoolMBean
{
int getIdleConnections();
int getActiveConnections();
int getTotalConnections();
int getThreadsAwaitingConnection();
void closeIdleConnections();
}
Loading…
Cancel
Save