More performance tweaks.

pull/1/head
Brett Wooldridge 11 years ago
parent 69a6ff2fb5
commit a2eee447b2

@ -20,11 +20,8 @@ import java.lang.management.ManagementFactory;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@ -42,19 +39,25 @@ import com.zaxxer.hikari.proxy.JavassistProxyFactoryFactory;
import com.zaxxer.hikari.util.ClassLoaderUtils;
import com.zaxxer.hikari.util.PropertyBeanSetter;
/**
* This is the primary connection pool class that provides the basic
* pooling behavior for HikariCP.
*
* @author Brett Wooldridge
*/
public class HikariPool implements HikariPoolMBean
{
private static final Logger LOGGER = LoggerFactory.getLogger(HikariPool.class);
private final HikariConfig configuration;
private final LinkedTransferQueue<IHikariConnectionProxy> idleConnections;
private final Set<IHikariConnectionProxy> inUseConnections;
private final AtomicInteger totalConnections;
private final AtomicInteger idleConnectionCount;
private final DataSource dataSource;
private final long leakDetectionThreshold;
private final boolean jdbc4ConnectionTest;
private volatile boolean delegationProxies;
private final boolean delegationProxies;
private final Timer houseKeepingTimer;
@ -71,9 +74,9 @@ public class HikariPool implements HikariPoolMBean
this.totalConnections = new AtomicInteger();
this.idleConnectionCount = new AtomicInteger();
this.idleConnections = new LinkedTransferQueue<IHikariConnectionProxy>();
this.inUseConnections = Collections.newSetFromMap(new ConcurrentHashMap<IHikariConnectionProxy, Boolean>(configuration.getMaximumPoolSize() * 2, 0.75f, 100));
this.jdbc4ConnectionTest = configuration.isJdbc4ConnectionTest();
this.leakDetectionThreshold = configuration.getLeakDetectionThreshold();
try
{
@ -82,9 +85,9 @@ public class HikariPool implements HikariPoolMBean
PropertyBeanSetter.setTargetFromProperties(dataSource, configuration.getDataSourceProperties());
HikariInstrumentationAgent instrumentationAgent = new HikariInstrumentationAgent(dataSource);
if (false || !instrumentationAgent.loadTransformerAgent())
delegationProxies = !instrumentationAgent.loadTransformerAgent();
if (delegationProxies)
{
delegationProxies = true;
LOGGER.info("Falling back to Javassist delegate-based proxies.");
}
}
@ -106,6 +109,12 @@ public class HikariPool implements HikariPoolMBean
fillPool();
}
/**
* Get a connection from the pool, or timeout trying.
*
* @return a java.sql.Connection instance
* @throws SQLException thrown if a timeout occurs trying to obtain a connection
*/
Connection getConnection() throws SQLException
{
try
@ -131,32 +140,33 @@ public class HikariPool implements HikariPoolMBean
final long maxLifetime = configuration.getMaxLifetime();
if (maxLifetime > 0 && start - connectionProxy.getCreationTime() > maxLifetime)
{
// Throw away the connection that has passed its lifetime
// Throw away the connection that has passed its lifetime, try again
closeConnection(connectionProxy);
timeout -= (System.currentTimeMillis() - start);
continue;
}
connectionProxy.unclose();
Connection connection = (Connection) connectionProxy;
if (!isConnectionAlive(connection, timeout))
{
// Throw away the dead connection
// Throw away the dead connection, try again
closeConnection(connectionProxy);
timeout -= (System.currentTimeMillis() - start);
continue;
}
if (configuration.getLeakDetectionThreshold() > 0)
if (leakDetectionThreshold > 0)
{
connectionProxy.captureStack(configuration.getLeakDetectionThreshold(), houseKeepingTimer);
connectionProxy.captureStack(leakDetectionThreshold, houseKeepingTimer);
}
connectionProxy.unclose();
inUseConnections.add(connectionProxy);
return connection;
} while (true);
} while (timeout > 0);
throw new SQLException("Timeout of encountered waiting for connection");
}
catch (InterruptedException e)
{
@ -164,14 +174,14 @@ public class HikariPool implements HikariPoolMBean
}
}
/**
* Release a connection back to the pool, or permanently close it if it
* is broken.
*
* @param connectionProxy the connection to release back to the pool
*/
public void releaseConnection(IHikariConnectionProxy connectionProxy)
{
boolean existing = inUseConnections.remove(connectionProxy);
if (!existing)
{
LOGGER.warn("Internal pool state inconsistency", new Throwable());
}
if (!connectionProxy.isBrokenConnection())
{
connectionProxy.setLastAccess(System.currentTimeMillis());
@ -184,6 +194,10 @@ public class HikariPool implements HikariPoolMBean
}
}
// ***********************************************************************
// HikariPoolMBean methods
// ***********************************************************************
/** {@inheritDoc} */
public int getActiveConnections()
{
@ -226,6 +240,13 @@ public class HikariPool implements HikariPoolMBean
}
}
// ***********************************************************************
// Private methods
// ***********************************************************************
/**
* Fill the pool up to the minimum size.
*/
private void fillPool()
{
int maxIters = (configuration.getMinimumPoolSize() / configuration.getAcquireIncrement()) + 1;
@ -235,16 +256,22 @@ public class HikariPool implements HikariPoolMBean
}
}
/**
* Add connections to the pool, not exceeding the maximum allowed.
*/
private synchronized void addConnections()
{
final int max = configuration.getMaximumPoolSize();
final int increment = configuration.getAcquireIncrement();
for (int i = 0; i < increment && totalConnections.get() < max; i++)
for (int i = 0; totalConnections.get() < max && i < increment; i++)
{
addConnection();
}
}
/**
* Create and add a single connection to the pool.
*/
private void addConnection()
{
int retries = 0;
@ -298,8 +325,21 @@ public class HikariPool implements HikariPoolMBean
}
}
private boolean isConnectionAlive(Connection connection, long timeoutMs)
/**
* Check whether the connection is alive or not.
*
* @param connection the connection to test
* @param timeoutMs the timeout before we consider the test a failure
* @return true if the connection is alive, false if it is not alive or we timed out
*/
private boolean isConnectionAlive(final Connection connection, long timeoutMs)
{
// Set a realistic minimum timeout
if (timeoutMs < 500)
{
timeoutMs = 500;
}
try
{
if (jdbc4ConnectionTest)
@ -311,12 +351,13 @@ public class HikariPool implements HikariPoolMBean
try
{
statement.executeQuery(configuration.getConnectionTestQuery());
return true;
}
finally
{
statement.close();
}
return true;
}
catch (SQLException e)
{
@ -325,6 +366,11 @@ public class HikariPool implements HikariPoolMBean
}
}
/**
* Permanently close a connection.
*
* @param connectionProxy the connection to actually close
*/
private void closeConnection(IHikariConnectionProxy connectionProxy)
{
try
@ -338,6 +384,9 @@ public class HikariPool implements HikariPoolMBean
}
}
/**
* Register the pool and pool configuration objects with the MBean server.
*/
private void registerMBean()
{
try
@ -362,6 +411,9 @@ public class HikariPool implements HikariPoolMBean
}
}
/**
* The house keeping task to retire idle and maxAge connections.
*/
private class HouseKeeper extends TimerTask
{
public void run()

@ -112,19 +112,19 @@ public class HikariClassTransformer implements ClassFileTransformer
}
else if (iface.equals("java.sql.PreparedStatement"))
{
return transformPreparedStatement(classFile);
return transformClass(classFile, "com.zaxxer.hikari.proxy.PreparedStatementProxy", "com.zaxxer.hikari.proxy.IHikariStatementProxy");
}
else if (iface.equals("java.sql.CallableStatement"))
{
return transformCallableStatement(classFile);
return transformClass(classFile, "com.zaxxer.hikari.proxy.CallableStatementProxy", "com.zaxxer.hikari.proxy.IHikariStatementProxy");
}
else if (iface.equals("java.sql.Statement"))
{
return transformStatement(classFile);
return transformClass(classFile, "com.zaxxer.hikari.proxy.StatementProxy", "com.zaxxer.hikari.proxy.IHikariStatementProxy");
}
else if (iface.equals("java.sql.ResultSet"))
{
return transformResultSet(classFile);
return transformClass(classFile, "com.zaxxer.hikari.proxy.ResultSetProxy", "com.zaxxer.hikari.proxy.IHikariResultSetProxy");
}
}
@ -161,6 +161,7 @@ public class HikariClassTransformer implements ClassFileTransformer
copyFields(proxy, target);
copyMethods(proxy, target, classFile);
mergeClassInitializers(proxy, target, classFile);
specialConnectionInjectCloseCheck(target);
injectTryCatch(target);
for (CtConstructor constructor : target.getConstructors())
@ -175,85 +176,16 @@ public class HikariClassTransformer implements ClassFileTransformer
/**
* @param classFile
*/
private byte[] transformPreparedStatement(ClassFile classFile) throws Exception
private byte[] transformClass(ClassFile classFile, String proxyClassName, String intfName) throws Exception
{
String className = classFile.getName();
CtClass target = classPool.getCtClass(className);
CtClass intf = classPool.get("com.zaxxer.hikari.proxy.IHikariStatementProxy");
CtClass intf = classPool.get(intfName);
target.addInterface(intf);
LOGGER.debug("Added interface {} to {}", intf.getName(), className);
CtClass proxy = classPool.get("com.zaxxer.hikari.proxy.PreparedStatementProxy");
copyFields(proxy, target);
copyMethods(proxy, target, classFile);
mergeClassInitializers(proxy, target, classFile);
injectTryCatch(target);
target.debugWriteFile("/tmp");
return target.toBytecode();
}
/**
* @param classFile
*/
private byte[] transformCallableStatement(ClassFile classFile) throws Exception
{
String className = classFile.getName();
CtClass target = classPool.getCtClass(className);
CtClass intf = classPool.get("com.zaxxer.hikari.proxy.IHikariStatementProxy");
target.addInterface(intf);
LOGGER.debug("Added interface {} to {}", intf.getName(), className);
CtClass proxy = classPool.get("com.zaxxer.hikari.proxy.CallableStatementProxy");
copyFields(proxy, target);
copyMethods(proxy, target, classFile);
mergeClassInitializers(proxy, target, classFile);
injectTryCatch(target);
target.debugWriteFile("/tmp");
return target.toBytecode();
}
/**
* @param classFile
*/
private byte[] transformStatement(ClassFile classFile) throws Exception
{
String className = classFile.getName();
CtClass target = classPool.getCtClass(className);
CtClass intf = classPool.get("com.zaxxer.hikari.proxy.IHikariStatementProxy");
target.addInterface(intf);
LOGGER.debug("Added interface {} to {}", intf.getName(), className);
CtClass proxy = classPool.get("com.zaxxer.hikari.proxy.StatementProxy");
copyFields(proxy, target);
copyMethods(proxy, target, classFile);
mergeClassInitializers(proxy, target, classFile);
injectTryCatch(target);
target.debugWriteFile("/tmp");
return target.toBytecode();
}
/**
* @param classFile
*/
private byte[] transformResultSet(ClassFile classFile) throws Exception
{
String className = classFile.getName();
CtClass target = classPool.getCtClass(className);
CtClass intf = classPool.get("com.zaxxer.hikari.proxy.IHikariResultSetProxy");
target.addInterface(intf);
LOGGER.debug("Added interface {} to {}", intf.getName(), className);
CtClass proxy = classPool.get("com.zaxxer.hikari.proxy.ResultSetProxy");
CtClass proxy = classPool.get(proxyClassName);
copyFields(proxy, target);
copyMethods(proxy, target, classFile);
@ -359,6 +291,11 @@ public class HikariClassTransformer implements ClassFileTransformer
continue;
}
if (method.getMethodInfo().getCodeAttribute() == null)
{
continue;
}
for (CtClass exception : method.getExceptionTypes())
{
if ("java.sql.SQLException".equals(exception.getName())) // only add try..catch to methods throwing SQLException
@ -370,6 +307,32 @@ public class HikariClassTransformer implements ClassFileTransformer
}
}
private void specialConnectionInjectCloseCheck(CtClass destClass) throws Exception
{
for (CtMethod method : destClass.getMethods())
{
if ((method.getModifiers() & Modifier.PUBLIC) != Modifier.PUBLIC || // only public methods
method.getAnnotation(HikariInject.class) != null) // ignore methods we've injected, they already try..catch
{
continue;
}
if (method.getMethodInfo().getCodeAttribute() == null)
{
continue;
}
for (CtClass exception : method.getExceptionTypes())
{
if ("java.sql.SQLException".equals(exception.getName())) // only add check to methods throwing SQLException
{
method.insertBefore("if (_isClosed) { throw new java.sql.SQLException(\"Connection is closed\"); }");
break;
}
}
}
}
/**
*
*/

@ -26,7 +26,6 @@ import java.util.HashSet;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicBoolean;
import com.zaxxer.hikari.HikariPool;
import com.zaxxer.hikari.javassist.HikariInject;
@ -55,7 +54,7 @@ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectio
@HikariInject private static final Set<String> SPECIAL_ERRORS;
@HikariInject private ArrayList<Statement> _openStatements;
@HikariInject private AtomicBoolean _isClosed;
@HikariInject private volatile boolean _isClosed;
@HikariInject private HikariPool _parentPool;
protected final Connection delegate;
@ -96,7 +95,7 @@ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectio
// If the connection is not closed. If it is closed, it means this is being
// called back as a result of the close() method below in which case we
// will clear the openStatements collection en mass.
if (!_isClosed.get())
if (!_isClosed)
{
_openStatements.remove(statement);
}
@ -129,7 +128,7 @@ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectio
@HikariInject
public void unclose()
{
_isClosed.set(false);
_isClosed = false;
}
@HikariInject
@ -175,10 +174,18 @@ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectio
private void __init()
{
_openStatements = new ArrayList<Statement>(64);
_isClosed = new AtomicBoolean();
_creationTime = _lastAccess = System.currentTimeMillis();
}
@HikariInject
private void checkClosed() throws SQLException
{
if (_isClosed)
{
throw new SQLException("Connection is closed");
}
}
public final Connection getDelegate()
{
return delegate;
@ -191,8 +198,9 @@ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectio
@HikariInject
public void close() throws SQLException
{
if (_isClosed.compareAndSet(false, true))
if (!_isClosed)
{
_isClosed = true;
if (_leakTask != null)
{
_leakTask.cancel();
@ -201,7 +209,6 @@ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectio
try
{
// Faster than an iterator
for (int i = _openStatements.size() - 1; i >= 0; i--)
{
@ -223,12 +230,13 @@ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectio
@HikariInject
public boolean isClosed() throws SQLException
{
return _isClosed.get();
return _isClosed;
}
@HikariInject
public Statement createStatement() throws SQLException
{
checkClosed();
try
{
Statement statementProxy = __createStatement();
@ -246,6 +254,7 @@ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectio
@HikariInject
public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException
{
checkClosed();
try
{
Statement statementProxy = __createStatement(resultSetType, resultSetConcurrency);
@ -263,6 +272,7 @@ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectio
@HikariInject
public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException
{
checkClosed();
try
{
Statement statementProxy = __createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
@ -280,6 +290,7 @@ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectio
@HikariInject
public CallableStatement prepareCall(String sql) throws SQLException
{
checkClosed();
try
{
CallableStatement statementProxy = __prepareCall(sql);
@ -297,6 +308,7 @@ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectio
@HikariInject
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException
{
checkClosed();
try
{
CallableStatement statementProxy = __prepareCall(sql, resultSetType, resultSetConcurrency);
@ -314,6 +326,7 @@ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectio
@HikariInject
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException
{
checkClosed();
try
{
CallableStatement statementProxy = __prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
@ -331,6 +344,7 @@ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectio
@HikariInject
public PreparedStatement prepareStatement(String sql) throws SQLException
{
checkClosed();
try
{
PreparedStatement statementProxy = __prepareStatement(sql);
@ -348,6 +362,7 @@ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectio
@HikariInject
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException
{
checkClosed();
try
{
PreparedStatement statementProxy = __prepareStatement(sql, autoGeneratedKeys);
@ -365,6 +380,7 @@ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectio
@HikariInject
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException
{
checkClosed();
try
{
PreparedStatement statementProxy = __prepareStatement(sql, resultSetType, resultSetConcurrency);
@ -382,6 +398,7 @@ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectio
@HikariInject
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException
{
checkClosed();
try
{
PreparedStatement statementProxy = __prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
@ -399,6 +416,7 @@ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectio
@HikariInject
public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException
{
checkClosed();
try
{
PreparedStatement statementProxy = __prepareStatement(sql, columnIndexes);
@ -416,6 +434,7 @@ public class ConnectionProxy extends HikariProxyBase implements IHikariConnectio
@HikariInject
public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException
{
checkClosed();
try
{
PreparedStatement statementProxy = __prepareStatement(sql, columnNames);

@ -18,15 +18,12 @@ package com.zaxxer.hikari.proxy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.SQLException;
/**
* @author Brett Wooldridge
*/
public abstract class HikariProxyBase
{
protected abstract SQLException checkException(SQLException e);
protected static boolean isWrapperFor(Object obj, Class<?> param)
{
try

@ -39,6 +39,7 @@ public class CreationTest
{
HikariConfig config = new HikariConfig();
config.setMinimumPoolSize(1);
config.setMaximumPoolSize(1);
config.setAcquireIncrement(1);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
@ -79,6 +80,7 @@ public class CreationTest
{
HikariConfig config = new HikariConfig();
config.setMinimumPoolSize(1);
config.setMaximumPoolSize(1);
config.setAcquireIncrement(1);
config.setMaxLifetime(500);
config.setConnectionTestQuery("VALUES 1");
@ -118,6 +120,7 @@ public class CreationTest
{
HikariConfig config = new HikariConfig();
config.setMinimumPoolSize(1);
config.setMaximumPoolSize(1);
config.setAcquireIncrement(1);
config.setMaxLifetime(500);
config.setConnectionTestQuery("VALUES 1");

@ -105,7 +105,7 @@ public class Benchmark2
HikariDataSource ds = new HikariDataSource(config);
return ds;
}
private DataSource setupBone()
{
try

Loading…
Cancel
Save