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.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;

@ -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<String> SQL_ERRORS;
protected final Connection delegate;
private final HikariPool parentPool;
private final PoolBagEntry bagEntry;
private final FastList<Statement> 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<Statement> openStatements;
private LeakTask leakTask;
// static initializer
static {
LOGGER = LoggerFactory.getLogger(ConnectionProxy.class);
SQL_ERRORS = new HashSet<String>();
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>(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) {

@ -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.

@ -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);
}
}
}

@ -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;

@ -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;

@ -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<String> SQL_ERRORS;
protected final Connection delegate;
private final HikariPool parentPool;
private final PoolBagEntry bagEntry;
private final FastList<Statement> 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<Statement> openStatements;
private LeakTask leakTask;
// static initializer
static {
LOGGER = LoggerFactory.getLogger(ConnectionProxy.class);
SQL_ERRORS = new HashSet<String>();
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>(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) {

@ -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.

@ -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);
}
}
}

@ -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;

Loading…
Cancel
Save