Cleaner pool size accounting.

pull/739/head
Brett Wooldridge 9 years ago
parent 166d791f62
commit ac08f5c16b

@ -36,7 +36,6 @@ import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -77,7 +76,6 @@ public class HikariPool extends PoolBase implements HikariPoolMXBean, IBagStateL
private final long HOUSEKEEPING_PERIOD_MS = Long.getLong("com.zaxxer.hikari.housekeeping.periodMs", SECONDS.toMillis(30));
private final PoolEntryCreator POOL_ENTRY_CREATOR = new PoolEntryCreator();
private final AtomicInteger totalConnections;
private final ThreadPoolExecutor addConnectionExecutor;
private final ThreadPoolExecutor closeConnectionExecutor;
private final ScheduledThreadPoolExecutor houseKeepingExecutorService;
@ -99,7 +97,6 @@ public class HikariPool extends PoolBase implements HikariPoolMXBean, IBagStateL
super(config);
this.connectionBag = new ConcurrentBag<>(this);
this.totalConnections = new AtomicInteger();
this.suspendResumeLock = config.isAllowPoolSuspension() ? new SuspendResumeLock() : SuspendResumeLock.FAUX_LOCK;
checkFailFast();
@ -403,7 +400,7 @@ public class HikariPool extends PoolBase implements HikariPoolMXBean, IBagStateL
final void closeConnection(final PoolEntry poolEntry, final String closureReason)
{
if (connectionBag.remove(poolEntry)) {
final int tc = totalConnections.decrementAndGet();
final int tc = getTotalConnections();
if (tc < 0) {
LOGGER.warn("{} - Unexpected value of totalConnections={}", poolName, tc, new Exception());
}
@ -412,6 +409,9 @@ public class HikariPool extends PoolBase implements HikariPoolMXBean, IBagStateL
@Override
public void run() {
quietlyCloseConnection(connection, closureReason);
if (poolState == POOL_NORMAL) {
fillPool();
}
}
});
}
@ -456,9 +456,9 @@ public class HikariPool extends PoolBase implements HikariPoolMXBean, IBagStateL
/**
* Fill pool up from current idle connections (as they are perceived at the point of execution) to minimumIdle connections.
*/
private void fillPool()
private synchronized void fillPool()
{
final int connectionsToAdd = Math.min(config.getMaximumPoolSize() - totalConnections.get(), config.getMinimumIdle() - getIdleConnections())
final int connectionsToAdd = Math.min(config.getMaximumPoolSize() - getTotalConnections(), config.getMinimumIdle() - getIdleConnections())
- addConnectionExecutor.getQueue().size();
for (int i = 0; i < connectionsToAdd; i++) {
addBagItem();
@ -488,9 +488,7 @@ public class HikariPool extends PoolBase implements HikariPoolMXBean, IBagStateL
quietlyCloseConnection(connection, "(connection aborted during shutdown)");
}
finally {
if (connectionBag.remove(poolEntry)) {
totalConnections.decrementAndGet();
}
connectionBag.remove(poolEntry);
}
}
}
@ -566,10 +564,9 @@ public class HikariPool extends PoolBase implements HikariPoolMXBean, IBagStateL
public Boolean call() throws Exception
{
long sleepBackoff = 250L;
while (poolState == POOL_NORMAL && totalConnections.get() < config.getMaximumPoolSize()) {
while (poolState == POOL_NORMAL && getTotalConnections() < config.getMaximumPoolSize()) {
final PoolEntry poolEntry = createPoolEntry();
if (poolEntry != null) {
totalConnections.incrementAndGet();
connectionBag.add(poolEntry);
return Boolean.TRUE;
}

@ -348,8 +348,14 @@ abstract class PoolBase
return connection;
}
catch (Exception e) {
if (connection != null) {
quietlyCloseConnection(connection, "(Failed to create/setup connection)");
}
else if (getLastConnectionFailure() == null) {
LOGGER.debug("{} - Failed to create/setup connection: {}", poolName, e.getMessage());
}
lastConnectionFailure.set(e);
quietlyCloseConnection(connection, "(Failed to create/set connection)");
throw e;
}
}

@ -107,7 +107,7 @@ public final class UtilityElf
}
LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(queueSize);
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 5, SECONDS, queue, threadFactory, policy);
ThreadPoolExecutor executor = new ThreadPoolExecutor(1 /*core*/, 1 /*max*/, 5 /*keepalive*/, SECONDS, queue, threadFactory, policy);
executor.allowCoreThreadTimeOut(true);
return executor;
}

@ -83,25 +83,23 @@ public class TestConnections
Assert.assertSame("Total connections not as expected", 1, pool.getTotalConnections());
Assert.assertSame("Idle connections not as expected", 1, pool.getIdleConnections());
Connection connection = ds.getConnection();
Assert.assertNotNull(connection);
Assert.assertSame("Total connections not as expected", 1, pool.getTotalConnections());
Assert.assertSame("Idle connections not as expected", 0, pool.getIdleConnections());
PreparedStatement statement = connection.prepareStatement("SELECT * FROM device WHERE device_id=?");
Assert.assertNotNull(statement);
statement.setInt(1, 0);
ResultSet resultSet = statement.executeQuery();
Assert.assertNotNull(resultSet);
Assert.assertFalse(resultSet.next());
resultSet.close();
statement.close();
connection.close();
try (Connection connection = ds.getConnection();
PreparedStatement statement = connection.prepareStatement("SELECT * FROM device WHERE device_id=?")) {
Assert.assertNotNull(connection);
Assert.assertNotNull(statement);
Assert.assertSame("Total connections not as expected", 1, pool.getTotalConnections());
Assert.assertSame("Idle connections not as expected", 0, pool.getIdleConnections());
statement.setInt(1, 0);
try (ResultSet resultSet = statement.executeQuery()) {
Assert.assertNotNull(resultSet);
Assert.assertFalse(resultSet.next());
}
}
Assert.assertSame("Total connections not as expected", 1, pool.getTotalConnections());
Assert.assertSame("Idle connections not as expected", 1, pool.getIdleConnections());
@ -132,29 +130,31 @@ public class TestConnections
Assert.assertSame("Total connections not as expected", 0, pool.getTotalConnections());
Assert.assertSame("Idle connections not as expected", 0, pool.getIdleConnections());
Connection connection = ds.getConnection();
Connection unwrap = connection.unwrap(Connection.class);
Assert.assertNotNull(connection);
Assert.assertSame("Second total connections not as expected", 1, pool.getTotalConnections());
Assert.assertSame("Second idle connections not as expected", 0, pool.getIdleConnections());
connection.close();
Connection unwrap;
Connection unwrap2;
try (Connection connection = ds.getConnection()) {
unwrap = connection.unwrap(Connection.class);
Assert.assertNotNull(connection);
Assert.assertSame("Second total connections not as expected", 1, pool.getTotalConnections());
Assert.assertSame("Second idle connections not as expected", 0, pool.getIdleConnections());
}
Assert.assertSame("Idle connections not as expected", 1, pool.getIdleConnections());
Connection connection2 = ds.getConnection();
Connection unwrap2 = connection2.unwrap(Connection.class);
Assert.assertSame(unwrap, unwrap2);
Assert.assertSame("Second total connections not as expected", 1, pool.getTotalConnections());
Assert.assertSame("Second idle connections not as expected", 0, pool.getIdleConnections());
connection2.close();
try (Connection connection = ds.getConnection()) {
unwrap2 = connection.unwrap(Connection.class);
Assert.assertSame(unwrap, unwrap2);
Assert.assertSame("Second total connections not as expected", 1, pool.getTotalConnections());
Assert.assertSame("Second idle connections not as expected", 0, pool.getIdleConnections());
}
quietlySleep(TimeUnit.SECONDS.toMillis(2));
connection2 = ds.getConnection();
Assert.assertNotSame("Expected a different connection", connection, connection2);
connection2.close();
try (Connection connection = ds.getConnection()) {
unwrap2 = connection.unwrap(Connection.class);
Assert.assertNotSame("Expected a different connection", unwrap, unwrap2);
}
Assert.assertSame("Post total connections not as expected", 1, pool.getTotalConnections());
Assert.assertSame("Post idle connections not as expected", 1, pool.getIdleConnections());
@ -184,29 +184,31 @@ public class TestConnections
Assert.assertSame("Total connections not as expected", 0, pool.getTotalConnections());
Assert.assertSame("Idle connections not as expected", 0, pool.getIdleConnections());
Connection connection = ds.getConnection();
Connection unwrap = connection.unwrap(Connection.class);
Assert.assertNotNull(connection);
Assert.assertSame("Second total connections not as expected", 1, pool.getTotalConnections());
Assert.assertSame("Second idle connections not as expected", 0, pool.getIdleConnections());
connection.close();
Connection unwrap;
Connection unwrap2;
try (Connection connection = ds.getConnection()) {
unwrap = connection.unwrap(Connection.class);
Assert.assertNotNull(connection);
Assert.assertSame("Second total connections not as expected", 1, pool.getTotalConnections());
Assert.assertSame("Second idle connections not as expected", 0, pool.getIdleConnections());
}
Assert.assertSame("Idle connections not as expected", 1, pool.getIdleConnections());
Connection connection2 = ds.getConnection();
Connection unwrap2 = connection2.unwrap(Connection.class);
Assert.assertSame(unwrap, unwrap2);
Assert.assertSame("Second total connections not as expected", 1, pool.getTotalConnections());
Assert.assertSame("Second idle connections not as expected", 0, pool.getIdleConnections());
connection2.close();
try (Connection connection = ds.getConnection()) {
unwrap2 = connection.unwrap(Connection.class);
Assert.assertSame(unwrap, unwrap2);
Assert.assertSame("Second total connections not as expected", 1, pool.getTotalConnections());
Assert.assertSame("Second idle connections not as expected", 0, pool.getIdleConnections());
}
quietlySleep(800);
connection2 = ds.getConnection();
Assert.assertNotSame("Expected a different connection", connection, connection2);
connection2.close();
try (Connection connection = ds.getConnection()) {
unwrap2 = connection.unwrap(Connection.class);
Assert.assertNotSame("Expected a different connection", unwrap, unwrap2);
}
Assert.assertSame("Post total connections not as expected", 1, pool.getTotalConnections());
Assert.assertSame("Post idle connections not as expected", 1, pool.getIdleConnections());
@ -226,8 +228,8 @@ public class TestConnections
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
try (HikariDataSource ds = new HikariDataSource(config)) {
Connection connection = ds.getConnection();
try (HikariDataSource ds = new HikariDataSource(config);
Connection connection = ds.getConnection()) {
connection.close();
// should no-op
@ -236,8 +238,6 @@ public class TestConnections
Assert.assertTrue("Connection should have closed", connection.isClosed());
Assert.assertFalse("Connection should have closed", connection.isValid(5));
Assert.assertTrue("Expected to contain ClosedConnection, but was " + connection, connection.toString().contains("ClosedConnection"));
connection.close();
}
}
@ -272,53 +272,52 @@ public class TestConnections
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
StubConnection.slowCreate = true;
try (HikariDataSource ds = new HikariDataSource(config)) {
HikariPool pool = TestElf.getPool(ds);
UtilityElf.quietlySleep(500);
HikariPool pool = TestElf.getPool(ds);
UtilityElf.quietlySleep(1250);
Assert.assertSame("Total connections not as expected", 1, pool.getTotalConnections());
Assert.assertSame("Idle connections not as expected", 1, pool.getIdleConnections());
// This will take the pool down to zero
Connection connection = ds.getConnection();
Assert.assertNotNull(connection);
try (Connection connection = ds.getConnection()) {
Assert.assertNotNull(connection);
Assert.assertSame("Total connections not as expected", 1, pool.getTotalConnections());
Assert.assertSame("Idle connections not as expected", 0, pool.getIdleConnections());
Assert.assertSame("Total connections not as expected", 1, pool.getTotalConnections());
Assert.assertSame("Idle connections not as expected", 0, pool.getIdleConnections());
PreparedStatement statement = connection.prepareStatement("SELECT some, thing FROM somewhere WHERE something=?");
Assert.assertNotNull(statement);
PreparedStatement statement = connection.prepareStatement("SELECT some, thing FROM somewhere WHERE something=?");
Assert.assertNotNull(statement);
ResultSet resultSet = statement.executeQuery();
Assert.assertNotNull(resultSet);
ResultSet resultSet = statement.executeQuery();
Assert.assertNotNull(resultSet);
try {
statement.getMaxFieldSize();
Assert.fail();
}
catch (Exception e) {
Assert.assertSame(SQLException.class, e.getClass());
}
try {
statement.getMaxFieldSize();
Assert.fail();
}
catch (Exception e) {
Assert.assertSame(SQLException.class, e.getClass());
}
pool.logPoolState("testBackfill() before close...");
pool.logPoolState("testBackfill() before close...");
// The connection will be ejected from the pool here
connection.close();
// The connection will be ejected from the pool here
}
UtilityElf.quietlySleep(500);
Assert.assertSame("Total connections not as expected", 0, pool.getTotalConnections());
pool.logPoolState("testBackfill() after close...");
Assert.assertSame("Total connections not as expected", 0, pool.getTotalConnections());
Assert.assertSame("Idle connections not as expected", 0, pool.getIdleConnections());
// This will cause a backfill
connection = ds.getConnection();
connection.close();
quietlySleep(1250);
Assert.assertTrue("Total connections not as expected", pool.getTotalConnections() > 0);
Assert.assertTrue("Idle connections not as expected", pool.getIdleConnections() > 0);
Assert.assertSame("Total connections not as expected", 1, pool.getTotalConnections());
Assert.assertSame("Idle connections not as expected", 1, pool.getIdleConnections());
}
finally {
StubConnection.slowCreate = false;
}
}
@ -349,10 +348,10 @@ public class TestConnections
{
try {
pool.logPoolState("Before acquire ");
Connection connection = ds.getConnection();
pool.logPoolState("After acquire ");
quietlySleep(500);
connection.close();
try (Connection connection = ds.getConnection()) {
pool.logPoolState("After acquire ");
quietlySleep(500);
}
}
catch (Exception e) {
ref.set(e);
@ -390,11 +389,14 @@ public class TestConnections
try (HikariDataSource ds = new HikariDataSource(config)) {
quietlySleep(500);
Connection connection = ds.getConnection();
connection.close();
try (Connection connection = ds.getConnection()) {
// close
}
quietlySleep(500);
connection = ds.getConnection();
try (Connection connection = ds.getConnection()) {
// close
}
}
finally {
StubConnection.oldDriver = false;
@ -433,15 +435,15 @@ public class TestConnections
}
});
Connection c3 = ds.getConnection();
Assert.assertEquals(2, pool.getIdleConnections());
pool.suspendPool();
t.start();
quietlySleep(500);
Assert.assertEquals(2, pool.getIdleConnections());
c3.close();
try (Connection c3 = ds.getConnection()) {
Assert.assertEquals(2, pool.getIdleConnections());
pool.suspendPool();
t.start();
quietlySleep(500);
Assert.assertEquals(2, pool.getIdleConnections());
}
Assert.assertEquals(3, pool.getIdleConnections());
pool.resumePool();
quietlySleep(500);
@ -540,14 +542,14 @@ public class TestConnections
Assert.assertSame("Total connections not as expected", 1, pool.getTotalConnections());
Assert.assertSame("Idle connections not as expected", 1, pool.getIdleConnections());
Connection connection = ds.getConnection();
Assert.assertNotNull(connection);
TimeUnit.SECONDS.sleep(30);
Assert.assertSame("Second total connections not as expected", 30, pool.getTotalConnections());
Assert.assertSame("Second idle connections not as expected", 29, pool.getIdleConnections());
connection.close();
try (Connection connection = ds.getConnection()) {
Assert.assertNotNull(connection);
TimeUnit.SECONDS.sleep(30);
Assert.assertSame("Second total connections not as expected", 30, pool.getTotalConnections());
Assert.assertSame("Second idle connections not as expected", 29, pool.getIdleConnections());
}
Assert.assertSame("Idle connections not as expected", 30, pool.getIdleConnections());

Loading…
Cancel
Save