|
|
|
@ -16,6 +16,9 @@
|
|
|
|
|
|
|
|
|
|
package com.zaxxer.hikari.pool;
|
|
|
|
|
|
|
|
|
|
import static com.zaxxer.hikari.util.ConcurrentBag.STATE_IN_USE;
|
|
|
|
|
import static com.zaxxer.hikari.util.ConcurrentBag.STATE_NOT_IN_USE;
|
|
|
|
|
import static com.zaxxer.hikari.util.PoolUtilities.IS_JAVA7;
|
|
|
|
|
import static com.zaxxer.hikari.util.PoolUtilities.createInstance;
|
|
|
|
|
import static com.zaxxer.hikari.util.PoolUtilities.createThreadPoolExecutor;
|
|
|
|
|
import static com.zaxxer.hikari.util.PoolUtilities.elapsedTimeMs;
|
|
|
|
@ -51,9 +54,6 @@ import com.zaxxer.hikari.util.ConcurrentBag.IBagStateListener;
|
|
|
|
|
import com.zaxxer.hikari.util.DefaultThreadFactory;
|
|
|
|
|
import com.zaxxer.hikari.util.DriverDataSource;
|
|
|
|
|
import com.zaxxer.hikari.util.PropertyBeanSetter;
|
|
|
|
|
import static com.zaxxer.hikari.util.PoolUtilities.IS_JAVA7;
|
|
|
|
|
import static com.zaxxer.hikari.util.ConcurrentBag.STATE_IN_USE;
|
|
|
|
|
import static com.zaxxer.hikari.util.ConcurrentBag.STATE_NOT_IN_USE;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This is the primary connection pool class that provides the basic
|
|
|
|
@ -65,11 +65,16 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
|
|
|
|
|
{
|
|
|
|
|
private static final Logger LOGGER = LoggerFactory.getLogger(HikariPool.class);
|
|
|
|
|
|
|
|
|
|
public int transactionIsolation;
|
|
|
|
|
public final String catalog;
|
|
|
|
|
public final boolean isAutoCommit;
|
|
|
|
|
public final boolean isReadOnly;
|
|
|
|
|
|
|
|
|
|
private final DataSource dataSource;
|
|
|
|
|
|
|
|
|
|
private final IConnectionCustomizer connectionCustomizer;
|
|
|
|
|
private final HikariConfig configuration;
|
|
|
|
|
private final ConcurrentBag<IHikariConnectionProxy> connectionBag;
|
|
|
|
|
private final ConcurrentBag<PoolBagEntry> connectionBag;
|
|
|
|
|
private final ThreadPoolExecutor addConnectionExecutor;
|
|
|
|
|
private final IMetricsTracker metricsTracker;
|
|
|
|
|
|
|
|
|
@ -77,20 +82,16 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
|
|
|
|
|
private final AtomicInteger totalConnections;
|
|
|
|
|
private final ScheduledThreadPoolExecutor houseKeepingExecutorService;
|
|
|
|
|
|
|
|
|
|
private final boolean isAutoCommit;
|
|
|
|
|
private final boolean isIsolateInternalQueries;
|
|
|
|
|
private final boolean isReadOnly;
|
|
|
|
|
private final boolean isRecordMetrics;
|
|
|
|
|
private final boolean isRegisteredMbeans;
|
|
|
|
|
private final boolean isJdbc4ConnectionTest;
|
|
|
|
|
private final long leakDetectionThreshold;
|
|
|
|
|
private final String catalog;
|
|
|
|
|
private final String username;
|
|
|
|
|
private final String password;
|
|
|
|
|
|
|
|
|
|
private volatile long connectionTimeout;
|
|
|
|
|
private volatile boolean isShutdown;
|
|
|
|
|
private int transactionIsolation;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Construct a HikariPool with the specified configuration.
|
|
|
|
@ -116,7 +117,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
|
|
|
|
|
this.password = password;
|
|
|
|
|
|
|
|
|
|
this.totalConnections = new AtomicInteger();
|
|
|
|
|
this.connectionBag = new ConcurrentBag<IHikariConnectionProxy>();
|
|
|
|
|
this.connectionBag = new ConcurrentBag<PoolBagEntry>();
|
|
|
|
|
this.connectionBag.addBagStateListener(this);
|
|
|
|
|
this.lastConnectionFailure = new AtomicReference<Throwable>();
|
|
|
|
|
this.connectionTimeout = configuration.getConnectionTimeout();
|
|
|
|
@ -166,32 +167,29 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
do {
|
|
|
|
|
IHikariConnectionProxy connection = connectionBag.borrow(timeout, TimeUnit.MILLISECONDS);
|
|
|
|
|
if (connection == null) {
|
|
|
|
|
final PoolBagEntry bagEntry = connectionBag.borrow(timeout, TimeUnit.MILLISECONDS);
|
|
|
|
|
if (bagEntry == null) {
|
|
|
|
|
break; // We timed out... break and throw exception
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final long now = System.currentTimeMillis();
|
|
|
|
|
connection.unclose(now);
|
|
|
|
|
|
|
|
|
|
if (now > connection.getExpirationTime() || (now - connection.getLastAccess() > 1000L && !isConnectionAlive(connection, timeout))) {
|
|
|
|
|
closeConnection(connection); // Throw away the dead connection and try again
|
|
|
|
|
if (now > bagEntry.expirationTime || (now - bagEntry.lastAccess > 1000L && !isConnectionAlive(bagEntry.connection, timeout))) {
|
|
|
|
|
closeConnection(bagEntry); // Throw away the dead connection and try again
|
|
|
|
|
timeout = connectionTimeout - elapsedTimeMs(start);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isIsolateInternalQueries) {
|
|
|
|
|
IHikariConnectionProxy newProxy = ProxyFactory.getProxyConnection(connection);
|
|
|
|
|
connectionBag.remove(connection);
|
|
|
|
|
connectionBag.add(newProxy);
|
|
|
|
|
connection = newProxy;
|
|
|
|
|
}
|
|
|
|
|
final IHikariConnectionProxy proxyConnection = ProxyFactory.getProxyConnection(this, bagEntry);
|
|
|
|
|
|
|
|
|
|
if (leakDetectionThreshold != 0) {
|
|
|
|
|
connection.captureStack(leakDetectionThreshold, houseKeepingExecutorService);
|
|
|
|
|
proxyConnection.captureStack(leakDetectionThreshold, houseKeepingExecutorService);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isRecordMetrics) {
|
|
|
|
|
bagEntry.lastOpenTime = now;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return connection;
|
|
|
|
|
return proxyConnection;
|
|
|
|
|
}
|
|
|
|
|
while (timeout > 0L);
|
|
|
|
|
}
|
|
|
|
@ -213,19 +211,20 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
|
|
|
|
|
* @param connectionProxy the connection to release back to the pool
|
|
|
|
|
* @param isBroken true if the connection was detected as broken
|
|
|
|
|
*/
|
|
|
|
|
public void releaseConnection(final IHikariConnectionProxy connectionProxy, final boolean isBroken)
|
|
|
|
|
public void releaseConnection(final PoolBagEntry bagEntry, final boolean isBroken)
|
|
|
|
|
{
|
|
|
|
|
if (isRecordMetrics) {
|
|
|
|
|
metricsTracker.recordConnectionUsage(elapsedTimeMs(connectionProxy.getLastOpenTime()));
|
|
|
|
|
metricsTracker.recordConnectionUsage(elapsedTimeMs(bagEntry.lastOpenTime));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isBroken || isShutdown) {
|
|
|
|
|
LOGGER.debug("Connection returned to pool {} is broken, or the pool is shutting down. Closing connection.", configuration.getPoolName());
|
|
|
|
|
closeConnection(connectionProxy);
|
|
|
|
|
closeConnection(bagEntry);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
connectionBag.requite(connectionProxy);
|
|
|
|
|
bagEntry.lastAccess = System.currentTimeMillis();
|
|
|
|
|
connectionBag.requite(bagEntry);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -259,6 +258,10 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void evictConnection(IHikariConnectionProxy proxyConnection) {
|
|
|
|
|
closeConnection(proxyConnection.getPoolBagEntry());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the wrapped DataSource.
|
|
|
|
|
*
|
|
|
|
@ -269,28 +272,6 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
|
|
|
|
|
return dataSource;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Permanently close the real (underlying) connection (eat any exception).
|
|
|
|
|
*
|
|
|
|
|
* @param connectionProxy the connection to actually close
|
|
|
|
|
*/
|
|
|
|
|
public void closeConnection(final IHikariConnectionProxy connectionProxy)
|
|
|
|
|
{
|
|
|
|
|
try {
|
|
|
|
|
int tc = totalConnections.decrementAndGet();
|
|
|
|
|
if (tc < 0) {
|
|
|
|
|
LOGGER.warn("Internal accounting inconsistency, totalConnections={}", tc, new Exception());
|
|
|
|
|
}
|
|
|
|
|
connectionProxy.realClose();
|
|
|
|
|
}
|
|
|
|
|
catch (SQLException e) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
finally {
|
|
|
|
|
connectionBag.remove(connectionProxy);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public String toString()
|
|
|
|
|
{
|
|
|
|
@ -365,9 +346,9 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
|
|
|
|
|
@Override
|
|
|
|
|
public void closeIdleConnections()
|
|
|
|
|
{
|
|
|
|
|
for (IHikariConnectionProxy connectionProxy : connectionBag.values(STATE_NOT_IN_USE)) {
|
|
|
|
|
if (connectionBag.reserve(connectionProxy)) {
|
|
|
|
|
closeConnection(connectionProxy);
|
|
|
|
|
for (PoolBagEntry bagEntry : connectionBag.values(STATE_NOT_IN_USE)) {
|
|
|
|
|
if (connectionBag.reserve(bagEntry)) {
|
|
|
|
|
closeConnection(bagEntry);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -383,6 +364,28 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
|
|
|
|
|
// Private methods
|
|
|
|
|
// ***********************************************************************
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Permanently close the real (underlying) connection (eat any exception).
|
|
|
|
|
*
|
|
|
|
|
* @param connectionProxy the connection to actually close
|
|
|
|
|
*/
|
|
|
|
|
private void closeConnection(final PoolBagEntry bagEntry)
|
|
|
|
|
{
|
|
|
|
|
try {
|
|
|
|
|
int tc = totalConnections.decrementAndGet();
|
|
|
|
|
if (tc < 0) {
|
|
|
|
|
LOGGER.warn("Internal accounting inconsistency, totalConnections={}", tc, new Exception());
|
|
|
|
|
}
|
|
|
|
|
bagEntry.connection.close();
|
|
|
|
|
}
|
|
|
|
|
catch (SQLException e) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
finally {
|
|
|
|
|
connectionBag.remove(bagEntry);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create and add a single connection to the pool.
|
|
|
|
|
*/
|
|
|
|
@ -402,10 +405,16 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
|
|
|
|
|
|
|
|
|
|
executeSqlAutoCommit(connection, configuration.getConnectionInitSql());
|
|
|
|
|
|
|
|
|
|
IHikariConnectionProxy proxyConnection = ProxyFactory.getProxyConnection(this, connection, configuration.getMaxLifetime(), transactionIsolation,
|
|
|
|
|
isAutoCommit, isReadOnly, catalog);
|
|
|
|
|
proxyConnection.resetConnectionState();
|
|
|
|
|
connectionBag.add(proxyConnection);
|
|
|
|
|
connection.setAutoCommit(isAutoCommit);
|
|
|
|
|
connection.setTransactionIsolation(transactionIsolation);
|
|
|
|
|
connection.setReadOnly(isReadOnly);
|
|
|
|
|
if (catalog != null) {
|
|
|
|
|
connection.setCatalog(catalog);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PoolBagEntry bagEntry = new PoolBagEntry(connection, configuration.getMaxLifetime());
|
|
|
|
|
|
|
|
|
|
connectionBag.add(bagEntry);
|
|
|
|
|
lastConnectionFailure.set(null);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
@ -427,7 +436,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
|
|
|
|
|
* @param timeoutMs the timeout before we consider the test a failure
|
|
|
|
|
* @return true if the connection is alive, false if it is not alive or we timed out
|
|
|
|
|
*/
|
|
|
|
|
private boolean isConnectionAlive(final IHikariConnectionProxy connection, long timeoutMs)
|
|
|
|
|
private boolean isConnectionAlive(final Connection connection, long timeoutMs)
|
|
|
|
|
{
|
|
|
|
|
try {
|
|
|
|
|
final boolean timeoutEnabled = (configuration.getConnectionTimeout() != Integer.MAX_VALUE);
|
|
|
|
@ -481,23 +490,23 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
|
|
|
|
|
private void abortActiveConnections() throws InterruptedException
|
|
|
|
|
{
|
|
|
|
|
ExecutorService assassinExecutor = createThreadPoolExecutor(configuration.getMaximumPoolSize(), "HikariCP connection assassin", configuration.getThreadFactory());
|
|
|
|
|
for (IHikariConnectionProxy connectionProxy : connectionBag.values(STATE_IN_USE)) {
|
|
|
|
|
for (PoolBagEntry bagEntry : connectionBag.values(STATE_IN_USE)) {
|
|
|
|
|
try {
|
|
|
|
|
connectionProxy.abort(assassinExecutor);
|
|
|
|
|
totalConnections.decrementAndGet();
|
|
|
|
|
bagEntry.connection.abort(assassinExecutor);
|
|
|
|
|
}
|
|
|
|
|
catch (AbstractMethodError e) {
|
|
|
|
|
quietlyCloseConnection(connectionProxy);
|
|
|
|
|
quietlyCloseConnection(bagEntry.connection);
|
|
|
|
|
}
|
|
|
|
|
catch (SQLException e) {
|
|
|
|
|
quietlyCloseConnection(connectionProxy);
|
|
|
|
|
quietlyCloseConnection(bagEntry.connection);
|
|
|
|
|
}
|
|
|
|
|
finally {
|
|
|
|
|
try {
|
|
|
|
|
connectionBag.remove(connectionProxy);
|
|
|
|
|
connectionBag.remove(bagEntry);
|
|
|
|
|
totalConnections.decrementAndGet();
|
|
|
|
|
}
|
|
|
|
|
catch (IllegalStateException ise) {
|
|
|
|
|
continue;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -558,14 +567,14 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
|
|
|
|
|
final long now = System.currentTimeMillis();
|
|
|
|
|
final long idleTimeout = configuration.getIdleTimeout();
|
|
|
|
|
|
|
|
|
|
for (IHikariConnectionProxy connectionProxy : connectionBag.values(STATE_NOT_IN_USE)) {
|
|
|
|
|
if (connectionBag.reserve(connectionProxy)) {
|
|
|
|
|
if ((idleTimeout > 0L && now > connectionProxy.getLastAccess() + idleTimeout) || (now > connectionProxy.getExpirationTime())) {
|
|
|
|
|
closeConnection(connectionProxy);
|
|
|
|
|
for (PoolBagEntry bagEntry : connectionBag.values(STATE_NOT_IN_USE)) {
|
|
|
|
|
if (connectionBag.reserve(bagEntry)) {
|
|
|
|
|
if ((idleTimeout > 0L && now > bagEntry.lastAccess + idleTimeout) || (now > bagEntry.expirationTime)) {
|
|
|
|
|
closeConnection(bagEntry);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
connectionBag.unreserve(connectionProxy);
|
|
|
|
|
connectionBag.unreserve(bagEntry);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|