diff --git a/src/main/java/com/zaxxer/hikari/HikariDataSource.java b/src/main/java/com/zaxxer/hikari/HikariDataSource.java index 6bbe7c33..e55a8524 100644 --- a/src/main/java/com/zaxxer/hikari/HikariDataSource.java +++ b/src/main/java/com/zaxxer/hikari/HikariDataSource.java @@ -40,7 +40,9 @@ public class HikariDataSource extends HikariConfig implements DataSource { private static final Logger LOGGER = LoggerFactory.getLogger(HikariDataSource.class); + // We use a concrete HashMap rather than Map to avoid an invokeinterface callsite private final HashMap multiPool; + private volatile boolean isShutdown; private int loginTimeout; @@ -190,7 +192,7 @@ public class HikariDataSource extends HikariConfig implements DataSource @Override public boolean isWrapperFor(Class iface) throws SQLException { - return (pool != null & pool.getDataSource().getClass().isAssignableFrom(iface)); + return (pool != null && pool.getDataSource().getClass().isAssignableFrom(iface)); } /** @@ -228,41 +230,14 @@ public class HikariDataSource extends HikariConfig implements DataSource isShutdown = true; - if (pool != null) + if (fastPathPool != null) { - try - { - pool.shutdown(); - } - catch (InterruptedException e) - { - LoggerFactory.getLogger(getClass()).warn("Interrupted during shutdown", e); - } - - if (pool.getDataSource() instanceof DriverDataSource) - { - ((DriverDataSource) pool.getDataSource()).shutdown(); - } + shutdownHelper(fastPathPool); } - if (!multiPool.isEmpty()) + for (HikariPool hikariPool : multiPool.values()) { - for (HikariPool hikariPool : multiPool.values()) - { - try - { - hikariPool.shutdown(); - } - catch (InterruptedException e) - { - LoggerFactory.getLogger(getClass()).warn("Interrupted during shutdown", e); - } - - if (hikariPool.getDataSource() instanceof DriverDataSource) - { - ((DriverDataSource) hikariPool.getDataSource()).shutdown(); - } - } + shutdownHelper(hikariPool); } } @@ -273,6 +248,23 @@ public class HikariDataSource extends HikariConfig implements DataSource return String.format("HikariDataSource (%s)", pool); } + private void shutdownHelper(HikariPool hPool) + { + try + { + hPool.shutdown(); + } + catch (InterruptedException e) + { + LoggerFactory.getLogger(getClass()).warn("Interrupted during shutdown", e); + } + + if (hPool.getDataSource() instanceof DriverDataSource) + { + ((DriverDataSource) hPool.getDataSource()).shutdown(); + } + } + private static class MultiPoolKey { private String username; diff --git a/src/main/java/com/zaxxer/hikari/pool/HikariPool.java b/src/main/java/com/zaxxer/hikari/pool/HikariPool.java index 260ca960..b83f6fff 100644 --- a/src/main/java/com/zaxxer/hikari/pool/HikariPool.java +++ b/src/main/java/com/zaxxer/hikari/pool/HikariPool.java @@ -42,9 +42,16 @@ import com.zaxxer.hikari.proxy.ProxyFactory; import com.zaxxer.hikari.util.ConcurrentBag; import com.zaxxer.hikari.util.ConcurrentBag.IBagStateListener; import com.zaxxer.hikari.util.DriverDataSource; -import com.zaxxer.hikari.util.PoolUtilities; import com.zaxxer.hikari.util.PropertyBeanSetter; +import static com.zaxxer.hikari.util.PoolUtilities.elapsedTimeMs; +import static com.zaxxer.hikari.util.PoolUtilities.createInstance; +import static com.zaxxer.hikari.util.PoolUtilities.createThreadPoolExecutor; +import static com.zaxxer.hikari.util.PoolUtilities.executeSqlAutoCommit; +import static com.zaxxer.hikari.util.PoolUtilities.quietlySleep; +import static com.zaxxer.hikari.util.PoolUtilities.quietlyCloseConnection; +import static com.zaxxer.hikari.util.PoolUtilities.IS_JAVA7; + /** * This is the primary connection pool class that provides the basic * pooling behavior for HikariCP. @@ -127,7 +134,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener HikariMBeanElf.registerMBeans(configuration, this); } - addConnectionExecutor = PoolUtilities.createThreadPoolExecutor(configuration.getMaximumPoolSize(), "HikariCP connection filler"); + addConnectionExecutor = createThreadPoolExecutor(configuration.getMaximumPoolSize(), "HikariCP connection filler"); fillPool(); @@ -149,16 +156,16 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener { final long start = System.currentTimeMillis(); final Context context = (isRecordMetrics ? metricsTracker.recordConnectionRequest(start) : MetricsTracker.NO_CONTEXT); - long timeout = configuration.getConnectionTimeout(); + try { do { IHikariConnectionProxy connection = connectionBag.borrow(timeout, TimeUnit.MILLISECONDS); - if (connection == null) // We timed out... break and throw exception + if (connection == null) { - break; + break; // We timed out... break and throw exception } final long now = System.currentTimeMillis(); @@ -166,8 +173,8 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener if (now > connection.getExpirationTime() || (now - connection.getLastAccess() > 1000 && !isConnectionAlive(connection, timeout))) { - closeConnection(connection); // Throw away the dead connection, try again - timeout -= PoolUtilities.elapsedTimeMs(start); + closeConnection(connection); // Throw away the dead connection and try again + timeout -= elapsedTimeMs(start); continue; } else if (leakDetectionThreshold != 0) @@ -178,19 +185,19 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener return connection; } while (timeout > 0); - - logPoolState("Timeout failure "); - throw new SQLException(String.format("Timeout of %dms encountered waiting for connection.", - configuration.getConnectionTimeout()), lastConnectionFailure.getAndSet(null)); } catch (InterruptedException e) { - return null; + throw new SQLException("Interrupted during connection acquisition", e); } finally { context.stop(); } + + logPoolState("Timeout failure "); + throw new SQLException(String.format("Timeout of %dms encountered waiting for connection.", + configuration.getConnectionTimeout()), lastConnectionFailure.getAndSet(null)); } /** @@ -203,28 +210,33 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener { if (isRecordMetrics) { - metricsTracker.recordConnectionUsage(PoolUtilities.elapsedTimeMs(connectionProxy.getLastOpenTime())); + metricsTracker.recordConnectionUsage(elapsedTimeMs(connectionProxy.getLastOpenTime())); } if (isBroken || isShutdown) { LOGGER.debug("Connection returned to pool {} is broken, or the pool is shutting down. Closing connection.", configuration.getPoolName()); closeConnection(connectionProxy); + return; } - else - { - connectionBag.requite(connectionProxy); - } + + connectionBag.requite(connectionProxy); } + /** + * Shutdown the pool, closing all idle connections and aborting or closing + * active connections. + * + * @throws InterruptedException thrown if the thread is interrupted during shutdown + */ public void shutdown() throws InterruptedException { if (!isShutdown) { isShutdown = true; LOGGER.info("HikariCP pool {} is shutting down.", configuration.getPoolName()); - logPoolState("State at shutdown "); + logPoolState("Before shutdown "); houseKeepingTimer.cancel(); addConnectionExecutor.shutdownNow(); @@ -234,9 +246,9 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener closeIdleConnections(); abortActiveConnections(); } - while ((getIdleConnections() > 0 || getActiveConnections() > 0 ) && PoolUtilities.elapsedTimeMs(start) < TimeUnit.SECONDS.toMillis(5)); + while ((getIdleConnections() > 0 || getActiveConnections() > 0 ) && elapsedTimeMs(start) < TimeUnit.SECONDS.toMillis(5)); - logPoolState("State after shutdown "); + logPoolState("After shutdown "); if (isRegisteredMbeans) { @@ -245,13 +257,18 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener } } + /** + * Get the wrapped DataSource. + * + * @return the wrapped DataSource + */ public DataSource getDataSource() { return dataSource; } /** - * Permanently close a connection. + * Permanently close the real (underlying) connection (eat any exception). * * @param connectionProxy the connection to actually close */ @@ -283,7 +300,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener } // *********************************************************************** - // IBagStateListener methods + // IBagStateListener callback // *********************************************************************** /** {@inheritDoc} */ @@ -293,21 +310,21 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener class AddConnection implements Runnable { public void run() { - int sleepBackoff = 200; + long sleepBackoff = 200; final int maxPoolSize = configuration.getMaximumPoolSize(); final int minIdle = configuration.getMinimumIdle(); while (!isShutdown && totalConnections.get() < maxPoolSize && (minIdle == 0 || getIdleConnections() < minIdle)) { if (!addConnection()) { - PoolUtilities.quietlySleep(sleepBackoff); - sleepBackoff = (int) Math.min(1000f, ((float) sleepBackoff) * 1.5); + quietlySleep(sleepBackoff); + sleepBackoff = (long) Math.min(1000f, ((float) sleepBackoff) * 1.5); continue; } - if (minIdle == 0) // This break is here so we only add one connection when demanded + if (minIdle == 0) { - break; + break; // This break is here so we only add one connection when there is no min. idle } } } @@ -385,7 +402,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener transactionIsolation = (transactionIsolation < 0 ? connection.getTransactionIsolation() : transactionIsolation); connectionCustomizer.customize(connection); - PoolUtilities.executeSqlAutoCommit(connection, configuration.getConnectionInitSql()); + executeSqlAutoCommit(connection, configuration.getConnectionInitSql()); IHikariConnectionProxy proxyConnection = ProxyFactory.getProxyConnection(this, connection, configuration.getMaxLifetime(), transactionIsolation, isAutoCommit, isReadOnly, catalog); @@ -400,9 +417,8 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener totalConnections.decrementAndGet(); lastConnectionFailure.set(e); - PoolUtilities.quietlyCloseConnection(connection); + quietlyCloseConnection(connection); LOGGER.debug("Connection attempt to database {} failed: {}", configuration.getPoolName(), e.getMessage(), e); - return false; } } @@ -424,29 +440,26 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener { return connection.isValid((int) TimeUnit.MILLISECONDS.toSeconds(timeoutMs)); } - else + + Statement statement = connection.createStatement(); + try { - Statement statement = connection.createStatement(); - try + if (configuration.getConnectionTimeout() < Integer.MAX_VALUE) { - if (configuration.getConnectionTimeout() < Integer.MAX_VALUE) - { - statement.setQueryTimeout((int) TimeUnit.MILLISECONDS.toSeconds(timeoutMs)); - } - statement.executeQuery(configuration.getConnectionTestQuery()); + statement.setQueryTimeout((int) TimeUnit.MILLISECONDS.toSeconds(timeoutMs)); } - finally - { - statement.close(); - } - + statement.executeQuery(configuration.getConnectionTestQuery()); + } + finally + { + statement.close(); if (isIsolateInternalQueries && !isAutoCommit) { connection.rollback(); } - - return true; } + + return true; } catch (SQLException e) { @@ -483,19 +496,18 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener */ private void abortActiveConnections() throws InterruptedException { - ThreadPoolExecutor assassinExecutor = PoolUtilities.createThreadPoolExecutor(1, "HikariCP connection assassin"); + ThreadPoolExecutor assassinExecutor = createThreadPoolExecutor(1, "HikariCP connection assassin"); for (IHikariConnectionProxy connectionProxy : connectionBag.values(ConcurrentBag.STATE_IN_USE)) { try { - if (PoolUtilities.IS_JAVA7) + if (IS_JAVA7) { connectionProxy.abort(assassinExecutor); + continue; } - else - { - connectionProxy.close(); - } + + connectionProxy.close(); } catch (SQLException e) { @@ -531,7 +543,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener { try { - DataSource dataSource = PoolUtilities.createInstance(dsClassName, DataSource.class); + DataSource dataSource = createInstance(dsClassName, DataSource.class); PropertyBeanSetter.setTargetFromProperties(dataSource, configuration.getDataSourceProperties()); return dataSource; } @@ -554,7 +566,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener { try { - return PoolUtilities.createInstance(configuration.getConnectionCustomizerClassName(), IConnectionCustomizer.class); + return createInstance(configuration.getConnectionCustomizerClassName(), IConnectionCustomizer.class); } catch (Exception e) { @@ -569,7 +581,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener { int total = totalConnections.get(); int idle = getIdleConnections(); - LOGGER.debug("{}Pool stats {} (total={}, inUse={}, avail={}, waiting={})", (prefix.length > 0 ? prefix[0] : ""), + LOGGER.debug("{}pool stats {} (total={}, inUse={}, avail={}, waiting={})", (prefix.length > 0 ? prefix[0] : ""), configuration.getPoolName(), total, total - idle, idle, getThreadsAwaitingConnection()); } @@ -581,10 +593,9 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener @Override public void run() { + logPoolState("Before cleanup "); houseKeepingTimer.purge(); - logPoolState("Before pool cleanup "); - final long now = System.currentTimeMillis(); final long idleTimeout = configuration.getIdleTimeout(); @@ -604,12 +615,9 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener } } - logPoolState("After pool cleanup "); + logPoolState("After cleanup "); - if (getIdleConnections() < configuration.getMinimumIdle() && totalConnections.get() < configuration.getMaximumPoolSize()) - { - addBagItem(); // TRY to maintain minimum connections - } + addBagItem(); // Try to maintain minimum connections } } } diff --git a/src/main/java/com/zaxxer/hikari/proxy/ConnectionProxy.java b/src/main/java/com/zaxxer/hikari/proxy/ConnectionProxy.java index ce3034de..f4e3988c 100644 --- a/src/main/java/com/zaxxer/hikari/proxy/ConnectionProxy.java +++ b/src/main/java/com/zaxxer/hikari/proxy/ConnectionProxy.java @@ -64,7 +64,6 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy private volatile long lastAccess; private long uncloseTime; - private StackTraceElement[] leakTrace; private TimerTask leakTask; private final int hashCode; @@ -126,7 +125,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy public final void captureStack(long leakDetectionThreshold, Timer scheduler) { StackTraceElement[] trace = Thread.currentThread().getStackTrace(); - leakTrace = new StackTraceElement[trace.length - 4]; + StackTraceElement[] leakTrace = new StackTraceElement[trace.length - 4]; System.arraycopy(trace, 4, leakTrace, 0, leakTrace.length); leakTask = new LeakTask(leakTrace, leakDetectionThreshold); diff --git a/src/main/java/com/zaxxer/hikari/util/ConcurrentBag.java b/src/main/java/com/zaxxer/hikari/util/ConcurrentBag.java index 165e2845..907ee306 100644 --- a/src/main/java/com/zaxxer/hikari/util/ConcurrentBag.java +++ b/src/main/java/com/zaxxer/hikari/util/ConcurrentBag.java @@ -322,18 +322,20 @@ public class ConcurrentBag[0]) != null; + b = AbstractQueuedLongSynchronizer.class.getMethod("hasQueuedPredecessors", new Class[0]) != null; } catch (Exception e) { - // nothing } + + JAVA7 = b; } @Override diff --git a/src/main/java/com/zaxxer/hikari/util/FastList.java b/src/main/java/com/zaxxer/hikari/util/FastList.java index b230d29a..ccb8cdde 100644 --- a/src/main/java/com/zaxxer/hikari/util/FastList.java +++ b/src/main/java/com/zaxxer/hikari/util/FastList.java @@ -27,7 +27,6 @@ import java.lang.reflect.Array; public final class FastList { private T[] elementData; - private int size; /** @@ -60,8 +59,7 @@ public final class FastList { try { - elementData[size] = element; - size++; + elementData[size++] = element; } catch (ArrayIndexOutOfBoundsException e) { @@ -71,7 +69,7 @@ public final class FastList @SuppressWarnings("unchecked") final T[] newElementData = (T[]) Array.newInstance(element.getClass(), newCapacity); System.arraycopy(elementData, 0, newElementData, 0, oldCapacity); - newElementData[size++] = element; + newElementData[size - 1] = element; elementData = newElementData; } } diff --git a/src/main/java/com/zaxxer/hikari/util/PoolUtilities.java b/src/main/java/com/zaxxer/hikari/util/PoolUtilities.java index 2ed84e28..41ef42ee 100644 --- a/src/main/java/com/zaxxer/hikari/util/PoolUtilities.java +++ b/src/main/java/com/zaxxer/hikari/util/PoolUtilities.java @@ -11,18 +11,20 @@ import java.util.concurrent.locks.AbstractQueuedLongSynchronizer; public final class PoolUtilities { - public static boolean IS_JAVA7; + public static final boolean IS_JAVA7; static { + boolean b = false; try { - IS_JAVA7 = AbstractQueuedLongSynchronizer.class.getMethod("hasQueuedPredecessors", new Class[0]) != null; + b = AbstractQueuedLongSynchronizer.class.getMethod("hasQueuedPredecessors", new Class[0]) != null; } catch (Exception e) { - IS_JAVA7 = false; } + + IS_JAVA7 = b; } public static void quietlyCloseConnection(Connection connection) @@ -119,7 +121,7 @@ public final class PoolUtilities int processors = Math.max(1, Runtime.getRuntime().availableProcessors() / 2); LinkedBlockingQueue queue = new LinkedBlockingQueue(queueSize); - ThreadPoolExecutor executor = new ThreadPoolExecutor(processors, processors, 10, TimeUnit.SECONDS, queue, threadFactory, new ThreadPoolExecutor.DiscardPolicy()); + ThreadPoolExecutor executor = new ThreadPoolExecutor(processors, processors, 2, TimeUnit.SECONDS, queue, threadFactory, new ThreadPoolExecutor.DiscardPolicy()); executor.allowCoreThreadTimeOut(true); return executor; } diff --git a/src/main/java/com/zaxxer/hikari/util/PropertyBeanSetter.java b/src/main/java/com/zaxxer/hikari/util/PropertyBeanSetter.java index 845f6bf9..9823261f 100644 --- a/src/main/java/com/zaxxer/hikari/util/PropertyBeanSetter.java +++ b/src/main/java/com/zaxxer/hikari/util/PropertyBeanSetter.java @@ -22,6 +22,7 @@ import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; import java.util.HashSet; +import java.util.Map.Entry; import java.util.Properties; import java.util.Set; @@ -46,10 +47,10 @@ public final class PropertyBeanSetter return; } - for (Object propKey : properties.keySet()) + for (Entry propEntry : properties.entrySet()) { - String propName = propKey.toString(); - Object propValue = properties.get(propKey); + String propName = propEntry.getKey().toString(); + Object propValue = propEntry.getValue(); if (target instanceof HikariConfig && propName.startsWith("dataSource.")) {