Tweak the leak task handling and resetConnection() handling for small performance gains in the *nominal* case. Possibly slower in edge-cases (but who cares).

pull/192/head
Brett Wooldridge 11 years ago
parent cf1109b0a5
commit 00b77f9cd3

@ -48,6 +48,7 @@ import com.zaxxer.hikari.metrics.IMetricsTracker.MetricsContext;
import com.zaxxer.hikari.metrics.MetricsFactory; import com.zaxxer.hikari.metrics.MetricsFactory;
import com.zaxxer.hikari.metrics.MetricsTracker; import com.zaxxer.hikari.metrics.MetricsTracker;
import com.zaxxer.hikari.proxy.IHikariConnectionProxy; import com.zaxxer.hikari.proxy.IHikariConnectionProxy;
import com.zaxxer.hikari.proxy.LeakTask;
import com.zaxxer.hikari.proxy.ProxyFactory; import com.zaxxer.hikari.proxy.ProxyFactory;
import com.zaxxer.hikari.util.ConcurrentBag; import com.zaxxer.hikari.util.ConcurrentBag;
import com.zaxxer.hikari.util.ConcurrentBag.IBagStateListener; import com.zaxxer.hikari.util.ConcurrentBag.IBagStateListener;
@ -64,12 +65,13 @@ import com.zaxxer.hikari.util.PropertyBeanSetter;
*/ */
public final class HikariPool implements HikariPoolMBean, IBagStateListener 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 String catalog;
public final boolean isAutoCommit; public final boolean isAutoCommit;
public final boolean isReadOnly; public final boolean isReadOnly;
public int transactionIsolation;
private final DataSource dataSource; private final DataSource dataSource;
@ -95,6 +97,14 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
private volatile long connectionTimeout; private volatile long connectionTimeout;
private volatile boolean isShutdown; 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. * Construct a HikariPool with the specified configuration.
* *
@ -142,8 +152,8 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
HikariMBeanElf.registerMBeans(configuration, this); HikariMBeanElf.registerMBeans(configuration, this);
} }
addConnectionExecutor = createThreadPoolExecutor(configuration.getMaximumPoolSize(), "HikariCP connection filler", configuration.getThreadFactory()); addConnectionExecutor = createThreadPoolExecutor(configuration.getMaximumPoolSize(), "HikariCP connection filler (pool " + configuration.getPoolName() + ")", configuration.getThreadFactory());
closeConnectionExecutor = createThreadPoolExecutor(configuration.getMaximumPoolSize(), "HikariCP connection closer", configuration.getThreadFactory()); closeConnectionExecutor = createThreadPoolExecutor(configuration.getMaximumPoolSize(), "HikariCP connection closer (pool " + configuration.getPoolName() + ")", configuration.getThreadFactory());
fillPool(); fillPool();
@ -181,11 +191,9 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
continue; continue;
} }
final IHikariConnectionProxy proxyConnection = ProxyFactory.getProxyConnection(this, bagEntry); LeakTask leakTask = (leakDetectionThreshold == 0) ? NO_LEAK : new LeakTask(leakDetectionThreshold, houseKeepingExecutorService);
if (leakDetectionThreshold != 0) { final IHikariConnectionProxy proxyConnection = ProxyFactory.getProxyConnection(this, bagEntry, leakTask);
proxyConnection.captureStack(leakDetectionThreshold, houseKeepingExecutorService);
}
if (isRecordMetrics) { if (isRecordMetrics) {
bagEntry.lastOpenTime = now; bagEntry.lastOpenTime = now;

@ -24,8 +24,6 @@ import java.sql.Statement;
import java.sql.Wrapper; import java.sql.Wrapper;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -41,27 +39,28 @@ import com.zaxxer.hikari.util.FastList;
*/ */
public abstract class ConnectionProxy implements IHikariConnectionProxy public abstract class ConnectionProxy implements IHikariConnectionProxy
{ {
private static final Logger LOGGER = LoggerFactory.getLogger(ConnectionProxy.class); private static final Logger LOGGER;
private static final Set<String> SQL_ERRORS; private static final Set<String> SQL_ERRORS;
protected final Connection delegate; protected final Connection delegate;
private final HikariPool parentPool; private final HikariPool parentPool;
private final PoolBagEntry bagEntry; private final PoolBagEntry bagEntry;
private final FastList<Statement> openStatements;
private final LeakTask leakTask;
private boolean forceClose; private boolean forceClose;
private boolean isAnythingDirty;
private boolean isAutoCommitDirty; private boolean isAutoCommitDirty;
private boolean isCatalogDirty; private boolean isCatalogDirty;
private boolean isClosed; private boolean isClosed;
private boolean isReadOnlyDirty; private boolean isReadOnlyDirty;
private boolean isTransactionIsolationDirty; private boolean isTransactionIsolationDirty;
private FastList<Statement> openStatements;
private LeakTask leakTask;
// static initializer // static initializer
static { static {
LOGGER = LoggerFactory.getLogger(ConnectionProxy.class);
SQL_ERRORS = new HashSet<String>(); SQL_ERRORS = new HashSet<String>();
SQL_ERRORS.add("57P01"); // ADMIN SHUTDOWN SQL_ERRORS.add("57P01"); // ADMIN SHUTDOWN
SQL_ERRORS.add("57P02"); // CRASH SHUTDOWN SQL_ERRORS.add("57P02"); // CRASH SHUTDOWN
@ -71,10 +70,11 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
SQL_ERRORS.add("JZ0C1"); // Sybase disconnect error 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.parentPool = pool;
this.bagEntry = bagEntry; this.bagEntry = bagEntry;
this.delegate = bagEntry.connection; this.delegate = bagEntry.connection;
this.leakTask = leakTask;
this.openStatements = new FastList<Statement>(Statement.class, 16); this.openStatements = new FastList<Statement>(Statement.class, 16);
} }
@ -96,18 +96,6 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
return bagEntry; 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} */ /** {@inheritDoc} */
@Override @Override
public final SQLException checkException(SQLException sqle) public final SQLException checkException(SQLException sqle)
@ -184,8 +172,6 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
if (isCatalogDirty && parentPool.catalog != null) { if (isCatalogDirty && parentPool.catalog != null) {
delegate.setCatalog(parentPool.catalog); delegate.setCatalog(parentPool.catalog);
} }
delegate.clearWarnings();
} }
// ********************************************************************** // **********************************************************************
@ -199,10 +185,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
if (!isClosed) { if (!isClosed) {
isClosed = true; isClosed = true;
if (leakTask != null) { leakTask.cancel();
leakTask.cancel();
leakTask = null;
}
try { try {
final int size = openStatements.size(); 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) { catch (SQLException e) {
checkException(e); throw checkException(e);
} }
finally { finally {
parentPool.releaseConnection(bagEntry, forceClose); parentPool.releaseConnection(bagEntry, forceClose);
@ -432,6 +419,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
checkClosed(); checkClosed();
try { try {
delegate.setAutoCommit(autoCommit); delegate.setAutoCommit(autoCommit);
isAnythingDirty = true;
isAutoCommitDirty = (autoCommit != parentPool.isAutoCommit); isAutoCommitDirty = (autoCommit != parentPool.isAutoCommit);
} }
catch (SQLException e) { catch (SQLException e) {
@ -446,6 +434,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
checkClosed(); checkClosed();
try { try {
delegate.setReadOnly(readOnly); delegate.setReadOnly(readOnly);
isAnythingDirty = true;
isReadOnlyDirty = (readOnly != parentPool.isReadOnly); isReadOnlyDirty = (readOnly != parentPool.isReadOnly);
} }
catch (SQLException e) { catch (SQLException e) {
@ -460,6 +449,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
checkClosed(); checkClosed();
try { try {
delegate.setTransactionIsolation(level); delegate.setTransactionIsolation(level);
isAnythingDirty = true;
isTransactionIsolationDirty = (level != parentPool.transactionIsolation); isTransactionIsolationDirty = (level != parentPool.transactionIsolation);
} }
catch (SQLException e) { catch (SQLException e) {
@ -473,6 +463,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
checkClosed(); checkClosed();
try { try {
delegate.setCatalog(catalog); delegate.setCatalog(catalog);
isAnythingDirty = true;
isCatalogDirty = (catalog != null && !catalog.equals(parentPool.catalog)) || (catalog == null && parentPool.catalog != null); isCatalogDirty = (catalog != null && !catalog.equals(parentPool.catalog)) || (catalog == null && parentPool.catalog != null);
} }
catch (SQLException e) { catch (SQLException e) {

@ -19,7 +19,6 @@ package com.zaxxer.hikari.proxy;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.concurrent.ScheduledExecutorService;
import com.zaxxer.hikari.pool.PoolBagEntry; import com.zaxxer.hikari.pool.PoolBagEntry;
@ -38,14 +37,6 @@ public interface IHikariConnectionProxy extends Connection
*/ */
PoolBagEntry getPoolBagEntry(); 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 * Check if the provided SQLException contains a SQLSTATE that indicates
* a disconnection from the server. * a disconnection from the server.
@ -55,27 +46,6 @@ public interface IHikariConnectionProxy extends Connection
*/ */
SQLException checkException(SQLException sqle); 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 * Return the broken state of the connection. If checkException() detected
* a broken connection, this method will return true, otherwise false. * a broken connection, this method will return true, otherwise false.

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,20 +16,36 @@
package com.zaxxer.hikari.proxy; package com.zaxxer.hikari.proxy;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.slf4j.LoggerFactory; 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 * @author Brett Wooldridge
*/ */
class LeakTask implements Runnable public class LeakTask implements Runnable
{ {
private final long leakTime; private final long leakTime;
private final ScheduledFuture<?> scheduledFuture;
private StackTraceElement[] stackTrace; 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; this.leakTime = System.currentTimeMillis() + leakDetectionThreshold;
scheduledFuture = executorService.schedule(this, leakDetectionThreshold, TimeUnit.MILLISECONDS);
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
@ -37,8 +53,11 @@ class LeakTask implements Runnable
public void run() public void run()
{ {
if (System.currentTimeMillis() > leakTime) { if (System.currentTimeMillis() > leakTime) {
StackTraceElement[] trace = new StackTraceElement[stackTrace.length - 3];
System.arraycopy(stackTrace, 4, trace, 0, trace.length);
Exception e = new Exception(); Exception e = new Exception();
e.setStackTrace(stackTrace); e.setStackTrace(trace);
LoggerFactory.getLogger(LeakTask.class).warn("Connection leak detection triggered, stack trace follows", e); LoggerFactory.getLogger(LeakTask.class).warn("Connection leak detection triggered, stack trace follows", e);
stackTrace = null; stackTrace = null;
} }
@ -47,5 +66,8 @@ class LeakTask implements Runnable
public void cancel() public void cancel()
{ {
stackTrace = null; stackTrace = null;
if (scheduledFuture != null) {
scheduledFuture.cancel(false);
}
} }
} }

@ -42,9 +42,10 @@ public final class ProxyFactory
* *
* @param pool the {@link HikariPool} that will own this proxy * @param pool the {@link HikariPool} that will own this proxy
* @param bagEntry the PoolBagEntry entry for 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} * @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 // Body is injected by JavassistProxyFactory
return null; return null;

@ -47,6 +47,7 @@ import com.zaxxer.hikari.metrics.IMetricsTracker.MetricsContext;
import com.zaxxer.hikari.metrics.MetricsFactory; import com.zaxxer.hikari.metrics.MetricsFactory;
import com.zaxxer.hikari.metrics.MetricsTracker; import com.zaxxer.hikari.metrics.MetricsTracker;
import com.zaxxer.hikari.proxy.IHikariConnectionProxy; import com.zaxxer.hikari.proxy.IHikariConnectionProxy;
import com.zaxxer.hikari.proxy.LeakTask;
import com.zaxxer.hikari.proxy.ProxyFactory; import com.zaxxer.hikari.proxy.ProxyFactory;
import com.zaxxer.hikari.util.ConcurrentBag; import com.zaxxer.hikari.util.ConcurrentBag;
import com.zaxxer.hikari.util.ConcurrentBag.IBagStateListener; import com.zaxxer.hikari.util.ConcurrentBag.IBagStateListener;
@ -63,12 +64,13 @@ import com.zaxxer.hikari.util.PropertyBeanSetter;
*/ */
public final class HikariPool implements HikariPoolMBean, IBagStateListener 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 String catalog;
public final boolean isAutoCommit; public final boolean isAutoCommit;
public final boolean isReadOnly; public final boolean isReadOnly;
public int transactionIsolation;
private final DataSource dataSource; private final DataSource dataSource;
@ -94,6 +96,14 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
private volatile long connectionTimeout; private volatile long connectionTimeout;
private volatile boolean isShutdown; 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. * Construct a HikariPool with the specified configuration.
* *
@ -178,11 +188,9 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
continue; continue;
} }
final IHikariConnectionProxy proxyConnection = ProxyFactory.getProxyConnection(this, bagEntry); LeakTask leakTask = (leakDetectionThreshold == 0) ? NO_LEAK : new LeakTask(leakDetectionThreshold, houseKeepingExecutorService);
if (leakDetectionThreshold != 0) { final IHikariConnectionProxy proxyConnection = ProxyFactory.getProxyConnection(this, bagEntry, leakTask);
proxyConnection.captureStack(leakDetectionThreshold, houseKeepingExecutorService);
}
if (isRecordMetrics) { if (isRecordMetrics) {
bagEntry.lastOpenTime = now; bagEntry.lastOpenTime = now;

@ -24,8 +24,6 @@ import java.sql.Statement;
import java.sql.Wrapper; import java.sql.Wrapper;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -41,27 +39,28 @@ import com.zaxxer.hikari.util.FastList;
*/ */
public abstract class ConnectionProxy implements IHikariConnectionProxy public abstract class ConnectionProxy implements IHikariConnectionProxy
{ {
private static final Logger LOGGER = LoggerFactory.getLogger(ConnectionProxy.class); private static final Logger LOGGER;
private static final Set<String> SQL_ERRORS; private static final Set<String> SQL_ERRORS;
protected final Connection delegate; protected final Connection delegate;
private final HikariPool parentPool; private final HikariPool parentPool;
private final PoolBagEntry bagEntry; private final PoolBagEntry bagEntry;
private final FastList<Statement> openStatements;
private final LeakTask leakTask;
private boolean forceClose; private boolean forceClose;
private boolean isAnythingDirty;
private boolean isAutoCommitDirty; private boolean isAutoCommitDirty;
private boolean isCatalogDirty; private boolean isCatalogDirty;
private boolean isClosed; private boolean isClosed;
private boolean isReadOnlyDirty; private boolean isReadOnlyDirty;
private boolean isTransactionIsolationDirty; private boolean isTransactionIsolationDirty;
private FastList<Statement> openStatements;
private LeakTask leakTask;
// static initializer // static initializer
static { static {
LOGGER = LoggerFactory.getLogger(ConnectionProxy.class);
SQL_ERRORS = new HashSet<String>(); SQL_ERRORS = new HashSet<String>();
SQL_ERRORS.add("57P01"); // ADMIN SHUTDOWN SQL_ERRORS.add("57P01"); // ADMIN SHUTDOWN
SQL_ERRORS.add("57P02"); // CRASH SHUTDOWN SQL_ERRORS.add("57P02"); // CRASH SHUTDOWN
@ -71,10 +70,11 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
SQL_ERRORS.add("JZ0C1"); // Sybase disconnect error 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.parentPool = pool;
this.bagEntry = bagEntry; this.bagEntry = bagEntry;
this.delegate = bagEntry.connection; this.delegate = bagEntry.connection;
this.leakTask = leakTask;
this.openStatements = new FastList<Statement>(Statement.class, 16); this.openStatements = new FastList<Statement>(Statement.class, 16);
} }
@ -96,18 +96,6 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
return bagEntry; 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} */ /** {@inheritDoc} */
@Override @Override
public final SQLException checkException(SQLException sqle) public final SQLException checkException(SQLException sqle)
@ -184,8 +172,6 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
if (isCatalogDirty && parentPool.catalog != null) { if (isCatalogDirty && parentPool.catalog != null) {
delegate.setCatalog(parentPool.catalog); delegate.setCatalog(parentPool.catalog);
} }
delegate.clearWarnings();
} }
// ********************************************************************** // **********************************************************************
@ -199,10 +185,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
if (!isClosed) { if (!isClosed) {
isClosed = true; isClosed = true;
if (leakTask != null) { leakTask.cancel();
leakTask.cancel();
leakTask = null;
}
try { try {
final int size = openStatements.size(); 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) { catch (SQLException e) {
throw checkException(e); throw checkException(e);
@ -432,6 +419,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
checkClosed(); checkClosed();
try { try {
delegate.setAutoCommit(autoCommit); delegate.setAutoCommit(autoCommit);
isAnythingDirty = true;
isAutoCommitDirty = (autoCommit != parentPool.isAutoCommit); isAutoCommitDirty = (autoCommit != parentPool.isAutoCommit);
} }
catch (SQLException e) { catch (SQLException e) {
@ -446,6 +434,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
checkClosed(); checkClosed();
try { try {
delegate.setReadOnly(readOnly); delegate.setReadOnly(readOnly);
isAnythingDirty = true;
isReadOnlyDirty = (readOnly != parentPool.isReadOnly); isReadOnlyDirty = (readOnly != parentPool.isReadOnly);
} }
catch (SQLException e) { catch (SQLException e) {
@ -460,6 +449,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
checkClosed(); checkClosed();
try { try {
delegate.setTransactionIsolation(level); delegate.setTransactionIsolation(level);
isAnythingDirty = true;
isTransactionIsolationDirty = (level != parentPool.transactionIsolation); isTransactionIsolationDirty = (level != parentPool.transactionIsolation);
} }
catch (SQLException e) { catch (SQLException e) {
@ -473,6 +463,7 @@ public abstract class ConnectionProxy implements IHikariConnectionProxy
checkClosed(); checkClosed();
try { try {
delegate.setCatalog(catalog); delegate.setCatalog(catalog);
isAnythingDirty = true;
isCatalogDirty = (catalog != null && !catalog.equals(parentPool.catalog)) || (catalog == null && parentPool.catalog != null); isCatalogDirty = (catalog != null && !catalog.equals(parentPool.catalog)) || (catalog == null && parentPool.catalog != null);
} }
catch (SQLException e) { catch (SQLException e) {

@ -19,7 +19,6 @@ package com.zaxxer.hikari.proxy;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.concurrent.ScheduledExecutorService;
import com.zaxxer.hikari.pool.PoolBagEntry; import com.zaxxer.hikari.pool.PoolBagEntry;
@ -38,14 +37,6 @@ public interface IHikariConnectionProxy extends Connection
*/ */
PoolBagEntry getPoolBagEntry(); 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 * Check if the provided SQLException contains a SQLSTATE that indicates
* a disconnection from the server. * a disconnection from the server.

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,20 +16,36 @@
package com.zaxxer.hikari.proxy; package com.zaxxer.hikari.proxy;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.slf4j.LoggerFactory; 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 * @author Brett Wooldridge
*/ */
class LeakTask implements Runnable public class LeakTask implements Runnable
{ {
private final long leakTime; private final long leakTime;
private final ScheduledFuture<?> scheduledFuture;
private StackTraceElement[] stackTrace; 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; this.leakTime = System.currentTimeMillis() + leakDetectionThreshold;
scheduledFuture = executorService.schedule(this, leakDetectionThreshold, TimeUnit.MILLISECONDS);
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
@ -37,8 +53,11 @@ class LeakTask implements Runnable
public void run() public void run()
{ {
if (System.currentTimeMillis() > leakTime) { if (System.currentTimeMillis() > leakTime) {
StackTraceElement[] trace = new StackTraceElement[stackTrace.length - 3];
System.arraycopy(stackTrace, 4, trace, 0, trace.length);
Exception e = new Exception(); Exception e = new Exception();
e.setStackTrace(stackTrace); e.setStackTrace(trace);
LoggerFactory.getLogger(LeakTask.class).warn("Connection leak detection triggered, stack trace follows", e); LoggerFactory.getLogger(LeakTask.class).warn("Connection leak detection triggered, stack trace follows", e);
stackTrace = null; stackTrace = null;
} }
@ -47,5 +66,8 @@ class LeakTask implements Runnable
public void cancel() public void cancel()
{ {
stackTrace = null; stackTrace = null;
if (scheduledFuture != null) {
scheduledFuture.cancel(false);
}
} }
} }

@ -42,9 +42,10 @@ public final class ProxyFactory
* *
* @param pool the {@link HikariPool} that will own this proxy * @param pool the {@link HikariPool} that will own this proxy
* @param bagEntry the PoolBagEntry entry for 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} * @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 // Body is injected by JavassistProxyFactory
return null; return null;

Loading…
Cancel
Save