From 4b796b520984b66d26465bdb4852cac17777f162 Mon Sep 17 00:00:00 2001 From: Brett Wooldridge Date: Wed, 25 Oct 2017 01:59:20 +0900 Subject: [PATCH] Clean up and better JavaDoc. --- .../java/com/zaxxer/hikari/HikariConfig.java | 488 ++++++++++-------- .../com/zaxxer/hikari/pool/HikariPool.java | 157 ++++-- 2 files changed, 391 insertions(+), 254 deletions(-) diff --git a/src/main/java/com/zaxxer/hikari/HikariConfig.java b/src/main/java/com/zaxxer/hikari/HikariConfig.java index 8dac93ca..af907dc3 100755 --- a/src/main/java/com/zaxxer/hikari/HikariConfig.java +++ b/src/main/java/com/zaxxer/hikari/HikariConfig.java @@ -58,7 +58,7 @@ public class HikariConfig implements HikariConfigMXBean private static boolean unitTest = false; - // Properties changeable at runtime through the MBean + // Properties changeable at runtime through the HikariConfigMXBean // private volatile long connectionTimeout; private volatile long validationTimeout; @@ -67,6 +67,8 @@ public class HikariConfig implements HikariConfigMXBean private volatile long maxLifetime; private volatile int maxPoolSize; private volatile int minIdle; + private volatile String username; + private volatile String password; // Properties NOT changeable at runtime // @@ -78,11 +80,9 @@ public class HikariConfig implements HikariConfigMXBean private String dataSourceJndiName; private String driverClassName; private String jdbcUrl; - private String password; private String poolName; private String schema; private String transactionIsolationName; - private String username; private boolean isAutoCommit; private boolean isReadOnly; private boolean isIsolateInternalQueries; @@ -145,6 +145,173 @@ public class HikariConfig implements HikariConfigMXBean loadProperties(propertyFileName); } + // *********************************************************************** + // HikariConfigMXBean methods + // *********************************************************************** + + /** {@inheritDoc} */ + @Override + public long getConnectionTimeout() + { + return connectionTimeout; + } + + /** {@inheritDoc} */ + @Override + public void setConnectionTimeout(long connectionTimeoutMs) + { + if (connectionTimeoutMs == 0) { + this.connectionTimeout = Integer.MAX_VALUE; + } + else if (connectionTimeoutMs < 250) { + throw new IllegalArgumentException("connectionTimeout cannot be less than 250ms"); + } + else { + this.connectionTimeout = connectionTimeoutMs; + } + } + + /** {@inheritDoc} */ + @Override + public long getIdleTimeout() + { + return idleTimeout; + } + + /** {@inheritDoc} */ + @Override + public void setIdleTimeout(long idleTimeoutMs) + { + if (idleTimeoutMs < 0) { + throw new IllegalArgumentException("idleTimeout cannot be negative"); + } + this.idleTimeout = idleTimeoutMs; + } + + /** {@inheritDoc} */ + @Override + public long getLeakDetectionThreshold() + { + return leakDetectionThreshold; + } + + /** {@inheritDoc} */ + @Override + public void setLeakDetectionThreshold(long leakDetectionThresholdMs) + { + this.leakDetectionThreshold = leakDetectionThresholdMs; + } + + /** {@inheritDoc} */ + @Override + public long getMaxLifetime() + { + return maxLifetime; + } + + /** {@inheritDoc} */ + @Override + public void setMaxLifetime(long maxLifetimeMs) + { + this.maxLifetime = maxLifetimeMs; + } + + /** {@inheritDoc} */ + @Override + public int getMaximumPoolSize() + { + return maxPoolSize; + } + + /** {@inheritDoc} */ + @Override + public void setMaximumPoolSize(int maxPoolSize) + { + if (maxPoolSize < 1) { + throw new IllegalArgumentException("maxPoolSize cannot be less than 1"); + } + this.maxPoolSize = maxPoolSize; + } + + /** {@inheritDoc} */ + @Override + public int getMinimumIdle() + { + return minIdle; + } + + /** {@inheritDoc} */ + @Override + public void setMinimumIdle(int minIdle) + { + if (minIdle < 0) { + throw new IllegalArgumentException("minimumIdle cannot be negative"); + } + this.minIdle = minIdle; + } + + /** + * Get the default password to use for DataSource.getConnection(username, password) calls. + * @return the password + */ + public String getPassword() + { + return password; + } + + /** + * Set the default password to use for DataSource.getConnection(username, password) calls. + * @param password the password + */ + @Override + public void setPassword(String password) + { + this.password = password; + } + + /** + * Get the default username used for DataSource.getConnection(username, password) calls. + * + * @return the username + */ + public String getUsername() + { + return username; + } + + /** + * Set the default username used for DataSource.getConnection(username, password) calls. + * + * @param username the username + */ + @Override + public void setUsername(String username) + { + this.username = username; + } + + /** {@inheritDoc} */ + @Override + public long getValidationTimeout() + { + return validationTimeout; + } + + /** {@inheritDoc} */ + @Override + public void setValidationTimeout(long validationTimeoutMs) + { + if (validationTimeoutMs < 250) { + throw new IllegalArgumentException("validationTimeout cannot be less than 250ms"); + } + + this.validationTimeout = validationTimeoutMs; + } + + // *********************************************************************** + // All other configuration methods + // *********************************************************************** + /** * Get the default catalog name to be set on connections. * @@ -210,46 +377,6 @@ public class HikariConfig implements HikariConfigMXBean this.connectionInitSql = connectionInitSql; } - /** {@inheritDoc} */ - @Override - public long getConnectionTimeout() - { - return connectionTimeout; - } - - /** {@inheritDoc} */ - @Override - public void setConnectionTimeout(long connectionTimeoutMs) - { - if (connectionTimeoutMs == 0) { - this.connectionTimeout = Integer.MAX_VALUE; - } - else if (connectionTimeoutMs < 250) { - throw new IllegalArgumentException("connectionTimeout cannot be less than 250ms"); - } - else { - this.connectionTimeout = connectionTimeoutMs; - } - } - - /** {@inheritDoc} */ - @Override - public long getValidationTimeout() - { - return validationTimeout; - } - - /** {@inheritDoc} */ - @Override - public void setValidationTimeout(long validationTimeoutMs) - { - if (validationTimeoutMs < 250) { - throw new IllegalArgumentException("validationTimeout cannot be less than 250ms"); - } - - this.validationTimeout = validationTimeoutMs; - } - /** * Get the {@link DataSource} that has been explicitly specified to be wrapped by the * pool. @@ -272,16 +399,39 @@ public class HikariConfig implements HikariConfigMXBean this.dataSource = dataSource; } + /** + * Get the name of the JDBC {@link DataSource} class used to create Connections. + * + * @return the fully qualified name of the JDBC {@link DataSource} class + */ public String getDataSourceClassName() { return dataSourceClassName; } + /** + * Set the fully qualified class name of the JDBC {@link DataSource} that will be used create Connections. + * + * @param className the fully qualified name of the JDBC {@link DataSource} class + */ public void setDataSourceClassName(String className) { this.dataSourceClassName = className; } + /** + * Add a property (name/value pair) that will be used to configure the {@link DataSource}/{@link java.sql.Driver}. + * + * In the case of a {@link DataSource}, the property names will be translated to Java setters following the Java Bean + * naming convention. For example, the property {@code cachePrepStmts} will translate into {@code setCachePrepStmts()} + * with the {@code value} passed as a parameter. + * + * In the case of a {@link java.sql.Driver}, the property will be added to a {@link Properties} instance that will + * be passed to the driver during {@link java.sql.Driver#connect(String, Properties)} calls. + * + * @param propertyName the name of the property + * @param value the value to be used by the DataSource/Driver + */ public void addDataSourceProperty(String propertyName, Object value) { dataSourceProperties.put(propertyName, value); @@ -349,23 +499,6 @@ public class HikariConfig implements HikariConfigMXBean } } - /** {@inheritDoc} */ - @Override - public long getIdleTimeout() - { - return idleTimeout; - } - - /** {@inheritDoc} */ - @Override - public void setIdleTimeout(long idleTimeoutMs) - { - if (idleTimeoutMs < 0) { - throw new IllegalArgumentException("idleTimeout cannot be negative"); - } - this.idleTimeout = idleTimeoutMs; - } - public String getJdbcUrl() { return jdbcUrl; @@ -496,11 +629,23 @@ public class HikariConfig implements HikariConfigMXBean initializationFailTimeout = (failFast ? 1 : -1); } + /** + * Determine whether internal pool queries, principally aliveness checks, will be isolated in their own transaction + * (via {@link java.sql.Connection#rollback()). Defaults to {@code false}. + * + * @return {@code true} if internal pool queries are isolated, {@code false} if not + */ public boolean isIsolateInternalQueries() { return isIsolateInternalQueries; } + /** + * Configure whether internal pool queries, principally aliveness checks, will be isolated in their own transaction + * (via {@link java.sql.Connection#rollback()). Defaults to {@code false}. + * + * @param isolate {@code true} if internal pool queries should be isolated, {@code false} if not + */ public void setIsolateInternalQueries(boolean isolate) { this.isIsolateInternalQueries = isolate; @@ -533,9 +678,9 @@ public class HikariConfig implements HikariConfigMXBean } /** - * Get the Codahale MetricRegistry, could be null. + * Get the MetricRegistry instance to used for registration of metrics used by HikariCP. Default is {@code null}. * - * @return the codahale MetricRegistry instance + * @return the MetricRegistry instance that will be used */ public Object getMetricRegistry() { @@ -543,9 +688,9 @@ public class HikariConfig implements HikariConfigMXBean } /** - * Set a Codahale MetricRegistry to use for HikariCP. + * Set a MetricRegistry instance to use for registration of metrics used by HikariCP. * - * @param metricRegistry the Codahale MetricRegistry to set + * @param metricRegistry the MetricRegistry instance to use */ public void setMetricRegistry(Object metricRegistry) { @@ -565,24 +710,11 @@ public class HikariConfig implements HikariConfigMXBean this.metricRegistry = metricRegistry; } - private Object getObjectOrPerformJndiLookup(Object object) - { - if (object instanceof String) { - try { - InitialContext initCtx = new InitialContext(); - return initCtx.lookup((String) object); - } - catch (NamingException e) { - throw new IllegalArgumentException(e); - } - } - return object; - } - /** - * Get the Codahale HealthCheckRegistry, could be null. + * Get the HealthCheckRegistry that will be used for registration of health checks by HikariCP. Currently only + * Codahale/DropWizard is supported for health checks. * - * @return the Codahale HealthCheckRegistry instance + * @return the HealthCheckRegistry instance that will be used */ public Object getHealthCheckRegistry() { @@ -590,9 +722,10 @@ public class HikariConfig implements HikariConfigMXBean } /** - * Set a Codahale HealthCheckRegistry to use for HikariCP. + * Set the HealthCheckRegistry that will be used for registration of health checks by HikariCP. Currently only + * Codahale/DropWizard is supported for health checks. Default is {@code null}. * - * @param healthCheckRegistry the Codahale HealthCheckRegistry to set + * @param healthCheckRegistry the HealthCheckRegistry to be used */ public void setHealthCheckRegistry(Object healthCheckRegistry) { @@ -622,105 +755,45 @@ public class HikariConfig implements HikariConfigMXBean healthCheckProperties.setProperty(key, value); } + /** + * Determine whether the Connections in the pool are in read-only mode. + * + * @return {@code true} if the Connections in the pool are read-only, {@code false} if not + */ public boolean isReadOnly() { return isReadOnly; } + /** + * Configures the Connections to be added to the pool as read-only Connections. + * + * @param readOnly {@code true} if the Connections in the pool are read-only, {@code false} if not + */ public void setReadOnly(boolean readOnly) { this.isReadOnly = readOnly; } - public boolean isRegisterMbeans() - { - return isRegisterMbeans; - } - - public void setRegisterMbeans(boolean register) - { - this.isRegisterMbeans = register; - } - - /** {@inheritDoc} */ - @Override - public long getLeakDetectionThreshold() - { - return leakDetectionThreshold; - } - - /** {@inheritDoc} */ - @Override - public void setLeakDetectionThreshold(long leakDetectionThresholdMs) - { - this.leakDetectionThreshold = leakDetectionThresholdMs; - } - - /** {@inheritDoc} */ - @Override - public long getMaxLifetime() - { - return maxLifetime; - } - - /** {@inheritDoc} */ - @Override - public void setMaxLifetime(long maxLifetimeMs) - { - this.maxLifetime = maxLifetimeMs; - } - - /** {@inheritDoc} */ - @Override - public int getMaximumPoolSize() - { - return maxPoolSize; - } - - /** {@inheritDoc} */ - @Override - public void setMaximumPoolSize(int maxPoolSize) - { - if (maxPoolSize < 1) { - throw new IllegalArgumentException("maxPoolSize cannot be less than 1"); - } - this.maxPoolSize = maxPoolSize; - } - - /** {@inheritDoc} */ - @Override - public int getMinimumIdle() - { - return minIdle; - } - - /** {@inheritDoc} */ - @Override - public void setMinimumIdle(int minIdle) - { - if (minIdle < 0) { - throw new IllegalArgumentException("minimumIdle cannot be negative"); - } - this.minIdle = minIdle; - } - /** - * Get the default password to use for DataSource.getConnection(username, password) calls. - * @return the password + * Determine whether HikariCP will self-register {@link HikariConfigMXBean} and {@link HikariPoolMXBean} instances + * in JMX. + * + * @return {@code true} if HikariCP will register MXBeans, {@code false} if it will not */ - public String getPassword() + public boolean isRegisterMbeans() { - return password; + return isRegisterMbeans; } /** - * Set the default password to use for DataSource.getConnection(username, password) calls. - * @param password the password + * Configures whether HikariCP self-registers the {@link HikariConfigMXBean} and {@link HikariPoolMXBean} in JMX. + * + * @param register {@code true} if HikariCP should register MXBeans, {@code false} if it should not */ - @Override - public void setPassword(String password) + public void setRegisterMbeans(boolean register) { - this.password = password; + this.isRegisterMbeans = register; } /** {@inheritDoc} */ @@ -817,46 +890,63 @@ public class HikariConfig implements HikariConfigMXBean } /** - * Get the default username used for DataSource.getConnection(username, password) calls. + * Get the thread factory used to create threads. * - * @return the username + * @return the thread factory (may be null, in which case the default thread factory is used) */ - public String getUsername() + public ThreadFactory getThreadFactory() { - return username; + return threadFactory; } /** - * Set the default username used for DataSource.getConnection(username, password) calls. + * Set the thread factory to be used to create threads. * - * @param username the username + * @param threadFactory the thread factory (setting to null causes the default thread factory to be used) */ - @Override - public void setUsername(String username) + public void setThreadFactory(ThreadFactory threadFactory) { - this.username = username; + this.threadFactory = threadFactory; } /** - * Get the thread factory used to create threads. + * Deprecated, use {@link #copyStateTo(HikariConfig)}. + *

+ * Copies the state of {@code this} into {@code other}. + *

* - * @return the thread factory (may be null, in which case the default thread factory is used) + * @param other Other {@link HikariConfig} to copy the state to. */ - public ThreadFactory getThreadFactory() + @Deprecated + public void copyState(HikariConfig other) { - return threadFactory; + copyStateTo(other); } /** - * Set the thread factory to be used to create threads. + * Copies the state of {@code this} into {@code other}. * - * @param threadFactory the thread factory (setting to null causes the default thread factory to be used) + * @param other Other {@link HikariConfig} to copy the state to. */ - public void setThreadFactory(ThreadFactory threadFactory) + public void copyStateTo(HikariConfig other) { - this.threadFactory = threadFactory; + for (Field field : HikariConfig.class.getDeclaredFields()) { + if (!Modifier.isFinal(field.getModifiers())) { + field.setAccessible(true); + try { + field.set(other, field.get(this)); + } + catch (Exception e) { + throw new RuntimeException("Failed to copy HikariConfig state: " + e.getMessage(), e); + } + } + } } + // *********************************************************************** + // Private methods + // *********************************************************************** + @SuppressWarnings("StatementWithEmptyBody") public void validate() { @@ -1043,37 +1133,17 @@ public class HikariConfig implements HikariConfigMXBean } } - /** - * Deprecated, use {@link #copyStateTo(HikariConfig)}. - *

- * Copies the state of {@code this} into {@code other}. - *

- * - * @param other Other {@link HikariConfig} to copy the state to. - */ - @Deprecated - public void copyState(HikariConfig other) - { - copyStateTo(other); - } - - /** - * Copies the state of {@code this} into {@code other}. - * - * @param other Other {@link HikariConfig} to copy the state to. - */ - public void copyStateTo(HikariConfig other) + private Object getObjectOrPerformJndiLookup(Object object) { - for (Field field : HikariConfig.class.getDeclaredFields()) { - if (!Modifier.isFinal(field.getModifiers())) { - field.setAccessible(true); - try { - field.set(other, field.get(this)); - } - catch (Exception e) { - throw new RuntimeException("Failed to copy HikariConfig state: " + e.getMessage(), e); - } + if (object instanceof String) { + try { + InitialContext initCtx = new InitialContext(); + return initCtx.lookup((String) object); + } + catch (NamingException e) { + throw new IllegalArgumentException(e); } } + return object; } } diff --git a/src/main/java/com/zaxxer/hikari/pool/HikariPool.java b/src/main/java/com/zaxxer/hikari/pool/HikariPool.java index 833e9c4b..48506dc2 100755 --- a/src/main/java/com/zaxxer/hikari/pool/HikariPool.java +++ b/src/main/java/com/zaxxer/hikari/pool/HikariPool.java @@ -16,17 +16,22 @@ package com.zaxxer.hikari.pool; -import static com.zaxxer.hikari.util.ClockSource.currentTime; -import static com.zaxxer.hikari.util.ClockSource.elapsedDisplayString; -import static com.zaxxer.hikari.util.ClockSource.elapsedMillis; -import static com.zaxxer.hikari.util.ClockSource.plusMillis; -import static com.zaxxer.hikari.util.ConcurrentBag.IConcurrentBagEntry.STATE_IN_USE; -import static com.zaxxer.hikari.util.ConcurrentBag.IConcurrentBagEntry.STATE_NOT_IN_USE; -import static com.zaxxer.hikari.util.UtilityElf.createThreadPoolExecutor; -import static com.zaxxer.hikari.util.UtilityElf.quietlySleep; -import static java.util.Collections.unmodifiableCollection; -import static java.util.concurrent.TimeUnit.MILLISECONDS; -import static java.util.concurrent.TimeUnit.SECONDS; +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.health.HealthCheckRegistry; +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariPoolMXBean; +import com.zaxxer.hikari.metrics.MetricsTrackerFactory; +import com.zaxxer.hikari.metrics.PoolStats; +import com.zaxxer.hikari.metrics.dropwizard.CodahaleHealthChecker; +import com.zaxxer.hikari.metrics.dropwizard.CodahaleMetricsTrackerFactory; +import com.zaxxer.hikari.metrics.micrometer.MicrometerMetricsTrackerFactory; +import com.zaxxer.hikari.util.ConcurrentBag; +import com.zaxxer.hikari.util.ConcurrentBag.IBagStateListener; +import com.zaxxer.hikari.util.SuspendResumeLock; +import com.zaxxer.hikari.util.UtilityElf.DefaultThreadFactory; +import io.micrometer.core.instrument.MeterRegistry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.sql.Connection; import java.sql.SQLException; @@ -35,7 +40,6 @@ import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.concurrent.Callable; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ScheduledExecutorService; @@ -45,23 +49,17 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadPoolExecutor; -import com.zaxxer.hikari.metrics.micrometer.MicrometerMetricsTrackerFactory; -import io.micrometer.core.instrument.MeterRegistry; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.codahale.metrics.MetricRegistry; -import com.codahale.metrics.health.HealthCheckRegistry; -import com.zaxxer.hikari.HikariConfig; -import com.zaxxer.hikari.HikariPoolMXBean; -import com.zaxxer.hikari.metrics.MetricsTrackerFactory; -import com.zaxxer.hikari.metrics.PoolStats; -import com.zaxxer.hikari.metrics.dropwizard.CodahaleHealthChecker; -import com.zaxxer.hikari.metrics.dropwizard.CodahaleMetricsTrackerFactory; -import com.zaxxer.hikari.util.ConcurrentBag; -import com.zaxxer.hikari.util.ConcurrentBag.IBagStateListener; -import com.zaxxer.hikari.util.SuspendResumeLock; -import com.zaxxer.hikari.util.UtilityElf.DefaultThreadFactory; +import static com.zaxxer.hikari.util.ClockSource.currentTime; +import static com.zaxxer.hikari.util.ClockSource.elapsedDisplayString; +import static com.zaxxer.hikari.util.ClockSource.elapsedMillis; +import static com.zaxxer.hikari.util.ClockSource.plusMillis; +import static com.zaxxer.hikari.util.ConcurrentBag.IConcurrentBagEntry.STATE_IN_USE; +import static com.zaxxer.hikari.util.ConcurrentBag.IConcurrentBagEntry.STATE_NOT_IN_USE; +import static com.zaxxer.hikari.util.UtilityElf.createThreadPoolExecutor; +import static com.zaxxer.hikari.util.UtilityElf.quietlySleep; +import static java.util.Collections.unmodifiableCollection; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; /** * This is the primary connection pool class that provides the basic @@ -85,7 +83,7 @@ public final class HikariPool extends PoolBase implements HikariPoolMXBean, IBag private static final String EVICTED_CONNECTION_MESSAGE = "(connection was evicted)"; private static final String DEAD_CONNECTION_MESSAGE = "(connection is dead)"; - private final PoolEntryCreator POOL_ENTRY_CREATOR = new PoolEntryCreator(null); + private final PoolEntryCreator POOL_ENTRY_CREATOR = new PoolEntryCreator(null /*logging prefix*/); private final PoolEntryCreator POST_FILL_POOL_ENTRY_CREATOR = new PoolEntryCreator("After adding "); private final Collection addConnectionQueue; private final ThreadPoolExecutor addConnectionExecutor; @@ -96,7 +94,7 @@ public final class HikariPool extends PoolBase implements HikariPoolMXBean, IBag private final ProxyLeakTaskFactory leakTaskFactory; private final SuspendResumeLock suspendResumeLock; - private ScheduledExecutorService houseKeepingExecutorService; + private final ScheduledExecutorService houseKeepingExecutorService; private ScheduledFuture houseKeeperTask; /** @@ -171,7 +169,7 @@ public final class HikariPool extends PoolBase implements HikariPoolMXBean, IBag final long now = currentTime(); if (poolEntry.isMarkedEvicted() || (elapsedMillis(poolEntry.lastAccessed, now) > ALIVE_BYPASS_WINDOW_MS && !isConnectionAlive(poolEntry.connection))) { - closeConnection(poolEntry, poolEntry.isMarkedEvicted() ? EVICTED_CONNECTION_MESSAGE : DEAD_CONNECTION_MESSAGE); // Throw away the dead connection (passed max age or failed alive test) + closeConnection(poolEntry, poolEntry.isMarkedEvicted() ? EVICTED_CONNECTION_MESSAGE : DEAD_CONNECTION_MESSAGE); timeout = hardTimeout - elapsedMillis(startTime); } else { @@ -181,6 +179,7 @@ public final class HikariPool extends PoolBase implements HikariPoolMXBean, IBag } while (timeout > 0L); metricsTracker.recordBorrowTimeoutStats(startTime); + throw createTimeoutException(startTime); } catch (InterruptedException e) { Thread.currentThread().interrupt(); @@ -189,8 +188,6 @@ public final class HikariPool extends PoolBase implements HikariPoolMXBean, IBag finally { suspendResumeLock.release(); } - - throw createTimeoutException(startTime); } /** @@ -204,7 +201,7 @@ public final class HikariPool extends PoolBase implements HikariPoolMXBean, IBag try { poolState = POOL_SHUTDOWN; - if (addConnectionExecutor == null) { + if (addConnectionExecutor == null) { // pool never started return; } @@ -250,9 +247,9 @@ public final class HikariPool extends PoolBase implements HikariPoolMXBean, IBag } /** - * Evict a connection from the pool. + * Evict a Connection from the pool. * - * @param connection the connection to evict + * @param connection the Connection to evict (actually a {@link ProxyConnection}) */ public void evictConnection(Connection connection) { @@ -267,6 +264,12 @@ public final class HikariPool extends PoolBase implements HikariPoolMXBean, IBag } } + /** + * Set a metrics registry to be used when registering metrics collectors. The HikariDataSource prevents this + * method from being called more than once. + * + * @param metricRegistry the metrics registry instance to use + */ public void setMetricRegistry(Object metricRegistry) { if (metricRegistry != null && metricRegistry.getClass().getName().contains("MetricRegistry")) { @@ -280,6 +283,11 @@ public final class HikariPool extends PoolBase implements HikariPoolMXBean, IBag } } + /** + * Set the MetricsTrackerFactory to be used to create the IMetricsTracker instance used by the pool. + * + * @param metricsTrackerFactory an instance of a class that subclasses MetricsTrackerFactory + */ public void setMetricsTrackerFactory(MetricsTrackerFactory metricsTrackerFactory) { if (metricsTrackerFactory != null) { @@ -290,6 +298,12 @@ public final class HikariPool extends PoolBase implements HikariPoolMXBean, IBag } } + /** + * Set the health check registry to be used when registering health checks. Currently only Codahale health + * checks are supported. + * + * @param healthCheckRegistry the health check registry instance to use + */ public void setHealthCheckRegistry(Object healthCheckRegistry) { if (healthCheckRegistry != null) { @@ -309,8 +323,6 @@ public final class HikariPool extends PoolBase implements HikariPoolMXBean, IBag if (shouldAdd) { addConnectionExecutor.submit(POOL_ENTRY_CREATOR); } - - CompletableFuture.completedFuture(Boolean.TRUE); } // *********************************************************************** @@ -432,12 +444,14 @@ public final class HikariPool extends PoolBase implements HikariPoolMXBean, IBag return connectionBag.getStateCounts(); } + // *********************************************************************** // Private methods // *********************************************************************** /** - * Creating new poolEntry. + * Creating new poolEntry. If maxLifetime is configured, create a future End-of-life task with 2.5% variance from + * the maxLifetime time to ensure there is no massive die-off of Connections in the pool. */ private PoolEntry createPoolEntry() { @@ -461,7 +475,7 @@ public final class HikariPool extends PoolBase implements HikariPoolMXBean, IBag return poolEntry; } catch (Exception e) { - if (poolState == POOL_NORMAL) { + if (poolState == POOL_NORMAL) { // we check POOL_NORMAL to avoid a flood of messages if shutdown() is running concurrently LOGGER.debug("{} - Cannot acquire connection from data source", poolName, (e instanceof ConnectionSetupException ? e.getCause() : e)); } return null; @@ -482,6 +496,8 @@ public final class HikariPool extends PoolBase implements HikariPoolMXBean, IBag /** * Attempt to abort or close active connections. + * + * @param assassinExecutor the ExecutorService to pass to Connection.abort() */ private void abortActiveConnections(final ExecutorService assassinExecutor) { @@ -503,6 +519,7 @@ public final class HikariPool extends PoolBase implements HikariPoolMXBean, IBag * If initializationFailFast is configured, check that we have DB connectivity. * * @throws PoolInitializationException if fails to create or validate connection + * @see HikariConfig#setInitializationFailTimeout(long) */ private void checkFailFast() { @@ -530,7 +547,7 @@ public final class HikariPool extends PoolBase implements HikariPoolMXBean, IBag throwPoolInitializationException(getLastConnectionFailure().getCause()); } - quietlySleep(1000L); + quietlySleep(SECONDS.toMillis(1)); } while (elapsedMillis(startTime) < initializationTimeout); if (initializationTimeout > 0) { @@ -538,6 +555,12 @@ public final class HikariPool extends PoolBase implements HikariPoolMXBean, IBag } } + /** + * Log the Throwable that caused pool initialization to fail, and then throw a PoolInitializationException with + * that cause attached. + * + * @param t the Throwable that caused the pool to fail to initialize (possibly null) + */ private void throwPoolInitializationException(Throwable t) { LOGGER.error("{} - Exception during pool initialization.", poolName, t); @@ -545,6 +568,19 @@ public final class HikariPool extends PoolBase implements HikariPoolMXBean, IBag throw new PoolInitializationException(t); } + /** + * "Soft" evict a Connection (/PoolEntry) from the pool. If this method is being called by the user directly + * through {@link com.zaxxer.hikari.HikariDataSource#evictConnection(Connection)} then {@code owner} is {@code true}. + * + * If the caller is the owner, or if the Connection is idle (i.e. can be "reserved" in the {@link ConcurrentBag}), + * then we can close the connection immediately. Otherwise, we leave it "marked" for eviction so that it is evicted + * the next time someone tries to acquire it from the pool. + * + * @param poolEntry the PoolEntry (/Connection) to "soft" evict from the pool + * @param reason the reason that the connection is being evicted + * @param owner true if the caller is the owner of the connection, false otherwise + * @return true if the connection was evicted (closed), false if it was merely marked for eviction + */ private boolean softEvictConnection(final PoolEntry poolEntry, final String reason, final boolean owner) { poolEntry.markEvicted(); @@ -556,6 +592,13 @@ public final class HikariPool extends PoolBase implements HikariPoolMXBean, IBag return false; } + /** + * Create/initialize the Housekeeping service {@link ScheduledExecutorService}. If the user specified an Executor + * to be used in the {@link HikariConfig}, then we use that. If no Executor was specified (typical), then create + * an Executor and configure it. + * + * @return either the user specified {@link ScheduledExecutorService}, or the one we created + */ private ScheduledExecutorService initializeHouseKeepingExecutorService() { if (config.getScheduledExecutor() == null) { @@ -570,6 +613,9 @@ public final class HikariPool extends PoolBase implements HikariPoolMXBean, IBag } } + /** + * Destroy (/shutdown) the Housekeeping service Executor, if it was the one that we created. + */ private void destroyHouseKeepingExecutorService() { if (config.getScheduledExecutor() == null) { @@ -577,6 +623,11 @@ public final class HikariPool extends PoolBase implements HikariPoolMXBean, IBag } } + /** + * Create a PoolStats instance that will be used by metrics tracking, with a pollable resolution of 1 second. + * + * @return a PoolStats instance + */ private PoolStats getPoolStats() { return new PoolStats(SECONDS.toMillis(1)) { @@ -590,6 +641,18 @@ public final class HikariPool extends PoolBase implements HikariPoolMXBean, IBag }; } + /** + * Create a timeout exception (specifically, {@link SQLTransientConnectionException}) to be thrown, because a + * timeout occurred when trying to acquire a Connection from the pool. If there was an underlying cause for the + * timeout, e.g. a SQLException thrown by the driver while trying to create a new Connection, then use the + * SQL State from that exception as our own and additionally set that exception as the "next" SQLException inside + * of our exception. + * + * As a side-effect, log the timeout failure at DEBUG, and record the timeout failure in the metrics tracker. + * + * @param startTime the start time (timestamp) of the acquisition attempt + * @return a SQLException to be thrown from {@link #getConnection()} + */ private SQLException createTimeoutException(long startTime) { logPoolState("Timeout failure "); @@ -608,6 +671,7 @@ public final class HikariPool extends PoolBase implements HikariPoolMXBean, IBag return connectionException; } + // *********************************************************************** // Non-anonymous Inner-classes // *********************************************************************** @@ -647,9 +711,13 @@ public final class HikariPool extends PoolBase implements HikariPoolMXBean, IBag return Boolean.FALSE; } + /** + * We only create connections if we need another idle connection or have threads still waiting + * for a new connection. Otherwise we bail out of the request to create. + * + * @return true if we should create a connection, false if the need has disappeared + */ private boolean shouldCreateAnotherConnection() { - // only create connections if we need another idle connection or have threads still waiting - // for a new connection, otherwise bail return getTotalConnections() < config.getMaximumPoolSize() && (connectionBag.getWaitingThreadCount() > 0 || getIdleConnections() < config.getMinimumIdle()); } @@ -680,7 +748,6 @@ public final class HikariPool extends PoolBase implements HikariPoolMXBean, IBag poolName, elapsedDisplayString(previous, now)); previous = now; softEvictConnections(); - fillPool(); return; } else if (now > plusMillis(previous, (3 * HOUSEKEEPING_PERIOD_MS) / 2)) {