Fix #128 fix accounting issue with totalConnections when aborting connections during shutdown.

pull/134/head
Brett Wooldridge 11 years ago
parent 6b7143fb3e
commit 281c287288

@ -484,6 +484,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
for (IHikariConnectionProxy connectionProxy : connectionBag.values(STATE_IN_USE)) { for (IHikariConnectionProxy connectionProxy : connectionBag.values(STATE_IN_USE)) {
try { try {
connectionProxy.abort(assassinExecutor); connectionProxy.abort(assassinExecutor);
totalConnections.decrementAndGet();
} }
catch (AbstractMethodError e) { catch (AbstractMethodError e) {
quietlyCloseConnection(connectionProxy); quietlyCloseConnection(connectionProxy);
@ -492,7 +493,6 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
quietlyCloseConnection(connectionProxy); quietlyCloseConnection(connectionProxy);
} }
finally { finally {
totalConnections.decrementAndGet();
try { try {
connectionBag.remove(connectionProxy); connectionBag.remove(connectionProxy);
} }

@ -16,6 +16,7 @@
package com.zaxxer.hikari; package com.zaxxer.hikari;
import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -33,161 +34,187 @@ import com.zaxxer.hikari.util.PoolUtilities;
*/ */
public class ShutdownTest public class ShutdownTest
{ {
@Before @Before
public void beforeTest() public void beforeTest()
{ {
StubConnection.count.set(0); StubConnection.count.set(0);
} }
@After @After
public void afterTest() public void afterTest()
{ {
StubConnection.slowCreate = false; StubConnection.slowCreate = false;
} }
@Test @Test
public void testShutdown1() throws SQLException public void testShutdown1() throws SQLException
{ {
Assert.assertSame("StubConnection count not as expected", 0, StubConnection.count.get()); Assert.assertSame("StubConnection count not as expected", 0, StubConnection.count.get());
StubConnection.slowCreate = true; StubConnection.slowCreate = true;
HikariConfig config = new HikariConfig(); HikariConfig config = new HikariConfig();
config.setMinimumIdle(0); config.setMinimumIdle(0);
config.setMaximumPoolSize(10); config.setMaximumPoolSize(10);
config.setInitializationFailFast(true); config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1"); config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource"); config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
final HikariDataSource ds = new HikariDataSource(config); final HikariDataSource ds = new HikariDataSource(config);
HikariPool pool = TestElf.getPool(ds); HikariPool pool = TestElf.getPool(ds);
Thread[] threads = new Thread[10]; Thread[] threads = new Thread[10];
for (int i = 0; i < 10; i++) for (int i = 0; i < 10; i++) {
{ threads[i] = new Thread() {
threads[i] = new Thread() { public void run()
public void run() { {
try try {
{ if (ds.getConnection() != null) {
if (ds.getConnection() != null) PoolUtilities.quietlySleep(TimeUnit.SECONDS.toMillis(1));
{ }
PoolUtilities.quietlySleep(TimeUnit.SECONDS.toMillis(1)); }
} catch (SQLException e) {
} }
catch (SQLException e) }
{ };
} threads[i].setDaemon(true);
} threads[i].start();
}; }
threads[i].setDaemon(true);
threads[i].start(); PoolUtilities.quietlySleep(300);
}
Assert.assertTrue("Totals connection count not as expected, ", pool.getTotalConnections() > 0);
PoolUtilities.quietlySleep(300);
ds.close();
Assert.assertTrue("Totals connection count not as expected, ", pool.getTotalConnections() > 0);
Assert.assertSame("Active connection count not as expected, ", 0, pool.getActiveConnections());
ds.close(); Assert.assertSame("Idle connection count not as expected, ", 0, pool.getIdleConnections());
Assert.assertSame("Total connection count not as expected", 0, pool.getTotalConnections());
Assert.assertSame("Active connection count not as expected, ", 0, pool.getActiveConnections()); }
Assert.assertSame("Idle connection count not as expected, ", 0, pool.getIdleConnections());
Assert.assertSame("Total connection count not as expected", 0, pool.getTotalConnections()); @Test
} public void testShutdown2() throws SQLException
{
@Test Assert.assertSame("StubConnection count not as expected", 0, StubConnection.count.get());
public void testShutdown2() throws SQLException
{ StubConnection.slowCreate = true;
Assert.assertSame("StubConnection count not as expected", 0, StubConnection.count.get());
HikariConfig config = new HikariConfig();
StubConnection.slowCreate = true; config.setMinimumIdle(10);
config.setMaximumPoolSize(10);
HikariConfig config = new HikariConfig(); config.setInitializationFailFast(false);
config.setMinimumIdle(10); config.setConnectionTestQuery("VALUES 1");
config.setMaximumPoolSize(10); config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
config.setInitializationFailFast(false);
config.setConnectionTestQuery("VALUES 1"); HikariDataSource ds = new HikariDataSource(config);
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource"); HikariPool pool = TestElf.getPool(ds);
HikariDataSource ds = new HikariDataSource(config); PoolUtilities.quietlySleep(300);
HikariPool pool = TestElf.getPool(ds);
Assert.assertTrue("Totals connection count not as expected, ", pool.getTotalConnections() > 0);
PoolUtilities.quietlySleep(300);
ds.close();
Assert.assertTrue("Totals connection count not as expected, ", pool.getTotalConnections() > 0);
Assert.assertSame("Active connection count not as expected, ", 0, pool.getActiveConnections());
ds.close(); Assert.assertSame("Idle connection count not as expected, ", 0, pool.getIdleConnections());
Assert.assertSame("Total connection count not as expected", 0, pool.getTotalConnections());
Assert.assertSame("Active connection count not as expected, ", 0, pool.getActiveConnections()); }
Assert.assertSame("Idle connection count not as expected, ", 0, pool.getIdleConnections());
Assert.assertSame("Total connection count not as expected", 0, pool.getTotalConnections()); @Test
} public void testShutdown3() throws SQLException
{
@Test Assert.assertSame("StubConnection count not as expected", 0, StubConnection.count.get());
public void testShutdown3() throws SQLException
{ StubConnection.slowCreate = true;
Assert.assertSame("StubConnection count not as expected", 0, StubConnection.count.get());
HikariConfig config = new HikariConfig();
StubConnection.slowCreate = true; config.setMinimumIdle(5);
config.setMaximumPoolSize(5);
HikariConfig config = new HikariConfig(); config.setInitializationFailFast(true);
config.setMinimumIdle(5); config.setConnectionTestQuery("VALUES 1");
config.setMaximumPoolSize(5); config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1"); HikariDataSource ds = new HikariDataSource(config);
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource"); HikariPool pool = TestElf.getPool(ds);
HikariDataSource ds = new HikariDataSource(config); PoolUtilities.quietlySleep(300);
HikariPool pool = TestElf.getPool(ds);
Assert.assertTrue("Totals connection count not as expected, ", pool.getTotalConnections() == 5);
PoolUtilities.quietlySleep(300);
ds.close();
Assert.assertTrue("Totals connection count not as expected, ", pool.getTotalConnections() == 5);
Assert.assertSame("Active connection count not as expected, ", 0, pool.getActiveConnections());
ds.close(); Assert.assertSame("Idle connection count not as expected, ", 0, pool.getIdleConnections());
Assert.assertSame("Total connection count not as expected", 0, pool.getTotalConnections());
Assert.assertSame("Active connection count not as expected, ", 0, pool.getActiveConnections()); }
Assert.assertSame("Idle connection count not as expected, ", 0, pool.getIdleConnections());
Assert.assertSame("Total connection count not as expected", 0, pool.getTotalConnections()); @Test
} public void testShutdown4() throws SQLException
{
@Test StubConnection.slowCreate = true;
public void testShutdown4() throws SQLException
{ HikariConfig config = new HikariConfig();
StubConnection.slowCreate = true; config.setMinimumIdle(10);
config.setMaximumPoolSize(10);
HikariConfig config = new HikariConfig(); config.setInitializationFailFast(false);
config.setMinimumIdle(10); config.setConnectionTestQuery("VALUES 1");
config.setMaximumPoolSize(10); config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
config.setInitializationFailFast(false);
config.setConnectionTestQuery("VALUES 1"); HikariDataSource ds = new HikariDataSource(config);
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
PoolUtilities.quietlySleep(300);
HikariDataSource ds = new HikariDataSource(config);
ds.close();
PoolUtilities.quietlySleep(300);
long start = System.currentTimeMillis();
ds.close(); while (PoolUtilities.elapsedTimeMs(start) < TimeUnit.SECONDS.toMillis(5) && threadCount() > 0) {
PoolUtilities.quietlySleep(250);
long start = System.currentTimeMillis(); }
while (PoolUtilities.elapsedTimeMs(start) < TimeUnit.SECONDS.toMillis(5) && threadCount() > 0)
{ Assert.assertSame("Thread was leaked", 0, threadCount());
PoolUtilities.quietlySleep(250); }
}
@Test
Assert.assertSame("Thread was leaked", 0, threadCount()); public void testShutdown5() throws SQLException
} {
Assert.assertSame("StubConnection count not as expected", 0, StubConnection.count.get());
private int threadCount()
{ StubConnection.slowCreate = false;
Thread[] threads = new Thread[Thread.activeCount() * 2];
Thread.enumerate(threads); HikariConfig config = new HikariConfig();
config.setMinimumIdle(5);
int count = 0; config.setMaximumPoolSize(5);
for (Thread thread : threads) config.setInitializationFailFast(true);
{ config.setConnectionTestQuery("VALUES 1");
count += (thread != null && thread.getName().startsWith("Hikari")) ? 1 : 0; config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
}
HikariDataSource ds = new HikariDataSource(config);
return count; HikariPool pool = TestElf.getPool(ds);
}
Connection[] connections = new Connection[5];
for (int i = 0; i < 5; i++) {
connections[i] = ds.getConnection();
}
Assert.assertTrue("Totals connection count not as expected, ", pool.getTotalConnections() == 5);
ds.close();
Assert.assertSame("Active connection count not as expected, ", 0, pool.getActiveConnections());
Assert.assertSame("Idle connection count not as expected, ", 0, pool.getIdleConnections());
Assert.assertSame("Total connection count not as expected", 0, pool.getTotalConnections());
}
private int threadCount()
{
Thread[] threads = new Thread[Thread.activeCount() * 2];
Thread.enumerate(threads);
int count = 0;
for (Thread thread : threads) {
count += (thread != null && thread.getName().startsWith("Hikari")) ? 1 : 0;
}
return count;
}
} }

@ -402,6 +402,7 @@ public class StubConnection extends StubBaseConnection implements Connection
/** {@inheritDoc} */ /** {@inheritDoc} */
public void abort(Executor executor) throws SQLException public void abort(Executor executor) throws SQLException
{ {
throw new SQLException("Intentianal exception during abort");
} }
/** {@inheritDoc} */ /** {@inheritDoc} */

@ -473,12 +473,12 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
connectionBag.values(STATE_IN_USE).parallelStream().forEach(connectionProxy -> { connectionBag.values(STATE_IN_USE).parallelStream().forEach(connectionProxy -> {
try { try {
connectionProxy.abort(assassinExecutor); connectionProxy.abort(assassinExecutor);
totalConnections.decrementAndGet();
} }
catch (SQLException | AbstractMethodError e) { catch (SQLException | AbstractMethodError e) {
quietlyCloseConnection(connectionProxy); quietlyCloseConnection(connectionProxy);
} }
finally { finally {
totalConnections.decrementAndGet();
try { try {
connectionBag.remove(connectionProxy); connectionBag.remove(connectionProxy);
} }

@ -16,6 +16,7 @@
package com.zaxxer.hikari; package com.zaxxer.hikari;
import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -33,161 +34,187 @@ import com.zaxxer.hikari.util.PoolUtilities;
*/ */
public class ShutdownTest public class ShutdownTest
{ {
@Before @Before
public void beforeTest() public void beforeTest()
{ {
StubConnection.count.set(0); StubConnection.count.set(0);
} }
@After @After
public void afterTest() public void afterTest()
{ {
StubConnection.slowCreate = false; StubConnection.slowCreate = false;
} }
@Test @Test
public void testShutdown1() throws SQLException public void testShutdown1() throws SQLException
{ {
Assert.assertSame("StubConnection count not as expected", 0, StubConnection.count.get()); Assert.assertSame("StubConnection count not as expected", 0, StubConnection.count.get());
StubConnection.slowCreate = true; StubConnection.slowCreate = true;
HikariConfig config = new HikariConfig(); HikariConfig config = new HikariConfig();
config.setMinimumIdle(0); config.setMinimumIdle(0);
config.setMaximumPoolSize(10); config.setMaximumPoolSize(10);
config.setInitializationFailFast(true); config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1"); config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource"); config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
final HikariDataSource ds = new HikariDataSource(config); final HikariDataSource ds = new HikariDataSource(config);
HikariPool pool = TestElf.getPool(ds); HikariPool pool = TestElf.getPool(ds);
Thread[] threads = new Thread[10]; Thread[] threads = new Thread[10];
for (int i = 0; i < 10; i++) for (int i = 0; i < 10; i++) {
{ threads[i] = new Thread() {
threads[i] = new Thread() { public void run()
public void run() { {
try try {
{ if (ds.getConnection() != null) {
if (ds.getConnection() != null) PoolUtilities.quietlySleep(TimeUnit.SECONDS.toMillis(1));
{ }
PoolUtilities.quietlySleep(TimeUnit.SECONDS.toMillis(1)); }
} catch (SQLException e) {
} }
catch (SQLException e) }
{ };
} threads[i].setDaemon(true);
} threads[i].start();
}; }
threads[i].setDaemon(true);
threads[i].start(); PoolUtilities.quietlySleep(300);
}
Assert.assertTrue("Totals connection count not as expected, ", pool.getTotalConnections() > 0);
PoolUtilities.quietlySleep(300);
ds.close();
Assert.assertTrue("Totals connection count not as expected, ", pool.getTotalConnections() > 0);
Assert.assertSame("Active connection count not as expected, ", 0, pool.getActiveConnections());
ds.close(); Assert.assertSame("Idle connection count not as expected, ", 0, pool.getIdleConnections());
Assert.assertSame("Total connection count not as expected", 0, pool.getTotalConnections());
Assert.assertSame("Active connection count not as expected, ", 0, pool.getActiveConnections()); }
Assert.assertSame("Idle connection count not as expected, ", 0, pool.getIdleConnections());
Assert.assertSame("Total connection count not as expected", 0, pool.getTotalConnections()); @Test
} public void testShutdown2() throws SQLException
{
@Test Assert.assertSame("StubConnection count not as expected", 0, StubConnection.count.get());
public void testShutdown2() throws SQLException
{ StubConnection.slowCreate = true;
Assert.assertSame("StubConnection count not as expected", 0, StubConnection.count.get());
HikariConfig config = new HikariConfig();
StubConnection.slowCreate = true; config.setMinimumIdle(10);
config.setMaximumPoolSize(10);
HikariConfig config = new HikariConfig(); config.setInitializationFailFast(false);
config.setMinimumIdle(10); config.setConnectionTestQuery("VALUES 1");
config.setMaximumPoolSize(10); config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
config.setInitializationFailFast(false);
config.setConnectionTestQuery("VALUES 1"); HikariDataSource ds = new HikariDataSource(config);
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource"); HikariPool pool = TestElf.getPool(ds);
HikariDataSource ds = new HikariDataSource(config); PoolUtilities.quietlySleep(300);
HikariPool pool = TestElf.getPool(ds);
Assert.assertTrue("Totals connection count not as expected, ", pool.getTotalConnections() > 0);
PoolUtilities.quietlySleep(300);
ds.close();
Assert.assertTrue("Totals connection count not as expected, ", pool.getTotalConnections() > 0);
Assert.assertSame("Active connection count not as expected, ", 0, pool.getActiveConnections());
ds.close(); Assert.assertSame("Idle connection count not as expected, ", 0, pool.getIdleConnections());
Assert.assertSame("Total connection count not as expected", 0, pool.getTotalConnections());
Assert.assertSame("Active connection count not as expected, ", 0, pool.getActiveConnections()); }
Assert.assertSame("Idle connection count not as expected, ", 0, pool.getIdleConnections());
Assert.assertSame("Total connection count not as expected", 0, pool.getTotalConnections()); @Test
} public void testShutdown3() throws SQLException
{
@Test Assert.assertSame("StubConnection count not as expected", 0, StubConnection.count.get());
public void testShutdown3() throws SQLException
{ StubConnection.slowCreate = true;
Assert.assertSame("StubConnection count not as expected", 0, StubConnection.count.get());
HikariConfig config = new HikariConfig();
StubConnection.slowCreate = true; config.setMinimumIdle(5);
config.setMaximumPoolSize(5);
HikariConfig config = new HikariConfig(); config.setInitializationFailFast(true);
config.setMinimumIdle(5); config.setConnectionTestQuery("VALUES 1");
config.setMaximumPoolSize(5); config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1"); HikariDataSource ds = new HikariDataSource(config);
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource"); HikariPool pool = TestElf.getPool(ds);
HikariDataSource ds = new HikariDataSource(config); PoolUtilities.quietlySleep(300);
HikariPool pool = TestElf.getPool(ds);
Assert.assertTrue("Totals connection count not as expected, ", pool.getTotalConnections() == 5);
PoolUtilities.quietlySleep(300);
ds.close();
Assert.assertTrue("Totals connection count not as expected, ", pool.getTotalConnections() == 5);
Assert.assertSame("Active connection count not as expected, ", 0, pool.getActiveConnections());
ds.close(); Assert.assertSame("Idle connection count not as expected, ", 0, pool.getIdleConnections());
Assert.assertSame("Total connection count not as expected", 0, pool.getTotalConnections());
Assert.assertSame("Active connection count not as expected, ", 0, pool.getActiveConnections()); }
Assert.assertSame("Idle connection count not as expected, ", 0, pool.getIdleConnections());
Assert.assertSame("Total connection count not as expected", 0, pool.getTotalConnections()); @Test
} public void testShutdown4() throws SQLException
{
@Test StubConnection.slowCreate = true;
public void testShutdown4() throws SQLException
{ HikariConfig config = new HikariConfig();
StubConnection.slowCreate = true; config.setMinimumIdle(10);
config.setMaximumPoolSize(10);
HikariConfig config = new HikariConfig(); config.setInitializationFailFast(false);
config.setMinimumIdle(10); config.setConnectionTestQuery("VALUES 1");
config.setMaximumPoolSize(10); config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
config.setInitializationFailFast(false);
config.setConnectionTestQuery("VALUES 1"); HikariDataSource ds = new HikariDataSource(config);
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
PoolUtilities.quietlySleep(300);
HikariDataSource ds = new HikariDataSource(config);
ds.close();
PoolUtilities.quietlySleep(300);
long start = System.currentTimeMillis();
ds.close(); while (PoolUtilities.elapsedTimeMs(start) < TimeUnit.SECONDS.toMillis(5) && threadCount() > 0) {
PoolUtilities.quietlySleep(250);
long start = System.currentTimeMillis(); }
while (PoolUtilities.elapsedTimeMs(start) < TimeUnit.SECONDS.toMillis(5) && threadCount() > 0)
{ Assert.assertSame("Thread was leaked", 0, threadCount());
PoolUtilities.quietlySleep(250); }
}
@Test
Assert.assertSame("Thread was leaked", 0, threadCount()); public void testShutdown5() throws SQLException
} {
Assert.assertSame("StubConnection count not as expected", 0, StubConnection.count.get());
private int threadCount()
{ StubConnection.slowCreate = false;
Thread[] threads = new Thread[Thread.activeCount() * 2];
Thread.enumerate(threads); HikariConfig config = new HikariConfig();
config.setMinimumIdle(5);
int count = 0; config.setMaximumPoolSize(5);
for (Thread thread : threads) config.setInitializationFailFast(true);
{ config.setConnectionTestQuery("VALUES 1");
count += (thread != null && thread.getName().startsWith("Hikari")) ? 1 : 0; config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
}
HikariDataSource ds = new HikariDataSource(config);
return count; HikariPool pool = TestElf.getPool(ds);
}
Connection[] connections = new Connection[5];
for (int i = 0; i < 5; i++) {
connections[i] = ds.getConnection();
}
Assert.assertTrue("Totals connection count not as expected, ", pool.getTotalConnections() == 5);
ds.close();
Assert.assertSame("Active connection count not as expected, ", 0, pool.getActiveConnections());
Assert.assertSame("Idle connection count not as expected, ", 0, pool.getIdleConnections());
Assert.assertSame("Total connection count not as expected", 0, pool.getTotalConnections());
}
private int threadCount()
{
Thread[] threads = new Thread[Thread.activeCount() * 2];
Thread.enumerate(threads);
int count = 0;
for (Thread thread : threads) {
count += (thread != null && thread.getName().startsWith("Hikari")) ? 1 : 0;
}
return count;
}
} }

@ -402,6 +402,7 @@ public class StubConnection extends StubBaseConnection implements Connection
/** {@inheritDoc} */ /** {@inheritDoc} */
public void abort(Executor executor) throws SQLException public void abort(Executor executor) throws SQLException
{ {
throw new SQLException("Intentianal exception during abort");
} }
/** {@inheritDoc} */ /** {@inheritDoc} */

Loading…
Cancel
Save