|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
|
|
|
import java.sql.Connection;
|
|
|
|
import java.sql.SQLException;
|
|
|
|
import java.sql.Statement;
|
|
|
|
import java.util.Timer;
|
|
|
|
import java.util.TimerTask;
|
|
|
|
import java.util.concurrent.LinkedBlockingQueue;
|
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
|
|
|
|
|
|
import javax.sql.DataSource;
|
|
|
|
|
|
|
|
import org.slf4j.Logger;
|
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
|
|
|
|
import com.zaxxer.hikari.proxy.IHikariConnectionProxy;
|
|
|
|
import com.zaxxer.hikari.proxy.ProxyFactory;
|
|
|
|
import com.zaxxer.hikari.util.PropertyBeanSetter;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This is the primary connection pool class that provides the basic
|
|
|
|
* pooling behavior for HikariCP.
|
|
|
|
*
|
|
|
|
* @author Brett Wooldridge
|
|
|
|
*/
|
|
|
|
public final class HikariPool implements HikariPoolMBean
|
|
|
|
{
|
|
|
|
private static final Logger LOGGER = LoggerFactory.getLogger(HikariPool.class);
|
|
|
|
|
|
|
|
final DataSource dataSource;
|
|
|
|
|
|
|
|
private final IConnectionCustomizer connectionCustomizer;
|
|
|
|
private final HikariConfig configuration;
|
|
|
|
private final LinkedBlockingQueue<IHikariConnectionProxy> idleConnections;
|
|
|
|
|
|
|
|
private final Timer houseKeepingTimer;
|
|
|
|
|
|
|
|
private final long leakDetectionThreshold;
|
|
|
|
private final AtomicBoolean backgroundFillQueued;
|
|
|
|
private final AtomicInteger idleConnectionCount;
|
|
|
|
private final AtomicInteger totalConnections;
|
|
|
|
private final AtomicInteger awaitingConnection;
|
|
|
|
private final boolean isAutoCommit;
|
|
|
|
private final boolean jdbc4ConnectionTest;
|
|
|
|
private final boolean isRegisteredMbeans;
|
|
|
|
private int transactionIsolation;
|
|
|
|
private volatile boolean shutdown;
|
|
|
|
private boolean debug;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Construct a HikariPool with the specified configuration.
|
|
|
|
*
|
|
|
|
* @param configuration a HikariConfig instance
|
|
|
|
*/
|
|
|
|
HikariPool(HikariConfig configuration)
|
|
|
|
{
|
|
|
|
configuration.validate();
|
|
|
|
|
|
|
|
this.configuration = configuration;
|
|
|
|
this.totalConnections = new AtomicInteger();
|
|
|
|
this.idleConnectionCount = new AtomicInteger();
|
|
|
|
this.awaitingConnection = new AtomicInteger();
|
|
|
|
this.backgroundFillQueued = new AtomicBoolean();
|
|
|
|
this.idleConnections = new LinkedBlockingQueue<IHikariConnectionProxy>(configuration.getMaximumPoolSize());
|
|
|
|
|
|
|
|
this.jdbc4ConnectionTest = configuration.isJdbc4ConnectionTest();
|
|
|
|
this.leakDetectionThreshold = configuration.getLeakDetectionThreshold();
|
|
|
|
this.isAutoCommit = configuration.isAutoCommit();
|
|
|
|
this.isRegisteredMbeans = configuration.isRegisterMbeans();
|
|
|
|
this.transactionIsolation = configuration.getTransactionIsolation();
|
|
|
|
this.debug = LOGGER.isDebugEnabled();
|
|
|
|
|
|
|
|
if (configuration.getDataSource() == null)
|
|
|
|
{
|
|
|
|
String dsClassName = configuration.getDataSourceClassName();
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Class<?> clazz = this.getClass().getClassLoader().loadClass(dsClassName);
|
|
|
|
this.dataSource = (DataSource) clazz.newInstance();
|
|
|
|
PropertyBeanSetter.setTargetFromProperties(dataSource, configuration.getDataSourceProperties());
|
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
throw new RuntimeException("Could not create datasource instance: " + dsClassName, e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.dataSource = configuration.getDataSource();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (configuration.getConnectionCustomizerClassName() != null)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Class<?> clazz = this.getClass().getClassLoader().loadClass(configuration.getConnectionCustomizerClassName());
|
|
|
|
this.connectionCustomizer = (IConnectionCustomizer) clazz.newInstance();
|
|
|
|
}
|
|
|
|
catch (ClassNotFoundException | InstantiationException | IllegalAccessException e)
|
|
|
|
{
|
|
|
|
throw new RuntimeException("Could not load connection customization class", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.connectionCustomizer = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isRegisteredMbeans)
|
|
|
|
{
|
|
|
|
HikariMBeanElf.registerMBeans(configuration, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
houseKeepingTimer = new Timer("Hikari Housekeeping Timer", true);
|
|
|
|
|
|
|
|
fillPool();
|
|
|
|
|
|
|
|
long idleTimeout = configuration.getIdleTimeout();
|
|
|
|
if (idleTimeout > 0 || configuration.getMaxLifetime() > 0)
|
|
|
|
{
|
|
|
|
long delayPeriod = Long.getLong("com.zaxxer.hikari.housekeeping.period", TimeUnit.SECONDS.toMillis(30));
|
|
|
|
houseKeepingTimer.scheduleAtFixedRate(new HouseKeeper(), delayPeriod, delayPeriod);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a connection from the pool, or timeout trying.
|
|
|
|
*
|
|
|
|
* @return a java.sql.Connection instance
|
|
|
|
* @throws SQLException thrown if a timeout occurs trying to obtain a connection
|
|
|
|
*/
|
|
|
|
Connection getConnection() throws SQLException
|
|
|
|
{
|
|
|
|
if (shutdown)
|
|
|
|
{
|
|
|
|
throw new SQLException("Pool has been shutdown");
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
if (isRegisteredMbeans)
|
|
|
|
{
|
|
|
|
awaitingConnection.incrementAndGet();
|
|
|
|
}
|
|
|
|
|
|
|
|
long timeout = configuration.getConnectionTimeout();
|
|
|
|
final long start = System.currentTimeMillis();
|
|
|
|
do
|
|
|
|
{
|
|
|
|
addConnections(AddConnectionStrategy.ONLY_IF_EMPTY);
|
|
|
|
|
|
|
|
IHikariConnectionProxy connectionProxy = idleConnections.poll(timeout, TimeUnit.MILLISECONDS);
|
|
|
|
if (connectionProxy == null)
|
|
|
|
{
|
|
|
|
// We timed out... break and throw exception
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
idleConnectionCount.decrementAndGet();
|
|
|
|
|
|
|
|
connectionProxy.unclose();
|
|
|
|
|
|
|
|
if (!isConnectionAlive(connectionProxy, timeout))
|
|
|
|
{
|
|
|
|
// Throw away the dead connection, try again
|
|
|
|
closeConnection(connectionProxy);
|
|
|
|
timeout -= (System.currentTimeMillis() - start);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (leakDetectionThreshold > 0)
|
|
|
|
{
|
|
|
|
connectionProxy.captureStack(leakDetectionThreshold, houseKeepingTimer);
|
|
|
|
}
|
|
|
|
|
|
|
|
connectionProxy.clearWarnings();
|
|
|
|
|
|
|
|
return connectionProxy;
|
|
|
|
|
|
|
|
} while (timeout > 0);
|
|
|
|
|
|
|
|
logPoolState();
|
|
|
|
|
|
|
|
String msg = String.format("Timeout of %dms encountered waiting for connection.", configuration.getConnectionTimeout());
|
|
|
|
LOGGER.error(msg);
|
|
|
|
|
|
|
|
throw new SQLException(msg);
|
|
|
|
}
|
|
|
|
catch (InterruptedException e)
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
finally
|
|
|
|
{
|
|
|
|
if (isRegisteredMbeans)
|
|
|
|
{
|
|
|
|
awaitingConnection.decrementAndGet();
|
|
|
|
}
|
|
|
|
|
|
|
|
addConnections(AddConnectionStrategy.BACKGROUND_FILL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Release a connection back to the pool, or permanently close it if it
|
|
|
|
* is broken.
|
|
|
|
*
|
|
|
|
* @param connectionProxy the connection to release back to the pool
|
|
|
|
*/
|
|
|
|
public void releaseConnection(IHikariConnectionProxy connectionProxy)
|
|
|
|
{
|
|
|
|
if (!connectionProxy.isBrokenConnection() && !shutdown)
|
|
|
|
{
|
|
|
|
idleConnectionCount.incrementAndGet();
|
|
|
|
if (!idleConnections.offer(connectionProxy))
|
|
|
|
{
|
|
|
|
closeConnection(connectionProxy);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LOGGER.debug("Connection returned to pool is broken, or the pool is shutting down. Closing connection.");
|
|
|
|
closeConnection(connectionProxy);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void shutdown()
|
|
|
|
{
|
|
|
|
shutdown = true;
|
|
|
|
houseKeepingTimer.cancel();
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
IHikariConnectionProxy connection = idleConnections.poll();
|
|
|
|
if (connection == null)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
closeConnection(connection);
|
|
|
|
}
|
|
|
|
|
|
|
|
HikariMBeanElf.unregisterMBeans(configuration, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
// ***********************************************************************
|
|
|
|
// HikariPoolMBean methods
|
|
|
|
// ***********************************************************************
|
|
|
|
|
|
|
|
/** {@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();
|
|
|
|
}
|
|
|
|
|
|
|
|
/** {@inheritDoc} */
|
|
|
|
public int getThreadsAwaitingConnection()
|
|
|
|
{
|
|
|
|
return awaitingConnection.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
/** {@inheritDoc} */
|
|
|
|
public void closeIdleConnections()
|
|
|
|
{
|
|
|
|
final int idleCount = idleConnectionCount.get();
|
|
|
|
for (int i = 0; i < idleCount; i++)
|
|
|
|
{
|
|
|
|
IHikariConnectionProxy connectionProxy = idleConnections.poll();
|
|
|
|
if (connectionProxy == null)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
idleConnectionCount.decrementAndGet();
|
|
|
|
|
|
|
|
closeConnection(connectionProxy);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ***********************************************************************
|
|
|
|
// Private methods
|
|
|
|
// ***********************************************************************
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fill the pool up to the minimum size.
|
|
|
|
*/
|
|
|
|
private void fillPool()
|
|
|
|
{
|
|
|
|
// maxIters avoids an infinite loop filling the pool if no connections can be acquired
|
|
|
|
int maxIters = configuration.getMinimumPoolSize() * configuration.getAcquireRetries();
|
|
|
|
while (totalConnections.get() < configuration.getMinimumPoolSize() && maxIters-- > 0)
|
|
|
|
{
|
|
|
|
int beforeCount = totalConnections.get();
|
|
|
|
addConnection();
|
|
|
|
if (configuration.isInitializationFailFast() && beforeCount == totalConnections.get())
|
|
|
|
{
|
|
|
|
throw new RuntimeException("Fail-fast during pool initialization");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
logPoolState("Initial fill ");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add connections to the pool, not exceeding the maximum allowed.
|
|
|
|
*/
|
|
|
|
private void addConnections(AddConnectionStrategy strategy)
|
|
|
|
{
|
|
|
|
switch (strategy)
|
|
|
|
{
|
|
|
|
case ONLY_IF_EMPTY:
|
|
|
|
if (idleConnectionCount.get() == 0)
|
|
|
|
{
|
|
|
|
final int max = configuration.getMaximumPoolSize();
|
|
|
|
final int increment = configuration.getAcquireIncrement();
|
|
|
|
for (int i = 0; idleConnectionCount.get() < increment && i < increment && totalConnections.get() < max; i++)
|
|
|
|
{
|
|
|
|
addConnection();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case MAINTAIN_MINIMUM:
|
|
|
|
final int min = configuration.getMinimumPoolSize();
|
|
|
|
final int max = configuration.getMaximumPoolSize();
|
|
|
|
final int increment = configuration.getAcquireIncrement();
|
|
|
|
for (int i = 0; totalConnections.get() < min && i < increment && totalConnections.get() < max; i++)
|
|
|
|
{
|
|
|
|
addConnection();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BACKGROUND_FILL:
|
|
|
|
if (idleConnectionCount.get() == 0 && backgroundFillQueued.compareAndSet(false, true))
|
|
|
|
{
|
|
|
|
houseKeepingTimer.schedule(new TimerTask() {
|
|
|
|
public void run()
|
|
|
|
{
|
|
|
|
final int max = configuration.getMaximumPoolSize();
|
|
|
|
final int increment = configuration.getAcquireIncrement();
|
|
|
|
while ((idleConnectionCount.get() < increment || awaitingConnection.get() > 0) && totalConnections.get() < max)
|
|
|
|
{
|
|
|
|
addConnection();
|
|
|
|
}
|
|
|
|
backgroundFillQueued.set(false);
|
|
|
|
}
|
|
|
|
}, 100/*ms*/);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create and add a single connection to the pool.
|
|
|
|
*/
|
|
|
|
private void addConnection()
|
|
|
|
{
|
|
|
|
int retries = 0;
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Connection connection = dataSource.getConnection();
|
|
|
|
|
|
|
|
if (transactionIsolation < 0)
|
|
|
|
{
|
|
|
|
transactionIsolation = connection.getTransactionIsolation();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (connectionCustomizer != null)
|
|
|
|
{
|
|
|
|
connectionCustomizer.customize(connection);
|
|
|
|
}
|
|
|
|
|
|
|
|
IHikariConnectionProxy proxyConnection = (IHikariConnectionProxy) ProxyFactory.getProxyConnection(this, connection, transactionIsolation);
|
|
|
|
|
|
|
|
String initSql = configuration.getConnectionInitSql();
|
|
|
|
if (initSql != null && initSql.length() > 0)
|
|
|
|
{
|
|
|
|
connection.setAutoCommit(true);
|
|
|
|
try (Statement statement = connection.createStatement())
|
|
|
|
{
|
|
|
|
statement.execute(initSql);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!shutdown)
|
|
|
|
{
|
|
|
|
idleConnectionCount.incrementAndGet();
|
|
|
|
totalConnections.incrementAndGet();
|
|
|
|
idleConnections.add(proxyConnection);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
if (retries++ > configuration.getAcquireRetries())
|
|
|
|
{
|
|
|
|
if (debug)
|
|
|
|
{
|
|
|
|
LOGGER.error("Maximum connection creation retries exceeded: {}", e.getMessage(), e);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LOGGER.error("Maximum connection creation retries exceeded: {}", e.getMessage());
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Thread.sleep(configuration.getAcquireRetryDelay());
|
|
|
|
}
|
|
|
|
catch (InterruptedException e1)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check whether the connection is alive or not.
|
|
|
|
*
|
|
|
|
* @param connection the connection to test
|
|
|
|
* @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)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
connection.setAutoCommit(isAutoCommit);
|
|
|
|
if (connection.isTransactionIsolationDirty())
|
|
|
|
{
|
|
|
|
connection.setTransactionIsolation(transactionIsolation);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the connection was used less than a second ago, short-circuit the alive test
|
|
|
|
if (System.currentTimeMillis() - connection.getLastAccess() < 1000)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
if (timeoutMs < 1000)
|
|
|
|
{
|
|
|
|
timeoutMs = 1000;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (jdbc4ConnectionTest)
|
|
|
|
{
|
|
|
|
return connection.isValid((int) TimeUnit.MILLISECONDS.toSeconds(timeoutMs));
|
|
|
|
}
|
|
|
|
|
|
|
|
try (Statement statement = connection.createStatement())
|
|
|
|
{
|
|
|
|
statement.setQueryTimeout((int) TimeUnit.MILLISECONDS.toSeconds(timeoutMs));
|
|
|
|
statement.executeQuery(configuration.getConnectionTestQuery());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
finally
|
|
|
|
{
|
|
|
|
if (!isAutoCommit)
|
|
|
|
{
|
|
|
|
connection.commit();
|
|
|
|
}
|
|
|
|
|
|
|
|
connection.resetTransactionIsolationDirty();
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
catch (SQLException e)
|
|
|
|
{
|
|
|
|
LOGGER.warn("Exception during keep alive check, that means the connection must be dead.", e);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Permanently close a connection.
|
|
|
|
*
|
|
|
|
* @param connectionProxy the connection to actually close
|
|
|
|
*/
|
|
|
|
private void closeConnection(IHikariConnectionProxy connectionProxy)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
totalConnections.decrementAndGet();
|
|
|
|
connectionProxy.realClose();
|
|
|
|
}
|
|
|
|
catch (SQLException e)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void logPoolState(String... prefix)
|
|
|
|
{
|
|
|
|
int total = totalConnections.get();
|
|
|
|
int idle = idleConnectionCount.get();
|
|
|
|
LOGGER.debug("{}Pool stats (total={}, inUse={}, avail={}, waiting={})",
|
|
|
|
(prefix.length > 0 ? prefix[0] : ""), total, total - idle, idle, (isRegisteredMbeans ? awaitingConnection.get() : "n/a"));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The house keeping task to retire idle and maxAge connections.
|
|
|
|
*/
|
|
|
|
private class HouseKeeper extends TimerTask
|
|
|
|
{
|
|
|
|
public void run()
|
|
|
|
{
|
|
|
|
debug = LOGGER.isDebugEnabled();
|
|
|
|
houseKeepingTimer.purge();
|
|
|
|
|
|
|
|
logPoolState("Before pool cleanup ");
|
|
|
|
|
|
|
|
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++)
|
|
|
|
{
|
|
|
|
IHikariConnectionProxy connectionProxy = idleConnections.poll();
|
|
|
|
if (connectionProxy == null)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
idleConnectionCount.decrementAndGet();
|
|
|
|
|
|
|
|
if ((idleTimeout > 0 && now > connectionProxy.getLastAccess() + idleTimeout)
|
|
|
|
||
|
|
|
|
(maxLifetime > 0 && now > connectionProxy.getCreationTime() + maxLifetime))
|
|
|
|
{
|
|
|
|
closeConnection(connectionProxy);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
idleConnectionCount.incrementAndGet();
|
|
|
|
idleConnections.add(connectionProxy);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
addConnections(AddConnectionStrategy.MAINTAIN_MINIMUM);
|
|
|
|
|
|
|
|
logPoolState("After pool cleanup ");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static enum AddConnectionStrategy
|
|
|
|
{
|
|
|
|
ONLY_IF_EMPTY,
|
|
|
|
BACKGROUND_FILL,
|
|
|
|
MAINTAIN_MINIMUM
|
|
|
|
}
|
|
|
|
}
|