diff --git a/CHANGES b/CHANGES index 05cc0388..3564ea25 100644 --- a/CHANGES +++ b/CHANGES @@ -2,16 +2,11 @@ HikariCP Changes Changes in 2.2.6 - * Fix an InvalidStateException thrown from the ConcurrentBag when two separate - threads call close() on the same connection by making the isClosed state of - the ConnectionProxy visible across threads through volatility. - * Support pool suspend/resume though JMX to support certain failover scenarios. * Fix theoretical race in JDBC 4.0 detection support. - * Suppress ordinary internal accounting sanity checks when the pool is being - undergoing forced shutdown. All bets are off when live connections are being + * Improve shutdown() semantics to avoid exceptions as connections are forcefully aborted. Changes in 2.2.5 diff --git a/hikaricp-java6/src/main/java/com/zaxxer/hikari/pool/HikariPool.java b/hikaricp-java6/src/main/java/com/zaxxer/hikari/pool/HikariPool.java index cebbeef2..6990655b 100644 --- a/hikaricp-java6/src/main/java/com/zaxxer/hikari/pool/HikariPool.java +++ b/hikaricp-java6/src/main/java/com/zaxxer/hikari/pool/HikariPool.java @@ -92,6 +92,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener private final IConnectionCustomizer connectionCustomizer; private final Semaphore acquisitionSemaphore; + private final LeakTask leakTask; private final AtomicInteger totalConnections; private final AtomicReference lastConnectionFailure; private final ScheduledThreadPoolExecutor houseKeepingExecutorService; @@ -100,7 +101,6 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener private final String password; private final boolean isRecordMetrics; private final boolean isIsolateInternalQueries; - private final long leakDetectionThreshold; private volatile boolean isShutdown; private volatile long connectionTimeout; @@ -143,7 +143,6 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener this.catalog = configuration.getCatalog(); this.connectionCustomizer = initializeCustomizer(); this.transactionIsolation = getTransactionIsolation(configuration.getTransactionIsolation()); - this.leakDetectionThreshold = configuration.getLeakDetectionThreshold(); this.isIsolateInternalQueries = configuration.isIsolateInternalQueries(); this.isRecordMetrics = configuration.getMetricRegistry() != null; @@ -160,6 +159,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener this.houseKeepingExecutorService.setRemoveOnCancelPolicy(true); } this.houseKeepingExecutorService.scheduleAtFixedRate(new HouseKeeper(), delayPeriod, delayPeriod, TimeUnit.MILLISECONDS); + this.leakTask = (configuration.getLeakDetectionThreshold() == 0) ? LeakTask.NO_LEAK : new LeakTask(configuration.getLeakDetectionThreshold(), houseKeepingExecutorService); setLoginTimeout(dataSource, connectionTimeout, LOGGER); registerMBeans(configuration, this); @@ -193,12 +193,9 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener continue; } - final LeakTask leakTask = (leakDetectionThreshold == 0) ? LeakTask.NO_LEAK : new LeakTask(leakDetectionThreshold, houseKeepingExecutorService); - final IHikariConnectionProxy proxyConnection = ProxyFactory.getProxyConnection(this, bagEntry, leakTask); - metricsContext.setConnectionLastOpen(bagEntry, now); - return proxyConnection; + return ProxyFactory.getProxyConnection(this, bagEntry, leakTask.start()); } while (timeout > 0L); } diff --git a/hikaricp-java6/src/main/java/com/zaxxer/hikari/util/LeakTask.java b/hikaricp-java6/src/main/java/com/zaxxer/hikari/util/LeakTask.java index 579fd20a..a9288522 100644 --- a/hikaricp-java6/src/main/java/com/zaxxer/hikari/util/LeakTask.java +++ b/hikaricp-java6/src/main/java/com/zaxxer/hikari/util/LeakTask.java @@ -34,28 +34,42 @@ public class LeakTask implements Runnable public static final LeakTask NO_LEAK; private static final Logger LOGGER = LoggerFactory.getLogger(LeakTask.class); - private final ScheduledFuture scheduledFuture; - private final Exception exception; + private final ScheduledExecutorService executorService; + private final long leakDetectionThreshold; + private ScheduledFuture scheduledFuture; + private Exception exception; static { NO_LEAK = new LeakTask() { @Override public void cancel() {}; + + @Override + public LeakTask start() + { + return this; + } }; } public LeakTask() { - scheduledFuture = null; - exception = null; + executorService = null; + leakDetectionThreshold = 0; } public LeakTask(final long leakDetectionThreshold, final ScheduledExecutorService executorService) { - this.exception = new Exception(); - + this.executorService = executorService; + this.leakDetectionThreshold = leakDetectionThreshold; + } + + public LeakTask start() + { + exception = new Exception(); scheduledFuture = executorService.schedule(this, leakDetectionThreshold, TimeUnit.MILLISECONDS); + return this; } /** {@inheritDoc} */ diff --git a/hikaricp/src/main/java/com/zaxxer/hikari/pool/HikariPool.java b/hikaricp/src/main/java/com/zaxxer/hikari/pool/HikariPool.java index 115bb325..289257e6 100644 --- a/hikaricp/src/main/java/com/zaxxer/hikari/pool/HikariPool.java +++ b/hikaricp/src/main/java/com/zaxxer/hikari/pool/HikariPool.java @@ -91,6 +91,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener private final IConnectionCustomizer connectionCustomizer; private final Semaphore acquisitionSemaphore; + private final LeakTask leakTask; private final AtomicInteger totalConnections; private final AtomicReference lastConnectionFailure; private final ScheduledThreadPoolExecutor houseKeepingExecutorService; @@ -99,7 +100,6 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener private final String password; private final boolean isRecordMetrics; private final boolean isIsolateInternalQueries; - private final long leakDetectionThreshold; private volatile boolean isShutdown; private volatile long connectionTimeout; @@ -142,7 +142,6 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener this.catalog = configuration.getCatalog(); this.connectionCustomizer = initializeCustomizer(); this.transactionIsolation = getTransactionIsolation(configuration.getTransactionIsolation()); - this.leakDetectionThreshold = configuration.getLeakDetectionThreshold(); this.isIsolateInternalQueries = configuration.isIsolateInternalQueries(); this.isRecordMetrics = configuration.getMetricRegistry() != null; @@ -157,6 +156,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener this.houseKeepingExecutorService = new ScheduledThreadPoolExecutor(1, configuration.getThreadFactory() != null ? configuration.getThreadFactory() : new DefaultThreadFactory("Hikari Housekeeping Timer (pool " + configuration.getPoolName() + ")", true)); this.houseKeepingExecutorService.setRemoveOnCancelPolicy(true); this.houseKeepingExecutorService.scheduleAtFixedRate(new HouseKeeper(), delayPeriod, delayPeriod, TimeUnit.MILLISECONDS); + this.leakTask = (configuration.getLeakDetectionThreshold() == 0) ? LeakTask.NO_LEAK : new LeakTask(configuration.getLeakDetectionThreshold(), houseKeepingExecutorService); setLoginTimeout(dataSource, connectionTimeout, LOGGER); registerMBeans(configuration, this); @@ -190,12 +190,9 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener continue; } - final LeakTask leakTask = (leakDetectionThreshold == 0) ? LeakTask.NO_LEAK : new LeakTask(leakDetectionThreshold, houseKeepingExecutorService); - final IHikariConnectionProxy proxyConnection = ProxyFactory.getProxyConnection(this, bagEntry, leakTask); - metricsContext.setConnectionLastOpen(bagEntry, now); - return proxyConnection; + return ProxyFactory.getProxyConnection(this, bagEntry, leakTask.start()); } while (timeout > 0L); } diff --git a/hikaricp/src/main/java/com/zaxxer/hikari/util/LeakTask.java b/hikaricp/src/main/java/com/zaxxer/hikari/util/LeakTask.java index 579fd20a..a9288522 100644 --- a/hikaricp/src/main/java/com/zaxxer/hikari/util/LeakTask.java +++ b/hikaricp/src/main/java/com/zaxxer/hikari/util/LeakTask.java @@ -34,28 +34,42 @@ public class LeakTask implements Runnable public static final LeakTask NO_LEAK; private static final Logger LOGGER = LoggerFactory.getLogger(LeakTask.class); - private final ScheduledFuture scheduledFuture; - private final Exception exception; + private final ScheduledExecutorService executorService; + private final long leakDetectionThreshold; + private ScheduledFuture scheduledFuture; + private Exception exception; static { NO_LEAK = new LeakTask() { @Override public void cancel() {}; + + @Override + public LeakTask start() + { + return this; + } }; } public LeakTask() { - scheduledFuture = null; - exception = null; + executorService = null; + leakDetectionThreshold = 0; } public LeakTask(final long leakDetectionThreshold, final ScheduledExecutorService executorService) { - this.exception = new Exception(); - + this.executorService = executorService; + this.leakDetectionThreshold = leakDetectionThreshold; + } + + public LeakTask start() + { + exception = new Exception(); scheduledFuture = executorService.schedule(this, leakDetectionThreshold, TimeUnit.MILLISECONDS); + return this; } /** {@inheritDoc} */