From 00b77f9cd3853848a0854560847f0f5c9953e700 Mon Sep 17 00:00:00 2001 From: Brett Wooldridge Date: Thu, 9 Oct 2014 15:58:37 +0900 Subject: [PATCH] Tweak the leak task handling and resetConnection() handling for small performance gains in the *nominal* case. Possibly slower in edge-cases (but who cares). --- .../com/zaxxer/hikari/pool/HikariPool.java | 26 ++++++---- .../zaxxer/hikari/proxy/ConnectionProxy.java | 51 ++++++++----------- .../hikari/proxy/IHikariConnectionProxy.java | 30 ----------- .../com/zaxxer/hikari/proxy/LeakTask.java | 32 ++++++++++-- .../com/zaxxer/hikari/proxy/ProxyFactory.java | 3 +- .../com/zaxxer/hikari/pool/HikariPool.java | 22 +++++--- .../zaxxer/hikari/proxy/ConnectionProxy.java | 49 ++++++++---------- .../hikari/proxy/IHikariConnectionProxy.java | 9 ---- .../com/zaxxer/hikari/proxy/LeakTask.java | 32 ++++++++++-- .../com/zaxxer/hikari/proxy/ProxyFactory.java | 3 +- 10 files changed, 131 insertions(+), 126 deletions(-) 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 8187a0da..ea3f7f9c 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 @@ -48,6 +48,7 @@ import com.zaxxer.hikari.metrics.IMetricsTracker.MetricsContext; import com.zaxxer.hikari.metrics.MetricsFactory; import com.zaxxer.hikari.metrics.MetricsTracker; import com.zaxxer.hikari.proxy.IHikariConnectionProxy; +import com.zaxxer.hikari.proxy.LeakTask; import com.zaxxer.hikari.proxy.ProxyFactory; import com.zaxxer.hikari.util.ConcurrentBag; import com.zaxxer.hikari.util.ConcurrentBag.IBagStateListener; @@ -64,12 +65,13 @@ import com.zaxxer.hikari.util.PropertyBeanSetter; */ public final class HikariPool implements HikariPoolMBean, IBagStateListener { - private static final Logger LOGGER = LoggerFactory.getLogger(HikariPool.class); + private static final Logger LOGGER; + private static final LeakTask NO_LEAK; - public int transactionIsolation; public final String catalog; public final boolean isAutoCommit; public final boolean isReadOnly; + public int transactionIsolation; private final DataSource dataSource; @@ -95,6 +97,14 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener private volatile long connectionTimeout; private volatile boolean isShutdown; + // static initializer + static { + LOGGER = LoggerFactory.getLogger(HikariPool.class); + NO_LEAK = new LeakTask() { + public void cancel() {}; + }; + } + /** * Construct a HikariPool with the specified configuration. * @@ -142,8 +152,8 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener HikariMBeanElf.registerMBeans(configuration, this); } - addConnectionExecutor = createThreadPoolExecutor(configuration.getMaximumPoolSize(), "HikariCP connection filler", configuration.getThreadFactory()); - closeConnectionExecutor = createThreadPoolExecutor(configuration.getMaximumPoolSize(), "HikariCP connection closer", configuration.getThreadFactory()); + addConnectionExecutor = createThreadPoolExecutor(configuration.getMaximumPoolSize(), "HikariCP connection filler (pool " + configuration.getPoolName() + ")", configuration.getThreadFactory()); + closeConnectionExecutor = createThreadPoolExecutor(configuration.getMaximumPoolSize(), "HikariCP connection closer (pool " + configuration.getPoolName() + ")", configuration.getThreadFactory()); fillPool(); @@ -181,11 +191,9 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener continue; } - final IHikariConnectionProxy proxyConnection = ProxyFactory.getProxyConnection(this, bagEntry); - - if (leakDetectionThreshold != 0) { - proxyConnection.captureStack(leakDetectionThreshold, houseKeepingExecutorService); - } + LeakTask leakTask = (leakDetectionThreshold == 0) ? NO_LEAK : new LeakTask(leakDetectionThreshold, houseKeepingExecutorService); + + final IHikariConnectionProxy proxyConnection = ProxyFactory.getProxyConnection(this, bagEntry, leakTask); if (isRecordMetrics) { bagEntry.lastOpenTime = now; diff --git a/hikaricp-java6/src/main/java/com/zaxxer/hikari/proxy/ConnectionProxy.java b/hikaricp-java6/src/main/java/com/zaxxer/hikari/proxy/ConnectionProxy.java index fbcdc971..ffe28b37 100644 --- a/hikaricp-java6/src/main/java/com/zaxxer/hikari/proxy/ConnectionProxy.java +++ b/hikaricp-java6/src/main/java/com/zaxxer/hikari/proxy/ConnectionProxy.java @@ -24,8 +24,6 @@ import java.sql.Statement; import java.sql.Wrapper; import java.util.HashSet; import java.util.Set; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,27 +39,28 @@ import com.zaxxer.hikari.util.FastList; */ public abstract class ConnectionProxy implements IHikariConnectionProxy { - private static final Logger LOGGER = LoggerFactory.getLogger(ConnectionProxy.class); - + private static final Logger LOGGER; private static final Set SQL_ERRORS; protected final Connection delegate; private final HikariPool parentPool; private final PoolBagEntry bagEntry; - + private final FastList openStatements; + private final LeakTask leakTask; + private boolean forceClose; + private boolean isAnythingDirty; private boolean isAutoCommitDirty; private boolean isCatalogDirty; private boolean isClosed; private boolean isReadOnlyDirty; private boolean isTransactionIsolationDirty; - private FastList openStatements; - private LeakTask leakTask; - // static initializer static { + LOGGER = LoggerFactory.getLogger(ConnectionProxy.class); + SQL_ERRORS = new HashSet(); SQL_ERRORS.add("57P01"); // ADMIN SHUTDOWN SQL_ERRORS.add("57P02"); // CRASH SHUTDOWN @@ -71,10 +70,11 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy SQL_ERRORS.add("JZ0C1"); // Sybase disconnect error } - protected ConnectionProxy(final HikariPool pool, final PoolBagEntry bagEntry) { + protected ConnectionProxy(final HikariPool pool, final PoolBagEntry bagEntry, final LeakTask leakTask) { this.parentPool = pool; this.bagEntry = bagEntry; this.delegate = bagEntry.connection; + this.leakTask = leakTask; this.openStatements = new FastList(Statement.class, 16); } @@ -96,18 +96,6 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy return bagEntry; } - /** {@inheritDoc} */ - @Override - public final void captureStack(long leakDetectionThreshold, ScheduledExecutorService executorService) - { - StackTraceElement[] trace = Thread.currentThread().getStackTrace(); - StackTraceElement[] leakTrace = new StackTraceElement[trace.length - 4]; - System.arraycopy(trace, 4, leakTrace, 0, leakTrace.length); - - leakTask = new LeakTask(leakTrace, leakDetectionThreshold); - executorService.schedule(leakTask, leakDetectionThreshold, TimeUnit.MILLISECONDS); - } - /** {@inheritDoc} */ @Override public final SQLException checkException(SQLException sqle) @@ -184,8 +172,6 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy if (isCatalogDirty && parentPool.catalog != null) { delegate.setCatalog(parentPool.catalog); } - - delegate.clearWarnings(); } // ********************************************************************** @@ -199,10 +185,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy if (!isClosed) { isClosed = true; - if (leakTask != null) { - leakTask.cancel(); - leakTask = null; - } + leakTask.cancel(); try { final int size = openStatements.size(); @@ -216,13 +199,17 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy } } - openStatements = null; + openStatements.clear(); + } + + if (isAnythingDirty) { + resetConnectionState(); } - resetConnectionState(); + delegate.clearWarnings(); } catch (SQLException e) { - checkException(e); + throw checkException(e); } finally { parentPool.releaseConnection(bagEntry, forceClose); @@ -432,6 +419,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy checkClosed(); try { delegate.setAutoCommit(autoCommit); + isAnythingDirty = true; isAutoCommitDirty = (autoCommit != parentPool.isAutoCommit); } catch (SQLException e) { @@ -446,6 +434,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy checkClosed(); try { delegate.setReadOnly(readOnly); + isAnythingDirty = true; isReadOnlyDirty = (readOnly != parentPool.isReadOnly); } catch (SQLException e) { @@ -460,6 +449,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy checkClosed(); try { delegate.setTransactionIsolation(level); + isAnythingDirty = true; isTransactionIsolationDirty = (level != parentPool.transactionIsolation); } catch (SQLException e) { @@ -473,6 +463,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy checkClosed(); try { delegate.setCatalog(catalog); + isAnythingDirty = true; isCatalogDirty = (catalog != null && !catalog.equals(parentPool.catalog)) || (catalog == null && parentPool.catalog != null); } catch (SQLException e) { diff --git a/hikaricp-java6/src/main/java/com/zaxxer/hikari/proxy/IHikariConnectionProxy.java b/hikaricp-java6/src/main/java/com/zaxxer/hikari/proxy/IHikariConnectionProxy.java index 48a695b8..f84ca1c2 100644 --- a/hikaricp-java6/src/main/java/com/zaxxer/hikari/proxy/IHikariConnectionProxy.java +++ b/hikaricp-java6/src/main/java/com/zaxxer/hikari/proxy/IHikariConnectionProxy.java @@ -19,7 +19,6 @@ package com.zaxxer.hikari.proxy; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; -import java.util.concurrent.ScheduledExecutorService; import com.zaxxer.hikari.pool.PoolBagEntry; @@ -38,14 +37,6 @@ public interface IHikariConnectionProxy extends Connection */ PoolBagEntry getPoolBagEntry(); - /** - * Catpure the stack and start leak detection. - * - * @param leakThreshold the number of milliseconds before a leak is reported - * @param houseKeepingExecutorService the executor service to run the leak detection task with - */ - void captureStack(long leakThreshold, ScheduledExecutorService houseKeepingExecutorService); - /** * Check if the provided SQLException contains a SQLSTATE that indicates * a disconnection from the server. @@ -55,27 +46,6 @@ public interface IHikariConnectionProxy extends Connection */ SQLException checkException(SQLException sqle); - /** - * Get the expiration timestamp of the connection. - * - * @return the expiration timestamp, or Long.MAX_VALUE if there is no maximum lifetime - */ - long getExpirationTime(); - - /** - * Get the last access timestamp of the connection. - * - * @return the last access timestamp - */ - long getLastAccess(); - - /** - * Get the timestamp of when the connection was removed from the pool for use. - * - * @return the timestamp the connection started to be used in the most recent request - */ - long getLastOpenTime(); - /** * Return the broken state of the connection. If checkException() detected * a broken connection, this method will return true, otherwise false. diff --git a/hikaricp-java6/src/main/java/com/zaxxer/hikari/proxy/LeakTask.java b/hikaricp-java6/src/main/java/com/zaxxer/hikari/proxy/LeakTask.java index 69834e23..422b416d 100644 --- a/hikaricp-java6/src/main/java/com/zaxxer/hikari/proxy/LeakTask.java +++ b/hikaricp-java6/src/main/java/com/zaxxer/hikari/proxy/LeakTask.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Brett Wooldridge + * Copyright (C) 2013, 2014 Brett Wooldridge * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,20 +16,36 @@ package com.zaxxer.hikari.proxy; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + import org.slf4j.LoggerFactory; /** + * A Runnable that is scheduled in the future to report leaks. The ScheduledFuture is + * cancelled if the connection is closed before the leak time expires. + * * @author Brett Wooldridge */ -class LeakTask implements Runnable +public class LeakTask implements Runnable { private final long leakTime; + private final ScheduledFuture scheduledFuture; private StackTraceElement[] stackTrace; - public LeakTask(StackTraceElement[] stackTrace, long leakDetectionThreshold) + public LeakTask() { - this.stackTrace = stackTrace; + leakTime = 0; + scheduledFuture = null; + } + + public LeakTask(final long leakDetectionThreshold, final ScheduledExecutorService executorService) + { + this.stackTrace = Thread.currentThread().getStackTrace(); + this.leakTime = System.currentTimeMillis() + leakDetectionThreshold; + scheduledFuture = executorService.schedule(this, leakDetectionThreshold, TimeUnit.MILLISECONDS); } /** {@inheritDoc} */ @@ -37,8 +53,11 @@ class LeakTask implements Runnable public void run() { if (System.currentTimeMillis() > leakTime) { + StackTraceElement[] trace = new StackTraceElement[stackTrace.length - 3]; + System.arraycopy(stackTrace, 4, trace, 0, trace.length); + Exception e = new Exception(); - e.setStackTrace(stackTrace); + e.setStackTrace(trace); LoggerFactory.getLogger(LeakTask.class).warn("Connection leak detection triggered, stack trace follows", e); stackTrace = null; } @@ -47,5 +66,8 @@ class LeakTask implements Runnable public void cancel() { stackTrace = null; + if (scheduledFuture != null) { + scheduledFuture.cancel(false); + } } } diff --git a/hikaricp-java6/src/main/java/com/zaxxer/hikari/proxy/ProxyFactory.java b/hikaricp-java6/src/main/java/com/zaxxer/hikari/proxy/ProxyFactory.java index d31f3bd5..f6720663 100644 --- a/hikaricp-java6/src/main/java/com/zaxxer/hikari/proxy/ProxyFactory.java +++ b/hikaricp-java6/src/main/java/com/zaxxer/hikari/proxy/ProxyFactory.java @@ -42,9 +42,10 @@ public final class ProxyFactory * * @param pool the {@link HikariPool} that will own this proxy * @param bagEntry the PoolBagEntry entry for this proxy + * @param leakTask a leak detetection task * @return a proxy that wraps the specified {@link Connection} */ - public static IHikariConnectionProxy getProxyConnection(final HikariPool pool, final PoolBagEntry bagEntry) + public static IHikariConnectionProxy getProxyConnection(final HikariPool pool, final PoolBagEntry bagEntry, final LeakTask leakTask) { // Body is injected by JavassistProxyFactory return null; 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 f4a8b475..f66c02f2 100644 --- a/hikaricp/src/main/java/com/zaxxer/hikari/pool/HikariPool.java +++ b/hikaricp/src/main/java/com/zaxxer/hikari/pool/HikariPool.java @@ -47,6 +47,7 @@ import com.zaxxer.hikari.metrics.IMetricsTracker.MetricsContext; import com.zaxxer.hikari.metrics.MetricsFactory; import com.zaxxer.hikari.metrics.MetricsTracker; import com.zaxxer.hikari.proxy.IHikariConnectionProxy; +import com.zaxxer.hikari.proxy.LeakTask; import com.zaxxer.hikari.proxy.ProxyFactory; import com.zaxxer.hikari.util.ConcurrentBag; import com.zaxxer.hikari.util.ConcurrentBag.IBagStateListener; @@ -63,12 +64,13 @@ import com.zaxxer.hikari.util.PropertyBeanSetter; */ public final class HikariPool implements HikariPoolMBean, IBagStateListener { - private static final Logger LOGGER = LoggerFactory.getLogger(HikariPool.class); + private static final Logger LOGGER; + private static final LeakTask NO_LEAK; - public int transactionIsolation; public final String catalog; public final boolean isAutoCommit; public final boolean isReadOnly; + public int transactionIsolation; private final DataSource dataSource; @@ -94,6 +96,14 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener private volatile long connectionTimeout; private volatile boolean isShutdown; + // static initializer + static { + LOGGER = LoggerFactory.getLogger(HikariPool.class); + NO_LEAK = new LeakTask() { + public void cancel() {}; + }; + } + /** * Construct a HikariPool with the specified configuration. * @@ -178,11 +188,9 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener continue; } - final IHikariConnectionProxy proxyConnection = ProxyFactory.getProxyConnection(this, bagEntry); - - if (leakDetectionThreshold != 0) { - proxyConnection.captureStack(leakDetectionThreshold, houseKeepingExecutorService); - } + LeakTask leakTask = (leakDetectionThreshold == 0) ? NO_LEAK : new LeakTask(leakDetectionThreshold, houseKeepingExecutorService); + + final IHikariConnectionProxy proxyConnection = ProxyFactory.getProxyConnection(this, bagEntry, leakTask); if (isRecordMetrics) { bagEntry.lastOpenTime = now; diff --git a/hikaricp/src/main/java/com/zaxxer/hikari/proxy/ConnectionProxy.java b/hikaricp/src/main/java/com/zaxxer/hikari/proxy/ConnectionProxy.java index ac385319..ffe28b37 100644 --- a/hikaricp/src/main/java/com/zaxxer/hikari/proxy/ConnectionProxy.java +++ b/hikaricp/src/main/java/com/zaxxer/hikari/proxy/ConnectionProxy.java @@ -24,8 +24,6 @@ import java.sql.Statement; import java.sql.Wrapper; import java.util.HashSet; import java.util.Set; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,27 +39,28 @@ import com.zaxxer.hikari.util.FastList; */ public abstract class ConnectionProxy implements IHikariConnectionProxy { - private static final Logger LOGGER = LoggerFactory.getLogger(ConnectionProxy.class); - + private static final Logger LOGGER; private static final Set SQL_ERRORS; protected final Connection delegate; private final HikariPool parentPool; private final PoolBagEntry bagEntry; - + private final FastList openStatements; + private final LeakTask leakTask; + private boolean forceClose; + private boolean isAnythingDirty; private boolean isAutoCommitDirty; private boolean isCatalogDirty; private boolean isClosed; private boolean isReadOnlyDirty; private boolean isTransactionIsolationDirty; - private FastList openStatements; - private LeakTask leakTask; - // static initializer static { + LOGGER = LoggerFactory.getLogger(ConnectionProxy.class); + SQL_ERRORS = new HashSet(); SQL_ERRORS.add("57P01"); // ADMIN SHUTDOWN SQL_ERRORS.add("57P02"); // CRASH SHUTDOWN @@ -71,10 +70,11 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy SQL_ERRORS.add("JZ0C1"); // Sybase disconnect error } - protected ConnectionProxy(final HikariPool pool, final PoolBagEntry bagEntry) { + protected ConnectionProxy(final HikariPool pool, final PoolBagEntry bagEntry, final LeakTask leakTask) { this.parentPool = pool; this.bagEntry = bagEntry; this.delegate = bagEntry.connection; + this.leakTask = leakTask; this.openStatements = new FastList(Statement.class, 16); } @@ -96,18 +96,6 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy return bagEntry; } - /** {@inheritDoc} */ - @Override - public final void captureStack(long leakDetectionThreshold, ScheduledExecutorService executorService) - { - StackTraceElement[] trace = Thread.currentThread().getStackTrace(); - StackTraceElement[] leakTrace = new StackTraceElement[trace.length - 4]; - System.arraycopy(trace, 4, leakTrace, 0, leakTrace.length); - - leakTask = new LeakTask(leakTrace, leakDetectionThreshold); - executorService.schedule(leakTask, leakDetectionThreshold, TimeUnit.MILLISECONDS); - } - /** {@inheritDoc} */ @Override public final SQLException checkException(SQLException sqle) @@ -184,8 +172,6 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy if (isCatalogDirty && parentPool.catalog != null) { delegate.setCatalog(parentPool.catalog); } - - delegate.clearWarnings(); } // ********************************************************************** @@ -199,10 +185,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy if (!isClosed) { isClosed = true; - if (leakTask != null) { - leakTask.cancel(); - leakTask = null; - } + leakTask.cancel(); try { final int size = openStatements.size(); @@ -216,10 +199,14 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy } } - openStatements = null; + openStatements.clear(); + } + + if (isAnythingDirty) { + resetConnectionState(); } - resetConnectionState(); + delegate.clearWarnings(); } catch (SQLException e) { throw checkException(e); @@ -432,6 +419,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy checkClosed(); try { delegate.setAutoCommit(autoCommit); + isAnythingDirty = true; isAutoCommitDirty = (autoCommit != parentPool.isAutoCommit); } catch (SQLException e) { @@ -446,6 +434,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy checkClosed(); try { delegate.setReadOnly(readOnly); + isAnythingDirty = true; isReadOnlyDirty = (readOnly != parentPool.isReadOnly); } catch (SQLException e) { @@ -460,6 +449,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy checkClosed(); try { delegate.setTransactionIsolation(level); + isAnythingDirty = true; isTransactionIsolationDirty = (level != parentPool.transactionIsolation); } catch (SQLException e) { @@ -473,6 +463,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy checkClosed(); try { delegate.setCatalog(catalog); + isAnythingDirty = true; isCatalogDirty = (catalog != null && !catalog.equals(parentPool.catalog)) || (catalog == null && parentPool.catalog != null); } catch (SQLException e) { diff --git a/hikaricp/src/main/java/com/zaxxer/hikari/proxy/IHikariConnectionProxy.java b/hikaricp/src/main/java/com/zaxxer/hikari/proxy/IHikariConnectionProxy.java index 38555c2c..f84ca1c2 100644 --- a/hikaricp/src/main/java/com/zaxxer/hikari/proxy/IHikariConnectionProxy.java +++ b/hikaricp/src/main/java/com/zaxxer/hikari/proxy/IHikariConnectionProxy.java @@ -19,7 +19,6 @@ package com.zaxxer.hikari.proxy; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; -import java.util.concurrent.ScheduledExecutorService; import com.zaxxer.hikari.pool.PoolBagEntry; @@ -38,14 +37,6 @@ public interface IHikariConnectionProxy extends Connection */ PoolBagEntry getPoolBagEntry(); - /** - * Catpure the stack and start leak detection. - * - * @param leakThreshold the number of milliseconds before a leak is reported - * @param houseKeepingExecutorService the executor service to run the leak detection task with - */ - void captureStack(long leakThreshold, ScheduledExecutorService houseKeepingExecutorService); - /** * Check if the provided SQLException contains a SQLSTATE that indicates * a disconnection from the server. diff --git a/hikaricp/src/main/java/com/zaxxer/hikari/proxy/LeakTask.java b/hikaricp/src/main/java/com/zaxxer/hikari/proxy/LeakTask.java index 69834e23..422b416d 100644 --- a/hikaricp/src/main/java/com/zaxxer/hikari/proxy/LeakTask.java +++ b/hikaricp/src/main/java/com/zaxxer/hikari/proxy/LeakTask.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Brett Wooldridge + * Copyright (C) 2013, 2014 Brett Wooldridge * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,20 +16,36 @@ package com.zaxxer.hikari.proxy; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + import org.slf4j.LoggerFactory; /** + * A Runnable that is scheduled in the future to report leaks. The ScheduledFuture is + * cancelled if the connection is closed before the leak time expires. + * * @author Brett Wooldridge */ -class LeakTask implements Runnable +public class LeakTask implements Runnable { private final long leakTime; + private final ScheduledFuture scheduledFuture; private StackTraceElement[] stackTrace; - public LeakTask(StackTraceElement[] stackTrace, long leakDetectionThreshold) + public LeakTask() { - this.stackTrace = stackTrace; + leakTime = 0; + scheduledFuture = null; + } + + public LeakTask(final long leakDetectionThreshold, final ScheduledExecutorService executorService) + { + this.stackTrace = Thread.currentThread().getStackTrace(); + this.leakTime = System.currentTimeMillis() + leakDetectionThreshold; + scheduledFuture = executorService.schedule(this, leakDetectionThreshold, TimeUnit.MILLISECONDS); } /** {@inheritDoc} */ @@ -37,8 +53,11 @@ class LeakTask implements Runnable public void run() { if (System.currentTimeMillis() > leakTime) { + StackTraceElement[] trace = new StackTraceElement[stackTrace.length - 3]; + System.arraycopy(stackTrace, 4, trace, 0, trace.length); + Exception e = new Exception(); - e.setStackTrace(stackTrace); + e.setStackTrace(trace); LoggerFactory.getLogger(LeakTask.class).warn("Connection leak detection triggered, stack trace follows", e); stackTrace = null; } @@ -47,5 +66,8 @@ class LeakTask implements Runnable public void cancel() { stackTrace = null; + if (scheduledFuture != null) { + scheduledFuture.cancel(false); + } } } diff --git a/hikaricp/src/main/java/com/zaxxer/hikari/proxy/ProxyFactory.java b/hikaricp/src/main/java/com/zaxxer/hikari/proxy/ProxyFactory.java index d31f3bd5..f6720663 100644 --- a/hikaricp/src/main/java/com/zaxxer/hikari/proxy/ProxyFactory.java +++ b/hikaricp/src/main/java/com/zaxxer/hikari/proxy/ProxyFactory.java @@ -42,9 +42,10 @@ public final class ProxyFactory * * @param pool the {@link HikariPool} that will own this proxy * @param bagEntry the PoolBagEntry entry for this proxy + * @param leakTask a leak detetection task * @return a proxy that wraps the specified {@link Connection} */ - public static IHikariConnectionProxy getProxyConnection(final HikariPool pool, final PoolBagEntry bagEntry) + public static IHikariConnectionProxy getProxyConnection(final HikariPool pool, final PoolBagEntry bagEntry, final LeakTask leakTask) { // Body is injected by JavassistProxyFactory return null;