Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added 'closeLeakedConnections' property which enables leaked connections closing. #114

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,12 @@ and close(). Maybe other pools are doing it wrong, but feel free to use leak de
HikariCP in production environments if you wish. Lowest acceptable value for enabling leak
detection is 10000 (10 secs). *Default: 0*

:negative_squared_cross_mark:``closeLeakedConnections``<br/>
This property controls whether unreturned connections must be closed automatically after being
used longer than the time limit specified by *leakDetectionThreshold* property.
The *true* value for this property is only valid when a positive value for the connection leak
detection threshold property is specified. *Default: false*

:negative_squared_cross_mark:``initializationFailFast``<br/>
This property controls whether the pool will "fail fast" if the pool cannot be seeded with
initial connections successfully. If connections cannot be created at pool startup time,
Expand Down
15 changes: 15 additions & 0 deletions hikaricp-java6/src/main/java/com/zaxxer/hikari/HikariConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public class HikariConfig implements HikariConfigMBean
private volatile long connectionTimeout;
private volatile long idleTimeout;
private volatile long leakDetectionThreshold;
private volatile boolean closeLeakedConnections;
private volatile long maxLifetime;
private volatile int maxPoolSize;
private volatile int minIdle;
Expand Down Expand Up @@ -484,6 +485,20 @@ public void setLeakDetectionThreshold(long leakDetectionThresholdMs)
this.leakDetectionThreshold = leakDetectionThresholdMs;
}

/** {@inheritDoc} */
@Override
public boolean getCloseLeakedConnections()
{
return closeLeakedConnections;
}

/** {@inheritDoc} */
@Override
public void setCloseLeakedConnections(boolean closeLeakedConnections)
{
this.closeLeakedConnections = closeLeakedConnections;
}

/** {@inheritDoc} */
@Override
public long getMaxLifetime()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,26 @@ public interface HikariConfigMBean
*/
void setLeakDetectionThreshold(long leakDetectionThresholdMs);

/**
* This property controls if unreturned connections must be closed automatically after being used longer than
* the time limit specified by {@link #getLeakDetectionThreshold()} method.
* This property is set to false by default. The <tt>true</tt> value for this property is only valid when
* a positive value for the connection leak detection threshold property is specified.
*
* @return <tt>true</tt> if leaked connections must be closed be the pool
*/
boolean getCloseLeakedConnections();

/**
* This property controls if unreturned connections must be closed automatically after being used longer than
* the time limit specified by {@link #getLeakDetectionThreshold()} method.
* This property is set to false by default. The <tt>true</tt> value for this property is only valid when
* a positive value for the connection leak detection threshold property is specified.
*
* @param closeLeakedConnections if <tt>true</tt> then leaked connections must be closed be the pool
*/
void setCloseLeakedConnections(boolean closeLeakedConnections);

/**
* This property controls the maximum lifetime of a connection in the pool. When a connection reaches this
* timeout, even if recently used, it will be retired from the pool. An in-use connection will never be
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
private final boolean isRegisteredMbeans;
private final boolean isJdbc4ConnectionTest;
private final long leakDetectionThreshold;
private final boolean closeLeakedConnections;
private final String catalog;
private final String username;
private final String password;
Expand Down Expand Up @@ -126,6 +127,7 @@ public HikariPool(HikariConfig configuration, String username, String password)
this.isRegisteredMbeans = configuration.isRegisterMbeans();
this.isJdbc4ConnectionTest = configuration.isJdbc4ConnectionTest();
this.leakDetectionThreshold = configuration.getLeakDetectionThreshold();
this.closeLeakedConnections = configuration.getCloseLeakedConnections();
this.transactionIsolation = configuration.getTransactionIsolation();
this.isRecordMetrics = configuration.isRecordMetrics();
this.metricsTracker = MetricsFactory.createMetricsTracker((isRecordMetrics ? configuration.getMetricsTrackerClassName()
Expand Down Expand Up @@ -183,7 +185,7 @@ public Connection getConnection() throws SQLException
}

if (leakDetectionThreshold != 0) {
connection.captureStack(leakDetectionThreshold, houseKeepingExecutorService);
connection.captureStack(leakDetectionThreshold, houseKeepingExecutorService, closeLeakedConnections);
}

return connection;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,13 +151,13 @@ public String toString()

/** {@inheritDoc} */
@Override
public final void captureStack(long leakDetectionThreshold, ScheduledExecutorService executorService)
public final void captureStack(long leakDetectionThreshold, ScheduledExecutorService executorService, boolean closeLeakedConnection)
{
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);
leakTask = new LeakTask(leakTrace, leakDetectionThreshold, closeLeakedConnection ? this : null);
executorService.schedule(leakTask, leakDetectionThreshold, TimeUnit.MILLISECONDS);
}

Expand Down Expand Up @@ -312,7 +312,8 @@ public final void close() throws SQLException
isClosed = true;

if (leakTask != null) {
leakTask.cancel();
//force close if leak task cannot be cancelled i.e. leaked connection closing was started
forceClose |= !leakTask.cancel();
leakTask = null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ public interface IHikariConnectionProxy extends Connection, IBagManagable
*
* @param leakThreshold the number of milliseconds before a leak is reported
* @param houseKeepingExecutorService the executor service to run the leak detection task with
* @param closeLeakedConnection if <tt>true</tt> then the connection will be closed after leak threshold is reached
*/
void captureStack(long leakThreshold, ScheduledExecutorService houseKeepingExecutorService);
void captureStack(long leakThreshold, ScheduledExecutorService houseKeepingExecutorService, boolean closeLeakedConnection);

/**
* Check if the provided SQLException contains a SQLSTATE that indicates
Expand Down
39 changes: 35 additions & 4 deletions hikaricp-java6/src/main/java/com/zaxxer/hikari/proxy/LeakTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,36 +16,67 @@

package com.zaxxer.hikari.proxy;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.SQLException;
import java.util.concurrent.atomic.AtomicInteger;

/**
* @author Brett Wooldridge
*/
class LeakTask implements Runnable
{
private static final int CLOSING_NOT_STARTED = 0, CLOSING_STARTED = 1, CLOSING_CANCELLED = 2;

private final long leakTime;
private IHikariConnectionProxy connectionOpt;

private StackTraceElement[] stackTrace;
private final AtomicInteger closingState = new AtomicInteger(CLOSING_NOT_STARTED);


public LeakTask(StackTraceElement[] stackTrace, long leakDetectionThreshold)
public LeakTask(StackTraceElement[] stackTrace, long leakDetectionThreshold, IHikariConnectionProxy connectionOpt)
{
this.stackTrace = stackTrace;
this.leakTime = System.currentTimeMillis() + leakDetectionThreshold;
this.connectionOpt = connectionOpt;
}

/** {@inheritDoc} */
@Override
public void run()
{
if (System.currentTimeMillis() > leakTime) {
Logger log = LoggerFactory.getLogger(LeakTask.class);

Exception e = new Exception();
e.setStackTrace(stackTrace);
LoggerFactory.getLogger(LeakTask.class).warn("Connection leak detection triggered, stack trace follows", e);
log.warn("Connection leak detection triggered, stack trace follows", e);
stackTrace = null;

if (connectionOpt != null && closingState.compareAndSet(CLOSING_NOT_STARTED, CLOSING_STARTED)) {
log.warn("Closing leaked connection");
try {
connectionOpt.close();
} catch (SQLException sqle) {
log.warn(sqle.getMessage(), sqle);
}
}
}
}

public void cancel()
/**
* Cancel leak detection task.
*
* @return false if leaked connection closing process has already started.
*/
public boolean cancel()
{
stackTrace = null;
if (closingState.compareAndSet(CLOSING_NOT_STARTED, CLOSING_CANCELLED)) {
stackTrace = null;
connectionOpt = null;
}
return closingState.get() == CLOSING_CANCELLED;
}
}
20 changes: 20 additions & 0 deletions hikaricp/src/main/java/com/zaxxer/hikari/HikariConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public class HikariConfig implements HikariConfigMBean
private volatile long connectionTimeout;
private volatile long idleTimeout;
private volatile long leakDetectionThreshold;
private volatile boolean closeLeakedConnections;
private volatile long maxLifetime;
private volatile int maxPoolSize;
private volatile int minIdle;
Expand Down Expand Up @@ -484,6 +485,20 @@ public void setLeakDetectionThreshold(long leakDetectionThresholdMs)
this.leakDetectionThreshold = leakDetectionThresholdMs;
}

/** {@inheritDoc} */
@Override
public boolean getCloseLeakedConnections()
{
return closeLeakedConnections;
}

/** {@inheritDoc} */
@Override
public void setCloseLeakedConnections(boolean closeLeakedConnections)
{
this.closeLeakedConnections = closeLeakedConnections;
}

/** {@inheritDoc} */
@Override
public long getMaxLifetime()
Expand Down Expand Up @@ -744,6 +759,11 @@ else if (idleTimeout < TimeUnit.SECONDS.toMillis(30) && idleTimeout != 0) {
leakDetectionThreshold = 0;
}

if (closeLeakedConnections && leakDetectionThreshold == 0) {
logger.warn("closeLeakedConnections is set to 'true' while leakDetectionThreshold is disabled. Disabling autoclosing leaked connections");
closeLeakedConnections = false;
}

if (maxLifetime < 0) {
logger.error("maxLifetime cannot be negative.");
throw new IllegalArgumentException("maxLifetime cannot be negative.");
Expand Down
20 changes: 20 additions & 0 deletions hikaricp/src/main/java/com/zaxxer/hikari/HikariConfigMBean.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,26 @@ public interface HikariConfigMBean
*/
void setLeakDetectionThreshold(long leakDetectionThresholdMs);

/**
* This property controls if unreturned connections must be closed automatically after being used longer than
* the time limit specified by {@link #getLeakDetectionThreshold()} method.
* This property is set to false by default. The <tt>true</tt> value for this property is only valid when
* a positive value for the connection leak detection threshold property is specified.
*
* @return <tt>true</tt> if leaked connections must be closed be the pool
*/
boolean getCloseLeakedConnections();

/**
* This property controls if unreturned connections must be closed automatically after being used longer than
* the time limit specified by {@link #getLeakDetectionThreshold()} method.
* This property is set to false by default. The <tt>true</tt> value for this property is only valid when
* a positive value for the connection leak detection threshold property is specified.
*
* @param closeLeakedConnections if <tt>true</tt> then leaked connections must be closed be the pool
*/
void setCloseLeakedConnections(boolean closeLeakedConnections);

/**
* This property controls the maximum lifetime of a connection in the pool. When a connection reaches this
* timeout, even if recently used, it will be retired from the pool. An in-use connection will never be
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
private final boolean isRegisteredMbeans;
private final boolean isJdbc4ConnectionTest;
private final long leakDetectionThreshold;
private final boolean closeLeakedConnections;
private final String catalog;
private final String username;
private final String password;
Expand Down Expand Up @@ -126,6 +127,7 @@ public HikariPool(HikariConfig configuration, String username, String password)
this.isRegisteredMbeans = configuration.isRegisterMbeans();
this.isJdbc4ConnectionTest = configuration.isJdbc4ConnectionTest();
this.leakDetectionThreshold = configuration.getLeakDetectionThreshold();
this.closeLeakedConnections = configuration.getCloseLeakedConnections();
this.transactionIsolation = configuration.getTransactionIsolation();
this.isRecordMetrics = configuration.isRecordMetrics();
this.metricsTracker = MetricsFactory.createMetricsTracker((isRecordMetrics ? configuration.getMetricsTrackerClassName()
Expand Down Expand Up @@ -183,7 +185,7 @@ public Connection getConnection() throws SQLException
}

if (leakDetectionThreshold != 0) {
connection.captureStack(leakDetectionThreshold, houseKeepingExecutorService);
connection.captureStack(leakDetectionThreshold, houseKeepingExecutorService, closeLeakedConnections);
}

return connection;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
Expand Down Expand Up @@ -151,13 +152,14 @@ public String toString()

/** {@inheritDoc} */
@Override
public final void captureStack(long leakDetectionThreshold, ScheduledExecutorService executorService)
public final void captureStack(long leakDetectionThreshold, ScheduledExecutorService executorService, boolean closeLeakedConnection)
{
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);
Optional<IHikariConnectionProxy> connectionToClose = closeLeakedConnection ? Optional.of(this) : Optional.empty();
leakTask = new LeakTask(leakTrace, leakDetectionThreshold, connectionToClose);
executorService.schedule(leakTask, leakDetectionThreshold, TimeUnit.MILLISECONDS);
}

Expand Down Expand Up @@ -312,7 +314,8 @@ public final void close() throws SQLException
isClosed = true;

if (leakTask != null) {
leakTask.cancel();
//force close if leak task cannot be cancelled i.e. leaked connection closing was started
forceClose |= !leakTask.cancel();
leakTask = null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ public interface IHikariConnectionProxy extends Connection, IBagManagable
*
* @param leakThreshold the number of milliseconds before a leak is reported
* @param houseKeepingExecutorService the executor service to run the leak detection task with
* @param closeLeakedConnection if <tt>true</tt> then the connection will be closed after leak threshold is reached
*/
void captureStack(long leakThreshold, ScheduledExecutorService houseKeepingExecutorService);
void captureStack(long leakThreshold, ScheduledExecutorService houseKeepingExecutorService, boolean closeLeakedConnection);

/**
* Check if the provided SQLException contains a SQLSTATE that indicates
Expand Down
Loading