Cleanup pool suspend/resume semaphore use, add unit test.

pull/201/merge
Brett Wooldridge 10 years ago
parent 560f6e402a
commit 855dbafb86

@ -41,7 +41,6 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.Semaphore;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@ -60,10 +59,10 @@ import com.zaxxer.hikari.metrics.MetricsTracker;
import com.zaxxer.hikari.metrics.MetricsTracker.MetricsContext;
import com.zaxxer.hikari.proxy.IHikariConnectionProxy;
import com.zaxxer.hikari.proxy.ProxyFactory;
import com.zaxxer.hikari.util.GlobalPoolLock;
import com.zaxxer.hikari.util.ConcurrentBag;
import com.zaxxer.hikari.util.ConcurrentBag.IBagStateListener;
import com.zaxxer.hikari.util.DefaultThreadFactory;
import com.zaxxer.hikari.util.FauxSemaphore;
import com.zaxxer.hikari.util.LeakTask;
/**
@ -90,7 +89,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
private final ConcurrentBag<PoolBagEntry> connectionBag;
private final ThreadPoolExecutor closeConnectionExecutor;
private final IConnectionCustomizer connectionCustomizer;
private final Semaphore acquisitionSemaphore;
private final GlobalPoolLock suspendResumeLock;
private final LeakTask leakTask;
private final AtomicInteger totalConnections;
@ -138,7 +137,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
this.isReadOnly = configuration.isReadOnly();
this.isAutoCommit = configuration.isAutoCommit();
this.acquisitionSemaphore = configuration.isAllowPoolSuspension() ? new Semaphore(10000, true) : FauxSemaphore.FAUX_SEMAPHORE;
this.suspendResumeLock = configuration.isAllowPoolSuspension() ? GlobalPoolLock.SUSPEND_RESUME_LOCK : GlobalPoolLock.FAUX_LOCK;
this.catalog = configuration.getCatalog();
this.connectionCustomizer = initializeCustomizer();
@ -175,7 +174,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
*/
public Connection getConnection() throws SQLException
{
acquisitionSemaphore.acquireUninterruptibly();
suspendResumeLock.acquire();
long timeout = connectionTimeout;
final long start = System.currentTimeMillis();
final MetricsContext metricsContext = (isRecordMetrics ? metricsTracker.recordConnectionRequest(start) : MetricsTracker.NO_CONTEXT);
@ -203,7 +202,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
throw new SQLException("Interrupted during connection acquisition", e);
}
finally {
acquisitionSemaphore.release();
suspendResumeLock.release();
metricsContext.stop();
}
@ -388,7 +387,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
public void suspendPool()
{
if (!isPoolSuspended) {
acquisitionSemaphore.acquireUninterruptibly(10000);
suspendResumeLock.suspend();
isPoolSuspended = true;
}
}
@ -400,7 +399,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
if (isPoolSuspended) {
isPoolSuspended = false;
addBagItem(); // re-populate the pool
acquisitionSemaphore.release(10000);
suspendResumeLock.resume();
}
}

@ -1,61 +0,0 @@
/*
* Copyright (C) 2013, 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.util;
import java.util.concurrent.Semaphore;
/**
* @author Brett Wooldridge
*/
public final class FauxSemaphore extends Semaphore
{
private static final long serialVersionUID = 7994006542758337519L;
public static final FauxSemaphore FAUX_SEMAPHORE = new FauxSemaphore();
/**
* Default constructor
*/
public FauxSemaphore()
{
super(1);
}
/** {@inheritDoc} */
@Override
public void acquireUninterruptibly()
{
}
/** {@inheritDoc} */
@Override
public void acquireUninterruptibly(int permits)
{
}
/** {@inheritDoc} */
@Override
public void release(int permits)
{
}
/** {@inheritDoc} */
@Override
public void release()
{
}
}

@ -0,0 +1,75 @@
/*
* Copyright (C) 2013, 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.util;
import java.util.concurrent.Semaphore;
/**
* This class implements a lock that can be used to suspend and resume the pool. It
* also provides a faux implementation that is used when the feature is disabled that
* hopefully gets fully "optimized away" by the JIT.
*
* @author Brett Wooldridge
*/
public class GlobalPoolLock
{
public static final GlobalPoolLock FAUX_LOCK = new GlobalPoolLock(false) {
@Override
public void acquire() {}
@Override
public void release() {}
@Override
public void suspend() {}
@Override
public void resume() {}
};
public static final GlobalPoolLock SUSPEND_RESUME_LOCK = new GlobalPoolLock(true);
private static final int MAX_PERMITS = 10000;
private final Semaphore acquisitionSemaphore;
/**
* Default constructor
*/
private GlobalPoolLock(final boolean createSemaphore) {
acquisitionSemaphore = (createSemaphore ? new Semaphore(MAX_PERMITS, true) : null);
}
public void acquire()
{
acquisitionSemaphore.acquireUninterruptibly();
}
public void release()
{
acquisitionSemaphore.release();
}
public void suspend()
{
acquisitionSemaphore.acquireUninterruptibly(MAX_PERMITS);
}
public void resume()
{
acquisitionSemaphore.release(MAX_PERMITS);
}
}

@ -47,7 +47,6 @@ public class TestConnections
HikariConfig config = new HikariConfig();
config.setMinimumIdle(1);
config.setMaximumPoolSize(1);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setConnectionInitSql("SELECT 1");
config.setReadOnly(true);
@ -95,7 +94,6 @@ public class TestConnections
HikariConfig config = new HikariConfig();
config.setMinimumIdle(0);
config.setMaximumPoolSize(1);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
@ -149,7 +147,6 @@ public class TestConnections
HikariConfig config = new HikariConfig();
config.setMinimumIdle(0);
config.setMaximumPoolSize(1);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
@ -199,7 +196,6 @@ public class TestConnections
HikariConfig config = new HikariConfig();
config.setMinimumIdle(1);
config.setMaximumPoolSize(1);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
@ -220,7 +216,6 @@ public class TestConnections
HikariConfig config = new HikariConfig();
config.setMinimumIdle(0);
config.setMaximumPoolSize(5);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
@ -243,7 +238,6 @@ public class TestConnections
config.setMinimumIdle(1);
config.setMaximumPoolSize(4);
config.setConnectionTimeout(500);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
@ -378,7 +372,6 @@ public class TestConnections
HikariConfig config = new HikariConfig();
config.setMinimumIdle(1);
config.setMaximumPoolSize(1);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
@ -402,4 +395,53 @@ public class TestConnections
ds.close();
}
}
@Test
public void testSuspendResume() throws Exception
{
HikariConfig config = new HikariConfig();
config.setMinimumIdle(3);
config.setMaximumPoolSize(3);
config.setAllowPoolSuspension(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
final HikariDataSource ds = new HikariDataSource(config);
try {
HikariPool pool = TestElf.getPool(ds);
while (pool.getTotalConnections() < 3) {
PoolUtilities.quietlySleep(50);
}
Thread t = new Thread(new Runnable() {
public void run()
{
try {
ds.getConnection();
ds.getConnection();
}
catch (Exception e) {
Assert.fail();
}
}
});
Connection c3 = ds.getConnection();
Assert.assertEquals(2, pool.getIdleConnections());
pool.suspendPool();
t.start();
PoolUtilities.quietlySleep(500);
Assert.assertEquals(2, pool.getIdleConnections());
c3.close();
Assert.assertEquals(3, pool.getIdleConnections());
pool.resumePool();
PoolUtilities.quietlySleep(500);
Assert.assertEquals(1, pool.getIdleConnections());
}
finally {
ds.close();
}
}
}

@ -40,7 +40,6 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.Semaphore;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@ -59,10 +58,10 @@ import com.zaxxer.hikari.metrics.MetricsTracker;
import com.zaxxer.hikari.metrics.MetricsTracker.MetricsContext;
import com.zaxxer.hikari.proxy.IHikariConnectionProxy;
import com.zaxxer.hikari.proxy.ProxyFactory;
import com.zaxxer.hikari.util.GlobalPoolLock;
import com.zaxxer.hikari.util.ConcurrentBag;
import com.zaxxer.hikari.util.ConcurrentBag.IBagStateListener;
import com.zaxxer.hikari.util.DefaultThreadFactory;
import com.zaxxer.hikari.util.FauxSemaphore;
import com.zaxxer.hikari.util.LeakTask;
/**
@ -89,7 +88,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
private final ConcurrentBag<PoolBagEntry> connectionBag;
private final ThreadPoolExecutor closeConnectionExecutor;
private final IConnectionCustomizer connectionCustomizer;
private final Semaphore acquisitionSemaphore;
private final GlobalPoolLock suspendResumeLock;
private final LeakTask leakTask;
private final AtomicInteger totalConnections;
@ -137,7 +136,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
this.isReadOnly = configuration.isReadOnly();
this.isAutoCommit = configuration.isAutoCommit();
this.acquisitionSemaphore = configuration.isAllowPoolSuspension() ? new Semaphore(10000, true) : FauxSemaphore.FAUX_SEMAPHORE;
this.suspendResumeLock = configuration.isAllowPoolSuspension() ? GlobalPoolLock.SUSPEND_RESUME_LOCK : GlobalPoolLock.FAUX_LOCK;
this.catalog = configuration.getCatalog();
this.connectionCustomizer = initializeCustomizer();
@ -172,7 +171,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
*/
public Connection getConnection() throws SQLException
{
acquisitionSemaphore.acquireUninterruptibly();
suspendResumeLock.acquire();
long timeout = connectionTimeout;
final long start = System.currentTimeMillis();
final MetricsContext metricsContext = (isRecordMetrics ? metricsTracker.recordConnectionRequest(start) : MetricsTracker.NO_CONTEXT);
@ -200,7 +199,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
throw new SQLException("Interrupted during connection acquisition", e);
}
finally {
acquisitionSemaphore.release();
suspendResumeLock.release();
metricsContext.stop();
}
@ -372,7 +371,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
public void suspendPool()
{
if (!isPoolSuspended) {
acquisitionSemaphore.acquireUninterruptibly(10000);
suspendResumeLock.suspend();
isPoolSuspended = true;
}
}
@ -384,7 +383,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener
if (isPoolSuspended) {
isPoolSuspended = false;
addBagItem(); // re-populate the pool
acquisitionSemaphore.release(10000);
suspendResumeLock.resume();
}
}

@ -1,61 +0,0 @@
/*
* Copyright (C) 2013, 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.util;
import java.util.concurrent.Semaphore;
/**
* @author Brett Wooldridge
*/
public final class FauxSemaphore extends Semaphore
{
private static final long serialVersionUID = 7994006542758337519L;
public static final FauxSemaphore FAUX_SEMAPHORE = new FauxSemaphore();
/**
* Default constructor
*/
public FauxSemaphore()
{
super(1);
}
/** {@inheritDoc} */
@Override
public void acquireUninterruptibly()
{
}
/** {@inheritDoc} */
@Override
public void acquireUninterruptibly(int permits)
{
}
/** {@inheritDoc} */
@Override
public void release(int permits)
{
}
/** {@inheritDoc} */
@Override
public void release()
{
}
}

@ -0,0 +1,75 @@
/*
* Copyright (C) 2013, 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.util;
import java.util.concurrent.Semaphore;
/**
* This class implements a lock that can be used to suspend and resume the pool. It
* also provides a faux implementation that is used when the feature is disabled that
* hopefully gets fully "optimized away" by the JIT.
*
* @author Brett Wooldridge
*/
public class GlobalPoolLock
{
public static final GlobalPoolLock FAUX_LOCK = new GlobalPoolLock(false) {
@Override
public void acquire() {}
@Override
public void release() {}
@Override
public void suspend() {}
@Override
public void resume() {}
};
public static final GlobalPoolLock SUSPEND_RESUME_LOCK = new GlobalPoolLock(true);
private static final int MAX_PERMITS = 10000;
private final Semaphore acquisitionSemaphore;
/**
* Default constructor
*/
private GlobalPoolLock(final boolean createSemaphore) {
acquisitionSemaphore = (createSemaphore ? new Semaphore(MAX_PERMITS, true) : null);
}
public void acquire()
{
acquisitionSemaphore.acquireUninterruptibly();
}
public void release()
{
acquisitionSemaphore.release();
}
public void suspend()
{
acquisitionSemaphore.acquireUninterruptibly(MAX_PERMITS);
}
public void resume()
{
acquisitionSemaphore.release(MAX_PERMITS);
}
}

@ -47,7 +47,6 @@ public class TestConnections
HikariConfig config = new HikariConfig();
config.setMinimumIdle(1);
config.setMaximumPoolSize(1);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setConnectionInitSql("SELECT 1");
config.setReadOnly(true);
@ -95,7 +94,6 @@ public class TestConnections
HikariConfig config = new HikariConfig();
config.setMinimumIdle(0);
config.setMaximumPoolSize(1);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
@ -149,7 +147,6 @@ public class TestConnections
HikariConfig config = new HikariConfig();
config.setMinimumIdle(0);
config.setMaximumPoolSize(1);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
@ -199,7 +196,6 @@ public class TestConnections
HikariConfig config = new HikariConfig();
config.setMinimumIdle(1);
config.setMaximumPoolSize(1);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
@ -220,7 +216,6 @@ public class TestConnections
HikariConfig config = new HikariConfig();
config.setMinimumIdle(0);
config.setMaximumPoolSize(5);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
@ -243,7 +238,6 @@ public class TestConnections
config.setMinimumIdle(1);
config.setMaximumPoolSize(4);
config.setConnectionTimeout(500);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
@ -378,7 +372,6 @@ public class TestConnections
HikariConfig config = new HikariConfig();
config.setMinimumIdle(1);
config.setMaximumPoolSize(1);
config.setInitializationFailFast(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
@ -402,4 +395,53 @@ public class TestConnections
ds.close();
}
}
@Test
public void testSuspendResume() throws Exception
{
HikariConfig config = new HikariConfig();
config.setMinimumIdle(3);
config.setMaximumPoolSize(3);
config.setAllowPoolSuspension(true);
config.setConnectionTestQuery("VALUES 1");
config.setDataSourceClassName("com.zaxxer.hikari.mocks.StubDataSource");
final HikariDataSource ds = new HikariDataSource(config);
try {
HikariPool pool = TestElf.getPool(ds);
while (pool.getTotalConnections() < 3) {
PoolUtilities.quietlySleep(50);
}
Thread t = new Thread(new Runnable() {
public void run()
{
try {
ds.getConnection();
ds.getConnection();
}
catch (Exception e) {
Assert.fail();
}
}
});
Connection c3 = ds.getConnection();
Assert.assertEquals(2, pool.getIdleConnections());
pool.suspendPool();
t.start();
PoolUtilities.quietlySleep(500);
Assert.assertEquals(2, pool.getIdleConnections());
c3.close();
Assert.assertEquals(3, pool.getIdleConnections());
pool.resumePool();
PoolUtilities.quietlySleep(500);
Assert.assertEquals(1, pool.getIdleConnections());
}
finally {
ds.close();
}
}
}

Loading…
Cancel
Save