Merge pull request #458 from brettwooldridge/experimental

Fixes #452 fixed race condition in the add-connection path
pull/445/merge
Brett Wooldridge 10 years ago
commit a6f2534f4a

@ -47,6 +47,7 @@
<pax.exam.version>4.5.0</pax.exam.version>
<pax.url.version>2.4.1</pax.url.version>
<slf4j.version>1.7.12</slf4j.version>
<log4j.version>2.4.1</log4j.version>
<felix.bundle.plugin.version>2.5.3</felix.bundle.plugin.version>
<felix.version>5.0.0</felix.version>
</properties>
@ -64,10 +65,28 @@
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>${slf4j.version}</version>
<optional>true</optional>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>${log4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
<version>1.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
@ -87,6 +106,16 @@
<version>${hibernate.version}</version>
<scope>provided</scope>
<optional>true</optional>
<exclusions>
<exclusion>
<artifactId>jboss-logging</artifactId>
<groupId>org.jboss.logging</groupId>
</exclusion>
<exclusion>
<artifactId>jboss-logging-annotations</artifactId>
<groupId>org.jboss.logging</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.dropwizard.metrics</groupId>
@ -300,6 +329,7 @@
<argLine>${surefireArgLine}</argLine>
<!-- Skips unit tests if the value of skip.unit.tests property is true -->
<skipTests>${skip.unit.tests}</skipTests>
<reuseForks>false</reuseForks>
</configuration>
</plugin>

@ -16,21 +16,15 @@
package com.zaxxer.hikari.pool;
import static com.zaxxer.hikari.util.ConcurrentBag.IConcurrentBagEntry.STATE_IN_USE;
import static com.zaxxer.hikari.util.ConcurrentBag.IConcurrentBagEntry.STATE_NOT_IN_USE;
import static com.zaxxer.hikari.util.ConcurrentBag.IConcurrentBagEntry.STATE_REMOVED;
import static com.zaxxer.hikari.util.UtilityElf.createThreadPoolExecutor;
import static com.zaxxer.hikari.util.UtilityElf.quietlySleep;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLTransientConnectionException;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadLocalRandom;
@ -55,6 +49,13 @@ import com.zaxxer.hikari.util.ConcurrentBag.IBagStateListener;
import com.zaxxer.hikari.util.DefaultThreadFactory;
import com.zaxxer.hikari.util.SuspendResumeLock;
import static com.zaxxer.hikari.pool.PoolEntry.MAXED_POOL_MARKER;
import static com.zaxxer.hikari.util.ConcurrentBag.IConcurrentBagEntry.STATE_IN_USE;
import static com.zaxxer.hikari.util.ConcurrentBag.IConcurrentBagEntry.STATE_NOT_IN_USE;
import static com.zaxxer.hikari.util.ConcurrentBag.IConcurrentBagEntry.STATE_REMOVED;
import static com.zaxxer.hikari.util.UtilityElf.createThreadPoolExecutor;
import static com.zaxxer.hikari.util.UtilityElf.quietlySleep;
/**
* This is the primary connection pool class that provides the basic
* pooling behavior for HikariCP.
@ -67,8 +68,10 @@ public class HikariPool extends PoolBase implements HikariPoolMXBean, IBagStateL
private static final ClockSource clockSource = ClockSource.INSTANCE;
private static final long ALIVE_BYPASS_WINDOW_MS = Long.getLong("com.zaxxer.hikari.aliveBypassWindowMs", TimeUnit.MILLISECONDS.toMillis(500));
private static final long HOUSEKEEPING_PERIOD_MS = Long.getLong("com.zaxxer.hikari.housekeeping.periodMs", TimeUnit.SECONDS.toMillis(30));
private final long ALIVE_BYPASS_WINDOW_MS = Long.getLong("com.zaxxer.hikari.aliveBypassWindowMs", TimeUnit.MILLISECONDS.toMillis(500));
private final long HOUSEKEEPING_PERIOD_MS = Long.getLong("com.zaxxer.hikari.housekeeping.periodMs", TimeUnit.SECONDS.toMillis(30));
private final AddPoolEntryCallable ADD_POOLENTRY_CALLABLE = new AddPoolEntryCallable();
private static final int POOL_NORMAL = 0;
private static final int POOL_SUSPENDED = 1;
@ -105,6 +108,7 @@ public class HikariPool extends PoolBase implements HikariPoolMXBean, IBagStateL
this.addConnectionExecutor = createThreadPoolExecutor(config.getMaximumPoolSize(), "Hikari connection filler (pool " + poolName + ")", config.getThreadFactory(), new ThreadPoolExecutor.DiscardPolicy());
this.closeConnectionExecutor = createThreadPoolExecutor(4, "Hikari connection closer (pool " + poolName + ")", config.getThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
if (config.getScheduledExecutorService() == null) {
ThreadFactory threadFactory = config.getThreadFactory() != null ? config.getThreadFactory() : new DefaultThreadFactory("Hikari housekeeper (pool " + poolName + ")", true);
this.houseKeepingExecutorService = new ScheduledThreadPoolExecutor(1, threadFactory, new ThreadPoolExecutor.DiscardPolicy());
@ -292,23 +296,7 @@ public class HikariPool extends PoolBase implements HikariPoolMXBean, IBagStateL
@Override
public Future<Boolean> addBagItem()
{
FutureTask<Boolean> future = new FutureTask<>(new Runnable() {
@Override
public void run()
{
long sleepBackoff = 200L;
final int minimumIdle = config.getMinimumIdle();
final int maxPoolSize = config.getMaximumPoolSize();
while (poolState == POOL_NORMAL && totalConnections.get() < maxPoolSize && getIdleConnections() <= minimumIdle && !addConnection()) {
// If we got into the loop, addConnection() failed, so we sleep and retry
quietlySleep(sleepBackoff);
sleepBackoff = Math.min(connectionTimeout / 2, (long) (sleepBackoff * 1.3));
}
}
}, true);
addConnectionExecutor.execute(future);
return future;
return addConnectionExecutor.submit(ADD_POOLENTRY_CALLABLE);
}
// ***********************************************************************
@ -399,6 +387,7 @@ public class HikariPool extends PoolBase implements HikariPoolMXBean, IBagStateL
*
* @param poolEntry the PoolBagEntry to release back to the pool
*/
@Override
final void releaseConnection(final PoolEntry poolEntry)
{
metricsTracker.recordConnectionUsage(poolEntry);
@ -437,18 +426,16 @@ public class HikariPool extends PoolBase implements HikariPoolMXBean, IBagStateL
/**
* Create and add a single connection to the pool.
*/
private boolean addConnection()
private PoolEntry createPoolEntry()
{
// Speculative increment of totalConnections with expectation of success
if (totalConnections.incrementAndGet() > config.getMaximumPoolSize()) {
totalConnections.decrementAndGet(); // Pool is maxed out, so undo speculative increment of totalConnections
//LOGGER.debug("{} - Cannot exceed maximum connections capacity: {}", poolName, config.getMaximumPoolSize());
return true;
return PoolEntry.MAXED_POOL_MARKER;
}
try {
final PoolEntry poolEntry = newPoolEntry();
connectionBag.add(poolEntry);
final long maxLifetime = config.getMaxLifetime();
if (maxLifetime > 0) {
@ -463,14 +450,14 @@ public class HikariPool extends PoolBase implements HikariPoolMXBean, IBagStateL
}
LOGGER.debug("{} - Added connection {}", poolName, poolEntry.connection);
return true;
return poolEntry;
}
catch (Exception e) {
totalConnections.decrementAndGet(); // We failed, so undo speculative increment of totalConnections
if (poolState == POOL_NORMAL) {
LOGGER.debug("{} - Cannot acquire connection from data source", poolName, e);
}
return false;
return null;
}
}
@ -522,16 +509,12 @@ public class HikariPool extends PoolBase implements HikariPoolMXBean, IBagStateL
{
if (config.isInitializationFailFast()) {
try {
if (!addConnection()) {
throw getLastConnectionFailure();
}
final PoolEntry poolEntry = connectionBag.borrow(connectionTimeout, TimeUnit.MILLISECONDS);
Connection connection = getConnection();
if (config.getMinimumIdle() == 0) {
closeConnection(poolEntry, "Closing connection borrowed for validation.");
evictConnection(connection);
}
else {
connectionBag.requite(poolEntry);
connection.close();
}
}
catch (Throwable e) {
@ -576,6 +559,29 @@ public class HikariPool extends PoolBase implements HikariPoolMXBean, IBagStateL
// Non-anonymous Inner-classes
// ***********************************************************************
private class AddPoolEntryCallable implements Callable<Boolean>
{
@Override
public Boolean call() throws Exception
{
long sleepBackoff = 200L;
do {
final PoolEntry poolEntry = createPoolEntry();
if (poolEntry == MAXED_POOL_MARKER) {
return Boolean.FALSE;
}
else if (poolEntry != null) {
connectionBag.add(poolEntry);
return Boolean.TRUE;
}
// addConnection() failed, so we sleep and retry
quietlySleep(sleepBackoff);
sleepBackoff = Math.min(connectionTimeout / 2, (long) (sleepBackoff * 1.3));
} while (true);
}
}
/**
* The house keeping task to retire idle connections.
*/

@ -1,11 +1,11 @@
package com.zaxxer.hikari.pool;
import static com.zaxxer.hikari.util.UtilityElf.createInstance;
import static com.zaxxer.hikari.pool.ProxyConnection.DIRTY_BIT_AUTOCOMMIT;
import static com.zaxxer.hikari.pool.ProxyConnection.DIRTY_BIT_CATALOG;
import static com.zaxxer.hikari.pool.ProxyConnection.DIRTY_BIT_READONLY;
import static com.zaxxer.hikari.pool.ProxyConnection.DIRTY_BIT_ISOLATION;
import static com.zaxxer.hikari.pool.ProxyConnection.DIRTY_BIT_AUTOCOMMIT;
import static com.zaxxer.hikari.pool.ProxyConnection.DIRTY_BIT_NETTIMEOUT;
import static com.zaxxer.hikari.pool.ProxyConnection.DIRTY_BIT_READONLY;
import static com.zaxxer.hikari.util.UtilityElf.createInstance;
import java.lang.management.ManagementFactory;
import java.sql.Connection;
@ -60,7 +60,7 @@ abstract class PoolBase
private final boolean isIsolateInternalQueries;
private final AtomicReference<Throwable> lastConnectionFailure;
private volatile boolean isValidChecked;
private volatile boolean isValidChecked;
PoolBase(final HikariConfig config)
{
@ -202,12 +202,12 @@ abstract class PoolBase
setNetworkTimeout(connection, networkTimeout);
resetBits |= DIRTY_BIT_NETTIMEOUT;
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("{} - Reset ({}) on connection {}", poolName, resetBits != 0 ? stringFromResetBits(resetBits) : "nothing", connection);
}
}
void shutdownNetworkTimeoutExecutor()
{
if (netTimeoutExecutor != null && netTimeoutExecutor instanceof ThreadPoolExecutor) {
@ -494,7 +494,7 @@ abstract class PoolBase
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newCachedThreadPool(threadFactory);
executor.allowCoreThreadTimeOut(true);
executor.setKeepAliveTime(15, TimeUnit.SECONDS);
netTimeoutExecutor = executor;
netTimeoutExecutor = executor;
}
}
@ -534,9 +534,9 @@ abstract class PoolBase
/**
* This will create a string for debug logging. Given a set of "reset bits", this
* method will return a concatenated string, for example:
*
*
* Input : 0b00110
* Output: "autoCommit, isolation"
* Output: "autoCommit, isolation"
*
* @param bits a set of "reset bits"
* @return a string of which states were reset
@ -544,7 +544,7 @@ abstract class PoolBase
private String stringFromResetBits(final int bits)
{
final StringBuilder sb = new StringBuilder();
for (int ndx = 0; ndx < RESET_STATES.length; ndx++) {
for (int ndx = 0; ndx < RESET_STATES.length; ndx++) {
if ( (bits & (0b1 << ndx)) != 0) {
sb.append(RESET_STATES[ndx]).append(", ");
}

@ -39,6 +39,8 @@ final class PoolEntry implements IConcurrentBagEntry
private static final Logger LOGGER = LoggerFactory.getLogger(PoolEntry.class);
static final Comparator<PoolEntry> LASTACCESS_COMPARABLE;
static final PoolEntry MAXED_POOL_MARKER = new PoolEntry();
Connection connection;
long lastAccessed;
long lastBorrowed;
@ -60,18 +62,27 @@ final class PoolEntry implements IConcurrentBagEntry
public int compare(final PoolEntry entryOne, final PoolEntry entryTwo) {
return Long.compare(entryOne.lastAccessed, entryTwo.lastAccessed);
}
};
};
}
private PoolEntry()
{
this.state = null;
this.openStatements = null;
this.hikariPool = null;
this.isReadOnly = false;
this.isAutoCommit = false;
}
PoolEntry(final Connection connection, final PoolBase pool, final boolean isReadOnly, final boolean isAutoCommit)
{
this.connection = connection;
this.hikariPool = (HikariPool) pool;
this.state = new AtomicInteger(STATE_NOT_IN_USE);
this.lastAccessed = ClockSource.INSTANCE.currentTime();
this.openStatements = new FastList<>(Statement.class, 16);
this.isReadOnly = isReadOnly;
this.isAutoCommit = isAutoCommit;
this.state = new AtomicInteger();
this.lastAccessed = ClockSource.INSTANCE.currentTime();
this.openStatements = new FastList<>(Statement.class, 16);
}
/**

@ -19,16 +19,19 @@ import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.zaxxer.hikari.util.ConcurrentBag.IConcurrentBagEntry;
import static com.zaxxer.hikari.util.ConcurrentBag.IConcurrentBagEntry.STATE_NOT_IN_USE;
import static com.zaxxer.hikari.util.ConcurrentBag.IConcurrentBagEntry.STATE_IN_USE;
import static com.zaxxer.hikari.util.ConcurrentBag.IConcurrentBagEntry.STATE_NOT_IN_USE;
import static com.zaxxer.hikari.util.ConcurrentBag.IConcurrentBagEntry.STATE_REMOVED;
import static com.zaxxer.hikari.util.ConcurrentBag.IConcurrentBagEntry.STATE_RESERVED;
@ -63,6 +66,7 @@ public class ConcurrentBag<T extends IConcurrentBagEntry> implements AutoCloseab
private final ThreadLocal<List> threadList;
private final IBagStateListener listener;
private final AtomicInteger waiters;
private volatile boolean closed;
public interface IConcurrentBagEntry
@ -87,15 +91,16 @@ public class ConcurrentBag<T extends IConcurrentBagEntry> implements AutoCloseab
*
* @param listener the IBagStateListener to attach to this bag
*/
public ConcurrentBag(IBagStateListener listener)
public ConcurrentBag(final IBagStateListener listener)
{
this.listener = listener;
this.weakThreadLocals = useWeakThreadLocals();
this.waiters = new AtomicInteger(0);
this.sharedList = new CopyOnWriteArrayList<>();
this.synchronizer = new QueuedSequenceSynchronizer();
if (weakThreadLocals) {
this.threadList = new ThreadLocal<>();
this.threadList = new ThreadLocal<>();
}
else {
this.threadList = new ThreadLocal<List>() {
@ -111,27 +116,20 @@ public class ConcurrentBag<T extends IConcurrentBagEntry> implements AutoCloseab
/**
* The method will borrow a BagEntry from the bag, blocking for the
* specified timeout if none are available.
*
*
* @param timeout how long to wait before giving up, in units of unit
* @param timeUnit a <code>TimeUnit</code> determining how to interpret the timeout parameter
* @return a borrowed instance from the bag or null if a timeout occurs
* @throws InterruptedException if interrupted while waiting
* @throws ExecutionException
* @throws TimeoutException
*/
@SuppressWarnings("unchecked")
public T borrow(long timeout, final TimeUnit timeUnit) throws InterruptedException
{
// Try the thread-local list first, if there are no blocked threads waiting already
List<?> list = threadList.get();
if (weakThreadLocals && list == null) {
list = new ArrayList<>(16);
threadList.set(list);
}
for (int i = list.size() - 1; i >= 0; i--) {
final T bagEntry = (T) (weakThreadLocals ? ((WeakReference) list.remove(i)).get() : list.remove(i));
if (bagEntry != null && bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
return bagEntry;
}
// Try the thread-local list first
T claimed = claimFromThreadLocal();
if (claimed != null) {
return claimed;
}
// Otherwise, scan the shared list ... for maximum of timeout
@ -140,22 +138,34 @@ public class ConcurrentBag<T extends IConcurrentBagEntry> implements AutoCloseab
final long startScan = System.nanoTime();
final long originTimeout = timeout;
long startSeq;
do {
waiters.incrementAndGet();
try {
do {
startSeq = synchronizer.currentSequence();
for (final T bagEntry : sharedList) {
if (bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
return bagEntry;
// scan the shared list
do {
startSeq = synchronizer.currentSequence();
for (T bagEntry : sharedList) {
if (bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
// if we might have stolen another thread's new connection, restart the add...
if (waiters.get() > 1 && addItemFuture == null) {
listener.addBagItem();
}
return bagEntry;
}
}
}
} while (startSeq < synchronizer.currentSequence());
} while (startSeq < synchronizer.currentSequence());
if (addItemFuture == null || addItemFuture.isDone()) {
addItemFuture = listener.addBagItem();
}
if (addItemFuture == null || addItemFuture.isDone()) {
addItemFuture = listener.addBagItem();
}
timeout = originTimeout - (System.nanoTime() - startScan);
} while (timeout > 1000L && synchronizer.waitUntilSequenceExceeded(startSeq, timeout));
timeout = originTimeout - (System.nanoTime() - startScan);
} while (timeout > 10_000L && synchronizer.waitUntilSequenceExceeded(startSeq, timeout));
}
finally {
waiters.decrementAndGet();
}
return null;
}
@ -218,6 +228,8 @@ public class ConcurrentBag<T extends IConcurrentBagEntry> implements AutoCloseab
if (!removed && !closed) {
LOGGER.warn("Attempt to remove an object from the bag that does not exist: {}", bagEntry);
}
// synchronizer.signal();
return removed;
}
@ -329,7 +341,7 @@ public class ConcurrentBag<T extends IConcurrentBagEntry> implements AutoCloseab
/**
* Get the total number of items in the bag.
*
* @return the number of items in the bag
* @return the number of items in the bag
*/
public int size()
{
@ -343,6 +355,25 @@ public class ConcurrentBag<T extends IConcurrentBagEntry> implements AutoCloseab
}
}
private T claimFromThreadLocal()
{
List<?> list = threadList.get();
if (weakThreadLocals && list == null) {
list = new ArrayList<>(16);
threadList.set(list);
}
for (int i = list.size() - 1; i >= 0; i--) {
@SuppressWarnings("unchecked")
final T bagEntry = (T) (weakThreadLocals ? ((WeakReference) list.remove(i)).get() : list.remove(i));
if (bagEntry != null && bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
return bagEntry;
}
}
return null;
}
/**
* Determine whether to use WeakReferences based on whether there is a
* custom ClassLoader implementation sitting between this class and the

@ -50,7 +50,7 @@ public interface Sequence
public static Sequence create()
{
try {
if (Sequence.class.getClassLoader().loadClass("java.util.concurrent.atomic.LongAdder") != null) {
if (Sequence.class.getClassLoader().loadClass("java.util.concurrent.atomic.LongAdder") != null && !Boolean.getBoolean("com.zaxxer.hikari.useAtomicLongSequence")) {
return new Java8Sequence();
}
}

@ -1,83 +1,83 @@
package com.zaxxer.hikari.pool;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.logging.log4j.Level;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
import org.slf4j.LoggerFactory;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.util.ConcurrentBag;
/**
* @author Matthew Tambara (matthew.tambara@liferay.com)
*/
public class AddConnectionRaceConditionTest
public class ConnectionRaceConditionTest
{
private HikariPool _hikariPool;
// @Test
@Test
public void testRaceCondition() throws Exception
{
HikariConfig config = new HikariConfig();
config.setMinimumIdle(0);
config.setMaximumPoolSize(10);
config.setInitializationFailFast(false);
config.setConnectionTimeout(2500);
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
TestElf.setSlf4jLogLevel(ConcurrentBag.class, Level.INFO);
final AtomicReference<Exception> ref = new AtomicReference<Exception>(null);
// Initialize HikariPool with no initial connections and room to grow
try (final HikariDataSource ds = new HikariDataSource(config)) {
_hikariPool = TestElf.getPool(ds);
ExecutorService threadPool = Executors.newFixedThreadPool(2);
for (int i = 0; i < 100000; i++) {
Future<Exception> submit1 = threadPool.submit(new Callable<Exception>() {
for (int i = 0; i < 500_000; i++) {
threadPool.submit(new Callable<Exception>() {
/** {@inheritDoc} */
@Override
public Exception call() throws Exception
{
Connection c2;
try {
c2 = _hikariPool.getConnection(5000);
c2 = ds.getConnection();
ds.evictConnection(c2);
}
catch (SQLException e) {
return e;
catch (Exception e) {
ref.set(e);
}
return null;
}
});
}
Future<Exception> submit2 = threadPool.submit(new Callable<Exception>() {
/** {@inheritDoc} */
@Override
public Exception call() throws Exception
{
Connection c2;
try {
c2 = _hikariPool.getConnection(5000);
ds.evictConnection(c2);
}
catch (SQLException e) {
return e;
}
return null;
}
});
threadPool.shutdown();
threadPool.awaitTermination(30, TimeUnit.SECONDS);
if (submit1.get() != null) {
throw submit1.get();
}
if (submit2.get() != null) {
throw submit2.get();
}
if (ref.get() != null) {
ref.get().fillInStackTrace();
LoggerFactory.getLogger(ConnectionRaceConditionTest.class).error("Submit1 task failed", ref.get());
Assert.fail("Task failed");
}
}
catch (Exception e) {
throw e;
}
}
@After
public void after()
{
System.getProperties().remove("com.zaxxer.hikari.housekeeping.periodMs");
TestElf.setSlf4jLogLevel(HikariPool.class, Level.WARN);
TestElf.setSlf4jLogLevel(ConcurrentBag.class, Level.WARN);
}
}

@ -17,50 +17,44 @@ public class ConnectionStateTest
@Test
public void testAutoCommit() throws SQLException
{
HikariDataSource ds = new HikariDataSource();
ds.setAutoCommit(true);
ds.setMinimumIdle(1);
ds.setMaximumPoolSize(1);
ds.setConnectionTestQuery("VALUES 1");
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
ds.addDataSourceProperty("user", "bar");
ds.addDataSourceProperty("password", "secret");
ds.addDataSourceProperty("url", "baf");
ds.addDataSourceProperty("loginTimeout", "10");
try {
Connection connection = ds.getConnection();
Connection unwrap = connection.unwrap(Connection.class);
connection.setAutoCommit(false);
connection.close();
Assert.assertTrue(unwrap.getAutoCommit());
}
finally {
ds.close();
try (HikariDataSource ds = new HikariDataSource()) {
ds.setAutoCommit(true);
ds.setMinimumIdle(1);
ds.setMaximumPoolSize(1);
ds.setConnectionTestQuery("VALUES 1");
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
ds.addDataSourceProperty("user", "bar");
ds.addDataSourceProperty("password", "secret");
ds.addDataSourceProperty("url", "baf");
ds.addDataSourceProperty("loginTimeout", "10");
try (Connection connection = ds.getConnection()) {
Connection unwrap = connection.unwrap(Connection.class);
connection.setAutoCommit(false);
connection.close();
Assert.assertTrue(unwrap.getAutoCommit());
}
}
}
@Test
public void testTransactionIsolation() throws SQLException
{
HikariDataSource ds = new HikariDataSource();
ds.setTransactionIsolation("TRANSACTION_READ_COMMITTED");
ds.setMinimumIdle(1);
ds.setMaximumPoolSize(1);
ds.setConnectionTestQuery("VALUES 1");
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
try {
Connection connection = ds.getConnection();
Connection unwrap = connection.unwrap(Connection.class);
connection.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
connection.close();
Assert.assertEquals(Connection.TRANSACTION_READ_COMMITTED, unwrap.getTransactionIsolation());
}
finally {
ds.close();
try (HikariDataSource ds = new HikariDataSource()) {
ds.setTransactionIsolation("TRANSACTION_READ_COMMITTED");
ds.setMinimumIdle(1);
ds.setMaximumPoolSize(1);
ds.setConnectionTestQuery("VALUES 1");
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
try (Connection connection = ds.getConnection()) {
Connection unwrap = connection.unwrap(Connection.class);
connection.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
connection.close();
Assert.assertEquals(Connection.TRANSACTION_READ_COMMITTED, unwrap.getTransactionIsolation());
}
}
}
@ -79,89 +73,76 @@ public class ConnectionStateTest
@Test
public void testReadOnly() throws Exception
{
HikariDataSource ds = new HikariDataSource();
ds.setCatalog("test");
ds.setMinimumIdle(1);
ds.setMaximumPoolSize(1);
ds.setConnectionTestQuery("VALUES 1");
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
try {
Connection connection = ds.getConnection();
Connection unwrap = connection.unwrap(Connection.class);
connection.setReadOnly(true);
connection.close();
Assert.assertFalse(unwrap.isReadOnly());
}
finally {
ds.close();
try (HikariDataSource ds = new HikariDataSource()) {
ds.setCatalog("test");
ds.setMinimumIdle(1);
ds.setMaximumPoolSize(1);
ds.setConnectionTestQuery("VALUES 1");
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
try (Connection connection = ds.getConnection()) {
Connection unwrap = connection.unwrap(Connection.class);
connection.setReadOnly(true);
connection.close();
Assert.assertFalse(unwrap.isReadOnly());
}
}
}
@Test
public void testCatalog() throws SQLException
{
HikariDataSource ds = new HikariDataSource();
ds.setCatalog("test");
ds.setMinimumIdle(1);
ds.setMaximumPoolSize(1);
ds.setConnectionTestQuery("VALUES 1");
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
try {
Connection connection = ds.getConnection();
Connection unwrap = connection.unwrap(Connection.class);
connection.setCatalog("other");
connection.close();
Assert.assertEquals("test", unwrap.getCatalog());
}
finally {
ds.close();
try (HikariDataSource ds = new HikariDataSource()) {
ds.setCatalog("test");
ds.setMinimumIdle(1);
ds.setMaximumPoolSize(1);
ds.setConnectionTestQuery("VALUES 1");
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
try (Connection connection = ds.getConnection()) {
Connection unwrap = connection.unwrap(Connection.class);
connection.setCatalog("other");
connection.close();
Assert.assertEquals("test", unwrap.getCatalog());
}
}
}
@Test
public void testCommitTracking() throws SQLException
{
HikariDataSource ds = new HikariDataSource();
ds.setAutoCommit(false);
ds.setMinimumIdle(1);
ds.setMaximumPoolSize(1);
ds.setConnectionTestQuery("VALUES 1");
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
try (HikariDataSource ds = new HikariDataSource()) {
ds.setAutoCommit(false);
ds.setMinimumIdle(1);
ds.setMaximumPoolSize(1);
ds.setConnectionTestQuery("VALUES 1");
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
try {
Connection connection = ds.getConnection();
try (Connection connection = ds.getConnection()) {
Statement statement = connection.createStatement();
statement.execute("SELECT something");
Assert.assertTrue(TestElf.getConnectionCommitDirtyState(connection));
Statement statement = connection.createStatement();
statement.execute("SELECT something");
Assert.assertTrue(TestElf.getConnectionCommitDirtyState(connection));
connection.commit();
Assert.assertFalse(TestElf.getConnectionCommitDirtyState(connection));
connection.commit();
Assert.assertFalse(TestElf.getConnectionCommitDirtyState(connection));
statement.execute("SELECT something", Statement.NO_GENERATED_KEYS);
Assert.assertTrue(TestElf.getConnectionCommitDirtyState(connection));
statement.execute("SELECT something", Statement.NO_GENERATED_KEYS);
Assert.assertTrue(TestElf.getConnectionCommitDirtyState(connection));
connection.rollback();
Assert.assertFalse(TestElf.getConnectionCommitDirtyState(connection));
connection.rollback();
Assert.assertFalse(TestElf.getConnectionCommitDirtyState(connection));
ResultSet resultSet = statement.executeQuery("SELECT something");
Assert.assertTrue(TestElf.getConnectionCommitDirtyState(connection));
ResultSet resultSet = statement.executeQuery("SELECT something");
Assert.assertTrue(TestElf.getConnectionCommitDirtyState(connection));
connection.rollback(null);
Assert.assertFalse(TestElf.getConnectionCommitDirtyState(connection));
connection.rollback(null);
Assert.assertFalse(TestElf.getConnectionCommitDirtyState(connection));
resultSet.updateRow();
Assert.assertTrue(TestElf.getConnectionCommitDirtyState(connection));
connection.close();
}
finally {
ds.close();
resultSet.updateRow();
Assert.assertTrue(TestElf.getConnectionCommitDirtyState(connection));
}
}
}
}

@ -10,54 +10,42 @@ import com.zaxxer.hikari.HikariDataSource;
public class IsolationTest
{
@Test
public void testIsolation() throws SQLException
{
HikariDataSource ds = new HikariDataSource();
ds.setMinimumIdle(1);
ds.setMaximumPoolSize(1);
ds.setIsolateInternalQueries(true);
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
try
{
Connection connection = ds.getConnection();
connection.close();
Connection connection2 = ds.getConnection();
connection2.close();
Assert.assertNotSame(connection, connection2);
Assert.assertSame(connection.unwrap(Connection.class), connection2.unwrap(Connection.class));
}
finally
{
ds.close();
}
}
@Test
public void testNonIsolation() throws SQLException
{
HikariDataSource ds = new HikariDataSource();
ds.setMinimumIdle(1);
ds.setMaximumPoolSize(1);
ds.setIsolateInternalQueries(false);
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
try
{
Connection connection = ds.getConnection();
connection.close();
Connection connection2 = ds.getConnection();
connection2.close();
Assert.assertSame(connection.unwrap(Connection.class), connection2.unwrap(Connection.class));
}
finally
{
ds.close();
}
}
@Test
public void testIsolation() throws SQLException
{
try (HikariDataSource ds = new HikariDataSource()) {
ds.setMinimumIdle(1);
ds.setMaximumPoolSize(1);
ds.setIsolateInternalQueries(true);
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
Connection connection = ds.getConnection();
connection.close();
Connection connection2 = ds.getConnection();
connection2.close();
Assert.assertNotSame(connection, connection2);
Assert.assertSame(connection.unwrap(Connection.class), connection2.unwrap(Connection.class));
}
}
@Test
public void testNonIsolation() throws SQLException
{
try (HikariDataSource ds = new HikariDataSource()) {
ds.setMinimumIdle(1);
ds.setMaximumPoolSize(1);
ds.setIsolateInternalQueries(false);
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
Connection connection = ds.getConnection();
connection.close();
Connection connection2 = ds.getConnection();
connection2.close();
Assert.assertSame(connection.unwrap(Connection.class), connection2.unwrap(Connection.class));
}
}
}

@ -24,13 +24,12 @@ import java.sql.SQLException;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.apache.logging.log4j.Level;
import org.junit.Assert;
import org.junit.Test;
import org.slf4j.spi.LocationAwareLogger;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.pool.HikariPool;
import com.zaxxer.hikari.util.UtilityElf;
/**
@ -105,7 +104,7 @@ public class MiscTest
final HikariDataSource ds = new HikariDataSource(config);
try {
TestElf.setSlf4jLogLevel(HikariPool.class, LocationAwareLogger.DEBUG_INT);
TestElf.setSlf4jLogLevel(HikariPool.class, Level.DEBUG);
TestElf.getPool(ds).logPoolState();
Connection connection = ds.getConnection();
@ -115,7 +114,7 @@ public class MiscTest
ps.close();
String s = new String(baos.toByteArray());
Assert.assertNotNull("Exception string was null", s);
Assert.assertTrue("Expected exception to contain 'Apparent connection leak detected' but contains *" + s + "*", s.contains("Apparent connection leak detected"));
Assert.assertTrue("Expected exception to contain 'Connection leak detection' but contains *" + s + "*", s.contains("Connection leak detection"));
}
finally
{

@ -23,28 +23,27 @@ public class RampUpDown
System.setProperty("com.zaxxer.hikari.housekeeping.periodMs", "250");
HikariDataSource ds = new HikariDataSource(config);
ds.setIdleTimeout(1000);
try (HikariDataSource ds = new HikariDataSource(config)) {
ds.setIdleTimeout(1000);
Assert.assertSame("Totals connections not as expected", 5, TestElf.getPool(ds).getTotalConnections());
Assert.assertSame("Totals connections not as expected", 5, TestElf.getPool(ds).getTotalConnections());
Connection[] connections = new Connection[ds.getMaximumPoolSize()];
for (int i = 0; i < connections.length; i++)
{
connections[i] = ds.getConnection();
}
Assert.assertSame("Totals connections not as expected", 60, TestElf.getPool(ds).getTotalConnections());
Connection[] connections = new Connection[ds.getMaximumPoolSize()];
for (int i = 0; i < connections.length; i++)
{
connections[i] = ds.getConnection();
}
for (Connection connection : connections)
{
connection.close();
}
Assert.assertSame("Totals connections not as expected", 60, TestElf.getPool(ds).getTotalConnections());
Thread.sleep(2500);
for (Connection connection : connections)
{
connection.close();
}
Assert.assertSame("Totals connections not as expected", 5, TestElf.getPool(ds).getTotalConnections());
Thread.sleep(2500);
ds.close();
Assert.assertSame("Totals connections not as expected", 5, TestElf.getPool(ds).getTotalConnections());
}
}
}

@ -29,7 +29,6 @@ import org.junit.Test;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.mocks.StubConnection;
import com.zaxxer.hikari.pool.HikariPool;
import com.zaxxer.hikari.util.ClockSource;
import com.zaxxer.hikari.util.UtilityElf;
@ -70,6 +69,7 @@ public class ShutdownTest
Thread[] threads = new Thread[10];
for (int i = 0; i < 10; i++) {
threads[i] = new Thread() {
@Override
public void run()
{
try {
@ -132,7 +132,7 @@ public class ShutdownTest
Assert.assertSame("StubConnection count not as expected", 0, StubConnection.count.get());
StubConnection.slowCreate = false;
HikariConfig config = new HikariConfig();
config.setMinimumIdle(5);
config.setMaximumPoolSize(5);
@ -263,6 +263,7 @@ public class ShutdownTest
for (int i = 0; i < 4; i++) {
final HikariDataSource ds = new HikariDataSource(config);
Thread t = new Thread() {
@Override
public void run() {
Connection connection = null;
try {
@ -299,8 +300,9 @@ public class ShutdownTest
};
};
t.start();
Thread t2 = new Thread() {
@Override
public void run() {
UtilityElf.quietlySleep(100);
try {

@ -18,10 +18,8 @@ package com.zaxxer.hikari.pool;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.junit.AfterClass;
import org.junit.Assert;
@ -30,8 +28,6 @@ import org.junit.Test;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.pool.HikariPool;
import com.zaxxer.hikari.pool.PoolEntry;
import com.zaxxer.hikari.util.ConcurrentBag;
import com.zaxxer.hikari.util.ConcurrentBag.IBagStateListener;
@ -54,7 +50,7 @@ public class TestConcurrentBag
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
ds = new HikariDataSource(config);
ds = new HikariDataSource(config);
pool = TestElf.getPool(ds);
}
@ -67,41 +63,11 @@ public class TestConcurrentBag
@Test
public void testConcurrentBag() throws Exception
{
ConcurrentBag<PoolEntry> bag = new ConcurrentBag<PoolEntry>(new IBagStateListener() {
ConcurrentBag<PoolEntry> bag = new ConcurrentBag<PoolEntry>( new IBagStateListener() {
@Override
public Future<Boolean> addBagItem()
{
return new Future<Boolean>() {
@Override
public boolean isDone()
{
return true;
}
@Override
public boolean isCancelled()
{
return false;
}
@Override
public Boolean get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
{
return null;
}
@Override
public Boolean get() throws InterruptedException, ExecutionException
{
return true;
}
@Override
public boolean cancel(boolean mayInterruptIfRunning)
{
return false;
}
};
return null;
}
});
Assert.assertEquals(0, bag.values(8).size());
@ -113,7 +79,7 @@ public class TestConcurrentBag
PoolEntry inuse = pool.newPoolEntry();
bag.add(inuse);
bag.borrow(2, TimeUnit.MILLISECONDS); // in use
PoolEntry notinuse = pool.newPoolEntry();
bag.add(notinuse); // not in use
@ -122,7 +88,7 @@ public class TestConcurrentBag
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos, true);
TestElf.setSlf4jTargetStream(ConcurrentBag.class, ps);
bag.requite(reserved);
bag.remove(notinuse);

@ -1,5 +1,5 @@
/**
*
*
*/
package com.zaxxer.hikari.pool;
@ -29,7 +29,7 @@ import com.zaxxer.hikari.util.UtilityElf;
*
*/
public class TestConnectionCloseBlocking {
private volatile boolean shouldSleep = true;
private volatile boolean shouldSleep = false;
@Test
public void testConnectionCloseBlocking() throws SQLException {
@ -39,16 +39,17 @@ public class TestConnectionCloseBlocking {
config.setConnectionTimeout(1500);
config.setDataSource(new CustomMockDataSource());
HikariDataSource ds = new HikariDataSource(config);
long start = ClockSource.INSTANCE.currentTime();
try {
try (HikariDataSource ds = new HikariDataSource(config)) {
Connection connection = ds.getConnection();
connection.close();
// Hikari only checks for validity for connections with lastAccess > 1000 ms so we sleep for 1001 ms to force
// Hikari to do a connection validation which will fail and will trigger the connection to be closed
UtilityElf.quietlySleep(1001);
start = ClockSource.INSTANCE.currentTime();
shouldSleep = true;
connection = ds.getConnection(); // on physical connection close we sleep 2 seconds
Assert.assertTrue("Waited longer than timeout",
(ClockSource.INSTANCE.elapsedMillis(start) < config.getConnectionTimeout()));
@ -57,7 +58,6 @@ public class TestConnectionCloseBlocking {
(ClockSource.INSTANCE.elapsedMillis(start) < config.getConnectionTimeout()));
} finally {
shouldSleep = false;
ds.close();
}
}

@ -8,10 +8,12 @@ import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.apache.logging.log4j.Level;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import com.sun.media.jfxmedia.logging.Logger;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.mocks.StubConnection;
@ -66,6 +68,7 @@ public class TestConnectionTimeoutRetry
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.schedule(new Runnable() {
@Override
public void run()
{
stubDataSource.setThrowException(null);
@ -109,6 +112,7 @@ public class TestConnectionTimeoutRetry
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
scheduler.schedule(new Runnable() {
@Override
public void run()
{
try {
@ -183,6 +187,7 @@ public class TestConnectionTimeoutRetry
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
scheduler.schedule(new Runnable() {
@Override
public void run()
{
try {
@ -221,8 +226,8 @@ public class TestConnectionTimeoutRetry
HikariConfig config = new HikariConfig();
config.setMinimumIdle(5);
config.setMaximumPoolSize(10);
config.setConnectionTimeout(1000);
config.setValidationTimeout(1000);
config.setConnectionTimeout(2000);
config.setValidationTimeout(2000);
config.setConnectionTestQuery("VALUES 2");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
@ -231,10 +236,10 @@ public class TestConnectionTimeoutRetry
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos, true);
TestElf.setSlf4jTargetStream(HikariPool.class, ps);
TestElf.setSlf4jLogLevel(HikariPool.class, Logger.DEBUG);
boolean success = false;
try (HikariDataSource ds = new HikariDataSource(config)) {
TestElf.setSlf4jLogLevel(HikariPool.class, Level.DEBUG);
Connection connection1 = ds.getConnection();
Connection connection2 = ds.getConnection();
Connection connection3 = ds.getConnection();
@ -243,7 +248,7 @@ public class TestConnectionTimeoutRetry
Connection connection6 = ds.getConnection();
Connection connection7 = ds.getConnection();
Thread.sleep(900);
Thread.sleep(1300);
Assert.assertSame("Total connections not as expected", 10, TestElf.getPool(ds).getTotalConnections());
Assert.assertSame("Idle connections not as expected", 3, TestElf.getPool(ds).getIdleConnections());
@ -258,14 +263,19 @@ public class TestConnectionTimeoutRetry
Assert.assertSame("Totals connections not as expected", 10, TestElf.getPool(ds).getTotalConnections());
Assert.assertSame("Idle connections not as expected", 10, TestElf.getPool(ds).getIdleConnections());
success = true;
}
finally {
TestElf.setSlf4jLogLevel(HikariPool.class, Logger.INFO);
System.getProperties().remove("com.zaxxer.hikari.housekeeping.periodMs");
if (!success) {
System.err.println(new String(baos.toByteArray()));
}
}
}
@Before
public void before()
{
TestElf.setSlf4jTargetStream(HikariPool.class, System.err);
}
@After
public void after()
{
System.getProperties().remove("com.zaxxer.hikari.housekeeping.periodMs");
TestElf.setSlf4jLogLevel(HikariPool.class, Level.WARN);
}
}

@ -24,30 +24,40 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.logging.log4j.Level;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.spi.LocationAwareLogger;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.mocks.StubConnection;
import com.zaxxer.hikari.mocks.StubDataSource;
import com.zaxxer.hikari.mocks.StubStatement;
import com.zaxxer.hikari.pool.HikariPool;
import com.zaxxer.hikari.pool.HikariPool.PoolInitializationException;
import com.zaxxer.hikari.util.UtilityElf;
/**
* System property testProxy can be one of:
* "com.zaxxer.hikari.JavaProxyFactory"
* "com.zaxxer.hikari.CglibProxyFactory"
* "com.zaxxer.hikari.JavassistProxyFactory"
*
* @author Brett Wooldridge
*/
public class TestConnections
{
@Before
public void before()
{
TestElf.setSlf4jTargetStream(HikariPool.class, System.err);
}
@After
public void after()
{
System.getProperties().remove("com.zaxxer.hikari.housekeeping.periodMs");
TestElf.setSlf4jLogLevel(HikariPool.class, Level.DEBUG);
}
@Test
public void testCreate() throws SQLException
{
@ -57,13 +67,14 @@ public class TestConnections
config.setConnectionTestQuery("VALUES 1");
config.setConnectionInitSql("SELECT 1");
config.setReadOnly(true);
config.setLeakDetectionThreshold(TimeUnit.SECONDS.toMillis(60));
config.setConnectionTimeout(2500);
config.setLeakDetectionThreshold(TimeUnit.SECONDS.toMillis(30));
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
HikariDataSource ds = new HikariDataSource(config);
ds.setLoginTimeout(10);
Assert.assertSame(10, ds.getLoginTimeout());
try {
try (HikariDataSource ds = new HikariDataSource(config)) {
ds.setLoginTimeout(10);
Assert.assertSame(10, ds.getLoginTimeout());
Assert.assertSame("Totals connections not as expected", 1, TestElf.getPool(ds).getTotalConnections());
Assert.assertSame("Idle connections not as expected", 1, TestElf.getPool(ds).getIdleConnections());
@ -90,9 +101,6 @@ public class TestConnections
Assert.assertSame("Totals connections not as expected", 1, TestElf.getPool(ds).getTotalConnections());
Assert.assertSame("Idle connections not as expected", 1, TestElf.getPool(ds).getIdleConnections());
}
finally {
ds.close();
}
}
@Test
@ -101,15 +109,14 @@ public class TestConnections
HikariConfig config = new HikariConfig();
config.setMinimumIdle(0);
config.setMaximumPoolSize(1);
config.setConnectionTimeout(2500);
config.setConnectionTestQuery("VALUES 1");
config.setInitializationFailFast(false);
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
System.setProperty("com.zaxxer.hikari.housekeeping.periodMs", "100");
HikariDataSource ds = new HikariDataSource(config);
try {
try (HikariDataSource ds = new HikariDataSource(config)) {
System.clearProperty("com.zaxxer.hikari.housekeeping.periodMs");
ds.setMaxLifetime(700);
@ -144,10 +151,6 @@ public class TestConnections
Assert.assertSame("Post total connections not as expected", 1, TestElf.getPool(ds).getTotalConnections());
Assert.assertSame("Post idle connections not as expected", 1, TestElf.getPool(ds).getIdleConnections());
}
finally {
ds.close();
System.getProperties().remove("com.zaxxer.hikari.housekeeping.periodMs");
}
}
@Test
@ -156,12 +159,13 @@ public class TestConnections
HikariConfig config = new HikariConfig();
config.setMinimumIdle(0);
config.setMaximumPoolSize(1);
config.setConnectionTimeout(2500);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
HikariDataSource ds = new HikariDataSource(config);
System.setProperty("com.zaxxer.hikari.housekeeping.periodMs", "100");
try {
try (HikariDataSource ds = new HikariDataSource(config)) {
ds.setMaxLifetime(700);
Assert.assertSame("Total connections not as expected", 0, TestElf.getPool(ds).getTotalConnections());
@ -194,9 +198,6 @@ public class TestConnections
Assert.assertSame("Post total connections not as expected", 1, TestElf.getPool(ds).getTotalConnections());
Assert.assertSame("Post idle connections not as expected", 1, TestElf.getPool(ds).getIdleConnections());
}
finally {
ds.close();
}
}
@Test
@ -205,11 +206,11 @@ public class TestConnections
HikariConfig config = new HikariConfig();
config.setMinimumIdle(1);
config.setMaximumPoolSize(1);
config.setConnectionTimeout(2500);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
HikariDataSource ds = new HikariDataSource(config);
try {
try (HikariDataSource ds = new HikariDataSource(config)) {
Connection connection = ds.getConnection();
connection.close();
@ -222,9 +223,6 @@ public class TestConnections
connection.close();
}
finally {
ds.close();
}
}
@Test
@ -233,20 +231,17 @@ public class TestConnections
HikariConfig config = new HikariConfig();
config.setMinimumIdle(0);
config.setMaximumPoolSize(5);
config.setConnectionTimeout(2500);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
HikariDataSource ds = new HikariDataSource(config);
try {
try (HikariDataSource ds = new HikariDataSource(config)) {
Connection connection = ds.getConnection();
Assert.assertEquals(1, TestElf.getPool(ds).getTotalConnections());
ds.evictConnection(connection);
Assert.assertEquals(0, TestElf.getPool(ds).getTotalConnections());
}
finally {
ds.close();
}
}
@Test
@ -260,8 +255,10 @@ public class TestConnections
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
HikariDataSource ds = new HikariDataSource(config);
try {
try (HikariDataSource ds = new HikariDataSource(config)) {
TestElf.setSlf4jLogLevel(HikariPool.class, Level.DEBUG);
UtilityElf.quietlySleep(500);
Assert.assertSame("Totals connections not as expected", 1, TestElf.getPool(ds).getTotalConnections());
@ -288,9 +285,15 @@ public class TestConnections
Assert.assertSame(SQLException.class, e.getClass());
}
TestElf.getPool(ds).logPoolState("testBackfill() before close...");
// The connection will be ejected from the pool here
connection.close();
UtilityElf.quietlySleep(500);
TestElf.getPool(ds).logPoolState("testBackfill() after close...");
Assert.assertSame("Totals connections not as expected", 0, TestElf.getPool(ds).getTotalConnections());
Assert.assertSame("Idle connections not as expected", 0, TestElf.getPool(ds).getIdleConnections());
@ -301,9 +304,6 @@ public class TestConnections
Assert.assertTrue("Totals connections not as expected", TestElf.getPool(ds).getTotalConnections() > 0);
Assert.assertTrue("Idle connections not as expected", TestElf.getPool(ds).getIdleConnections() > 0);
}
finally {
ds.close();
}
}
@Test
@ -312,27 +312,34 @@ public class TestConnections
HikariConfig config = new HikariConfig();
config.setMinimumIdle(1);
config.setMaximumPoolSize(4);
config.setConnectionTimeout(TimeUnit.MINUTES.toMillis(1));
config.setConnectionTimeout(20000);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
StubConnection.count.set(0);
final HikariDataSource ds = new HikariDataSource(config);
try {
final AtomicReference<Exception> ref = new AtomicReference<>();
try (final HikariDataSource ds = new HikariDataSource(config)) {
TestElf.setSlf4jLogLevel(HikariPool.class, Level.DEBUG);
Thread[] threads = new Thread[20];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(new Runnable() {
@Override
public void run()
{
try {
HikariPool pool = TestElf.getPool(ds);
pool.logPoolState("Before acquire");
Connection connection = ds.getConnection();
pool.logPoolState("After acquire");
quietlySleep(500);
connection.close();
}
catch (Exception e) {
e.printStackTrace();
ref.set(e);
}
}
});
@ -346,11 +353,9 @@ public class TestConnections
threads[i].join();
}
Assert.assertNull((ref.get() != null ? ref.get().toString() : ""), ref.get());
Assert.assertEquals(4, StubConnection.count.get());
}
finally {
ds.close();
}
}
@Test
@ -359,13 +364,13 @@ public class TestConnections
HikariConfig config = new HikariConfig();
config.setMinimumIdle(1);
config.setMaximumPoolSize(1);
config.setConnectionTimeout(2500);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
StubConnection.oldDriver = true;
StubStatement.oldDriver = true;
HikariDataSource ds = new HikariDataSource(config);
try {
try (HikariDataSource ds = new HikariDataSource(config)) {
quietlySleep(500);
Connection connection = ds.getConnection();
@ -377,7 +382,6 @@ public class TestConnections
finally {
StubConnection.oldDriver = false;
StubStatement.oldDriver = false;
ds.close();
}
}
@ -387,20 +391,19 @@ public class TestConnections
HikariConfig config = new HikariConfig();
config.setMinimumIdle(3);
config.setMaximumPoolSize(3);
config.setConnectionTimeout(2500);
config.setAllowPoolSuspension(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
TestElf.setSlf4jLogLevel(HikariPool.class, LocationAwareLogger.DEBUG_INT);
final HikariDataSource ds = new HikariDataSource(config);
try {
try (final HikariDataSource ds = new HikariDataSource(config)) {
HikariPool pool = TestElf.getPool(ds);
while (pool.getTotalConnections() < 3) {
quietlySleep(50);
}
Thread t = new Thread(new Runnable() {
@Override
public void run()
{
try {
@ -427,9 +430,6 @@ public class TestConnections
quietlySleep(500);
Assert.assertEquals(1, pool.getIdleConnections());
}
finally {
ds.close();
}
}
@Test
@ -438,21 +438,20 @@ public class TestConnections
StubDataSource stubDataSource = new StubDataSource();
stubDataSource.setThrowException(new SQLException("Connection refused"));
HikariDataSource ds = new HikariDataSource();
ds.setMinimumIdle(3);
ds.setMaximumPoolSize(3);
ds.setAllowPoolSuspension(true);
ds.setConnectionTestQuery("VALUES 1");
ds.setDataSource(stubDataSource);
try (HikariDataSource ds = new HikariDataSource()) {
ds.setMinimumIdle(3);
ds.setMaximumPoolSize(3);
ds.setConnectionTimeout(2500);
ds.setAllowPoolSuspension(true);
ds.setConnectionTestQuery("VALUES 1");
ds.setDataSource(stubDataSource);
try (Connection c = ds.getConnection()) {
Assert.fail("Initialization should have failed");
}
catch (PoolInitializationException e) {
// passed
}
finally {
ds.close();
try (Connection c = ds.getConnection()) {
Assert.fail("Initialization should have failed");
}
catch (PoolInitializationException e) {
// passed
}
}
}

@ -21,13 +21,17 @@ import java.lang.reflect.Field;
import java.sql.Connection;
import java.util.HashMap;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.layout.CsvLogEventLayout;
import org.apache.logging.slf4j.Log4jLogger;
import org.slf4j.LoggerFactory;
import org.slf4j.impl.SimpleLogger;
import com.zaxxer.hikari.pool.ProxyConnection;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.pool.HikariPool;
/**
* Utility methods for testing.
@ -37,7 +41,7 @@ import com.zaxxer.hikari.pool.HikariPool;
public final class TestElf
{
private TestElf() {
// default constructor
// default constructor
}
public static HikariPool getPool(HikariDataSource ds)
@ -74,7 +78,7 @@ public final class TestElf
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public static void setConfigUnitTest(boolean unitTest)
@ -86,32 +90,61 @@ public final class TestElf
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public static void setSlf4jTargetStream(Class<?> clazz, PrintStream stream)
{
SimpleLogger simpleLogger = (SimpleLogger) LoggerFactory.getLogger(clazz);
try {
Field field = clazz.getClassLoader().loadClass("org.slf4j.impl.SimpleLogger").getDeclaredField("TARGET_STREAM");
Log4jLogger log4Jlogger = (Log4jLogger) LoggerFactory.getLogger(clazz);
Field field = clazz.getClassLoader().loadClass("org.apache.logging.slf4j.Log4jLogger").getDeclaredField("logger");
field.setAccessible(true);
field.set(simpleLogger, stream);
Logger logger = (Logger) field.get(log4Jlogger);
if (logger.getAppenders().containsKey("string")) {
Appender appender = logger.getAppenders().get("string");
logger.removeAppender(appender);
}
logger.addAppender(new StringAppender("string", stream));
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void setSlf4jLogLevel(Class<?> clazz, int logLevel)
public static void setSlf4jLogLevel(Class<?> clazz, Level logLevel)
{
SimpleLogger simpleLogger = (SimpleLogger) LoggerFactory.getLogger(clazz);
try {
Field field = clazz.getClassLoader().loadClass("org.slf4j.impl.SimpleLogger").getDeclaredField("currentLogLevel");
Log4jLogger log4Jlogger = (Log4jLogger) LoggerFactory.getLogger(clazz);
Field field = clazz.getClassLoader().loadClass("org.apache.logging.slf4j.Log4jLogger").getDeclaredField("logger");
field.setAccessible(true);
field.setInt(simpleLogger, logLevel);
Logger logger = (Logger) field.get(log4Jlogger);
logger.setLevel(logLevel);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
private static class StringAppender extends AbstractAppender
{
private static final long serialVersionUID = -1932433845656444920L;
private PrintStream stream;
StringAppender(String name, PrintStream stream)
{
super(name, null, CsvLogEventLayout.createDefaultLayout());
this.stream = stream;
}
@Override
public void append(LogEvent event)
{
stream.println(event.getMessage().getFormattedMessage());
}
}
}

@ -57,8 +57,7 @@ public class TestMetrics
config.setPoolName("test");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
HikariDataSource ds = new HikariDataSource(config);
try {
try (HikariDataSource ds = new HikariDataSource(config)) {
ds.getConnection().close();
Timer timer = metricRegistry.getTimers(new MetricFilter() {
@ -73,9 +72,6 @@ public class TestMetrics
Assert.assertEquals(1, timer.getCount());
Assert.assertTrue(timer.getMeanRate() > 0.0);
}
finally {
ds.close();
}
}
@Test
@ -91,8 +87,7 @@ public class TestMetrics
config.setPoolName("test");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
HikariDataSource ds = new HikariDataSource(config);
try {
try (HikariDataSource ds = new HikariDataSource(config)) {
Connection connection = ds.getConnection();
UtilityElf.quietlySleep(250L);
connection.close();
@ -110,9 +105,6 @@ public class TestMetrics
double seventyFifth = histo.getSnapshot().get75thPercentile();
Assert.assertTrue("Seventy-fith percentile less than 250ms: " + seventyFifth, seventyFifth >= 250.0);
}
finally {
ds.close();
}
}
@Test
@ -128,10 +120,9 @@ public class TestMetrics
config.setPoolName("test");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
config.addHealthCheckProperty("connectivityCheckTimeoutMs", "1000");
config.addHealthCheckProperty("expected99thPercentileMs", "10");
config.addHealthCheckProperty("expected99thPercentileMs", "100");
HikariDataSource ds = new HikariDataSource(config);
try {
try (HikariDataSource ds = new HikariDataSource(config)) {
UtilityElf.quietlySleep(TimeUnit.SECONDS.toMillis(2));
Connection connection = ds.getConnection();
@ -148,9 +139,6 @@ public class TestMetrics
Result slaResult = healthChecks.get("test.pool.Connection99Percent");
Assert.assertTrue(slaResult.isHealthy());
}
finally {
ds.close();
}
}
@Test
@ -159,7 +147,7 @@ public class TestMetrics
HikariDataSource ds = new HikariDataSource();
ds.setMaximumPoolSize(1);
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
MetricRegistry metricRegistry = new MetricRegistry();
HealthCheckRegistry healthRegistry = new HealthCheckRegistry();
@ -196,7 +184,7 @@ public class TestMetrics
HikariDataSource ds = new HikariDataSource();
ds.setMaximumPoolSize(1);
ds.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
MetricRegistry metricRegistry = new MetricRegistry();
HealthCheckRegistry healthRegistry = new HealthCheckRegistry();
@ -216,7 +204,7 @@ public class TestMetrics
Assert.fail("Should not have been allowed to set registry after pool started");
}
catch (IllegalStateException ise) {
// pass
}
finally {
ds.close();

@ -144,7 +144,9 @@ public class TestValidation
config.setMaxLifetime(TimeUnit.MINUTES.toMillis(2));
config.setIdleTimeout(TimeUnit.MINUTES.toMillis(3));
config.validate();
Assert.assertTrue(new String(baos.toByteArray()).contains("greater than maxLifetime"));
String s = new String(baos.toByteArray());
Assert.assertTrue("Expected exception to contain 'greater than maxLifetime' but contains *" + s + "*", s.contains("greater than maxLifetime"));
}
@Test
@ -202,7 +204,9 @@ public class TestValidation
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
config.setDataSource(new StubDataSource());
config.validate();
Assert.assertTrue(new String(baos.toByteArray()).contains("using dataSource"));
String s = new String(baos.toByteArray());
Assert.assertTrue("Expected exception to contain 'using dataSource' but contains *" + s + "*", s.contains("using dataSource"));
}
catch (IllegalStateException ise) {
Assert.fail();

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="OFF">
<appenders>
<Console name="Console" target="SYSTEM_ERR">
<PatternLayout pattern="%6sn [%-26.26t] %-5level %-20c{1} - %msg%n" />
</Console>
</appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console" />
</Root>
</Loggers>
</configuration>
Loading…
Cancel
Save