ShutdownTest.java
/*
* Copyright (C) 2014 Brett Wooldridge
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.zaxxer.hikari.pool;
import static com.zaxxer.hikari.pool.TestElf.getPool;
import static com.zaxxer.hikari.pool.TestElf.newHikariConfig;
import static com.zaxxer.hikari.pool.TestElf.setSlf4jLogLevel;
import static com.zaxxer.hikari.util.ClockSource.currentTime;
import static com.zaxxer.hikari.util.ClockSource.elapsedMillis;
import static com.zaxxer.hikari.util.UtilityElf.quietlySleep;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.apache.logging.log4j.Level;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.mocks.StubConnection;
import com.zaxxer.hikari.util.UtilityElf;
/**
* @author Brett Wooldridge
*/
public class ShutdownTest
{
@Before
public void beforeTest()
{
setSlf4jLogLevel(PoolBase.class, Level.DEBUG);
setSlf4jLogLevel(HikariPool.class, Level.DEBUG);
StubConnection.count.set(0);
}
@After
public void afterTest()
{
setSlf4jLogLevel(PoolBase.class, Level.WARN);
setSlf4jLogLevel(HikariPool.class, Level.WARN);
StubConnection.slowCreate = false;
}
@Test
public void testShutdown1() throws SQLException
{
Assert.assertSame("StubConnection count not as expected", 0, StubConnection.count.get());
StubConnection.slowCreate = true;
HikariConfig config = newHikariConfig();
config.setMinimumIdle(0);
config.setMaximumPoolSize(10);
config.setInitializationFailTimeout(Long.MAX_VALUE);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
try (HikariDataSource ds = new HikariDataSource(config)) {
HikariPool pool = getPool(ds);
Thread[] threads = new Thread[10];
for (int i = 0; i < 10; i++) {
threads[i] = new Thread() {
@Override
public void run()
{
try {
if (ds.getConnection() != null) {
quietlySleep(SECONDS.toMillis(1));
}
}
catch (SQLException e) {
}
}
};
threads[i].setDaemon(true);
}
for (int i = 0; i < 10; i++) {
threads[i].start();
}
quietlySleep(1800L);
assertTrue("Total connection count not as expected, ", pool.getTotalConnections() > 0);
ds.close();
assertSame("Active connection count not as expected, ", 0, pool.getActiveConnections());
assertSame("Idle connection count not as expected, ", 0, pool.getIdleConnections());
assertSame("Total connection count not as expected, ", 0, pool.getTotalConnections());
assertTrue(ds.isClosed());
}
}
@Test
public void testShutdown2() throws SQLException
{
assertSame("StubConnection count not as expected", 0, StubConnection.count.get());
StubConnection.slowCreate = true;
HikariConfig config = newHikariConfig();
config.setMinimumIdle(10);
config.setMaximumPoolSize(10);
config.setInitializationFailTimeout(0);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
try (HikariDataSource ds = new HikariDataSource(config)) {
HikariPool pool = getPool(ds);
quietlySleep(1200L);
assertTrue("Total connection count not as expected, ", pool.getTotalConnections() > 0);
ds.close();
assertSame("Active connection count not as expected, ", 0, pool.getActiveConnections());
assertSame("Idle connection count not as expected, ", 0, pool.getIdleConnections());
assertSame("Total connection count not as expected, ", 0, pool.getTotalConnections());
assertTrue(ds.toString().startsWith("HikariDataSource (") && ds.toString().endsWith(")"));
}
}
@Test
public void testShutdown3() throws SQLException
{
assertSame("StubConnection count not as expected", 0, StubConnection.count.get());
StubConnection.slowCreate = false;
HikariConfig config = newHikariConfig();
config.setMinimumIdle(5);
config.setMaximumPoolSize(5);
config.setInitializationFailTimeout(0);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
try (HikariDataSource ds = new HikariDataSource(config)) {
HikariPool pool = getPool(ds);
quietlySleep(1200L);
assertTrue("Total connection count not as expected, ", pool.getTotalConnections() == 5);
ds.close();
assertSame("Active connection count not as expected, ", 0, pool.getActiveConnections());
assertSame("Idle connection count not as expected, ", 0, pool.getIdleConnections());
assertSame("Total connection count not as expected, ", 0, pool.getTotalConnections());
}
}
@Test
public void testShutdown4() throws SQLException
{
StubConnection.slowCreate = true;
HikariConfig config = newHikariConfig();
config.setMinimumIdle(10);
config.setMaximumPoolSize(10);
config.setInitializationFailTimeout(Long.MAX_VALUE);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
try (HikariDataSource ds = new HikariDataSource(config)) {
quietlySleep(500L);
ds.close();
long startTime = currentTime();
while (elapsedMillis(startTime) < SECONDS.toMillis(5) && threadCount() > 0) {
quietlySleep(250);
}
assertSame("Unreleased connections after shutdown", 0, getPool(ds).getTotalConnections());
}
}
@Test
public void testShutdown5() throws SQLException
{
Assert.assertSame("StubConnection count not as expected", 0, StubConnection.count.get());
HikariConfig config = newHikariConfig();
config.setMinimumIdle(5);
config.setMaximumPoolSize(5);
config.setInitializationFailTimeout(0);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
try (HikariDataSource ds = new HikariDataSource(config)) {
HikariPool pool = getPool(ds);
Connection[] connections = new Connection[5];
for (int i = 0; i < 5; i++) {
connections[i] = ds.getConnection();
}
Assert.assertTrue("Total 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());
}
}
@Test
public void testAfterShutdown() throws Exception
{
HikariConfig config = newHikariConfig();
config.setMinimumIdle(0);
config.setMaximumPoolSize(5);
config.setInitializationFailTimeout(0);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
try (HikariDataSource ds = new HikariDataSource(config)) {
ds.close();
try {
ds.getConnection();
}
catch (SQLException e) {
Assert.assertTrue(e.getMessage().contains("has been closed."));
}
}
}
@Test
public void testShutdownDuringInit() throws Exception
{
final HikariConfig config = newHikariConfig();
config.setMinimumIdle(5);
config.setMaximumPoolSize(5);
config.setConnectionTimeout(1000);
config.setValidationTimeout(1000);
config.setInitializationFailTimeout(0);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
try (HikariDataSource ds = new HikariDataSource(config)) {
StubConnection.slowCreate = true;
UtilityElf.quietlySleep(3000L);
}
}
@Test
public void testThreadedShutdown() throws Exception
{
final HikariConfig config = newHikariConfig();
config.setMinimumIdle(5);
config.setMaximumPoolSize(5);
config.setConnectionTimeout(1000);
config.setValidationTimeout(1000);
config.setInitializationFailTimeout(0);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
for (int i = 0; i < 4; i++) {
try (final HikariDataSource ds = new HikariDataSource(config)) {
Thread t = new Thread() {
@Override
public void run()
{
try (Connection connection = ds.getConnection()) {
for (int i = 0; i < 10; i++) {
Connection connection2 = null;
try {
connection2 = ds.getConnection();
PreparedStatement stmt = connection2.prepareStatement("SOMETHING");
UtilityElf.quietlySleep(20);
stmt.getMaxFieldSize();
}
catch (SQLException e) {
try {
if (connection2 != null) {
connection2.close();
}
}
catch (SQLException e2) {
if (e2.getMessage().contains("shutdown") || e2.getMessage().contains("evicted")) {
break;
}
}
}
}
}
catch (Exception e) {
Assert.fail(e.getMessage());
}
finally {
ds.close();
}
}
};
t.start();
Thread t2 = new Thread() {
@Override
public void run()
{
UtilityElf.quietlySleep(100);
try {
ds.close();
}
catch (IllegalStateException e) {
Assert.fail(e.getMessage());
}
}
};
t2.start();
t.join();
t2.join();
ds.close();
}
}
}
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;
}
}