From 2793746752087239dfcace0f7b01cd8796c45371 Mon Sep 17 00:00:00 2001 From: cadeeper Date: Mon, 23 Nov 2020 09:46:43 +0800 Subject: [PATCH 1/6] features: add heartbeat to keepalive connection if configure it --- .../java/com/zaxxer/hikari/HikariConfig.java | 23 +++++++++++++++++++ .../com/zaxxer/hikari/pool/HikariPool.java | 14 ++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/zaxxer/hikari/HikariConfig.java b/src/main/java/com/zaxxer/hikari/HikariConfig.java index db9bd2d83..148432b06 100644 --- a/src/main/java/com/zaxxer/hikari/HikariConfig.java +++ b/src/main/java/com/zaxxer/hikari/HikariConfig.java @@ -55,6 +55,7 @@ public class HikariConfig implements HikariConfigMXBean private static final long VALIDATION_TIMEOUT = SECONDS.toMillis(5); private static final long IDLE_TIMEOUT = MINUTES.toMillis(10); private static final long MAX_LIFETIME = MINUTES.toMillis(30); + private static final long DEFAULT_KEEPALIVE_TIME = MINUTES.toMillis(30); private static final int DEFAULT_POOL_SIZE = 10; private static boolean unitTest = false; @@ -99,6 +100,10 @@ public class HikariConfig implements HikariConfigMXBean private Object healthCheckRegistry; private Properties healthCheckProperties; + private boolean isKeepalive; + + private long keepaliveTime; + private volatile boolean sealed; /** @@ -117,6 +122,8 @@ public HikariConfig() idleTimeout = IDLE_TIMEOUT; initializationFailTimeout = 1; isAutoCommit = true; + isKeepalive = false; + keepaliveTime = DEFAULT_KEEPALIVE_TIME; String systemProp = System.getProperty("hikaricp.configurationFile"); if (systemProp != null) { @@ -720,6 +727,22 @@ public void addHealthCheckProperty(String key, String value) healthCheckProperties.setProperty(key, value); } + public boolean isKeepalive() { + return isKeepalive; + } + + public void setKeepalive(boolean keepalive) { + isKeepalive = keepalive; + } + + public long getKeepaliveTime() { + return keepaliveTime; + } + + public void setKeepaliveTime(long keepaliveTime) { + this.keepaliveTime = keepaliveTime; + } + /** * Determine whether the Connections in the pool are in read-only mode. * diff --git a/src/main/java/com/zaxxer/hikari/pool/HikariPool.java b/src/main/java/com/zaxxer/hikari/pool/HikariPool.java index 788e1b97d..9b70d5359 100644 --- a/src/main/java/com/zaxxer/hikari/pool/HikariPool.java +++ b/src/main/java/com/zaxxer/hikari/pool/HikariPool.java @@ -28,7 +28,7 @@ import com.zaxxer.hikari.util.ConcurrentBag; import com.zaxxer.hikari.util.ConcurrentBag.IBagStateListener; import com.zaxxer.hikari.util.SuspendResumeLock; -import com.zaxxer.hikari.util.UtilityElf.DefaultThreadFactory; +import com.zaxxer.hikari.util.UtilityElf.*; import io.micrometer.core.instrument.MeterRegistry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -477,6 +477,8 @@ private PoolEntry createPoolEntry() final PoolEntry poolEntry = newPoolEntry(); final long maxLifetime = config.getMaxLifetime(); + final boolean isKeepalive = config.isKeepalive(); + if (maxLifetime > 0) { // variance up to 2.5% of the maxlifetime final long variance = maxLifetime > 10_000 ? ThreadLocalRandom.current().nextLong( maxLifetime / 40 ) : 0; @@ -619,6 +621,16 @@ private boolean softEvictConnection(final PoolEntry poolEntry, final String reas return false; } + /** + * check if the connection is alive + * if the connection state is in use ,we suppose it is alive. then we'll check it by {@link PoolBase#isConnectionAlive} + * @param poolEntry the PoolEntry (/Connection) to be checked from the pool + * @return true if the connection is alive,false if it was dead + */ + private boolean keepaliveConnection(final PoolEntry poolEntry) { + return poolEntry.getState() == STATE_IN_USE || isConnectionAlive(poolEntry.connection); + } + /** * Create/initialize the Housekeeping service {@link ScheduledExecutorService}. If the user specified an Executor * to be used in the {@link HikariConfig}, then we use that. If no Executor was specified (typical), then create From b38f51fcb75ee0b7c1c21253cef6846eb8273dbc Mon Sep 17 00:00:00 2001 From: cadeeper Date: Mon, 23 Nov 2020 09:52:38 +0800 Subject: [PATCH 2/6] features: add heartbeat to keepalive connection if configure it --- .../java/com/zaxxer/hikari/HikariConfig.java | 6 ++++++ .../com/zaxxer/hikari/pool/HikariPool.java | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/main/java/com/zaxxer/hikari/HikariConfig.java b/src/main/java/com/zaxxer/hikari/HikariConfig.java index 148432b06..0a3911490 100644 --- a/src/main/java/com/zaxxer/hikari/HikariConfig.java +++ b/src/main/java/com/zaxxer/hikari/HikariConfig.java @@ -1041,6 +1041,12 @@ private void validateNumerics() maxLifetime = MAX_LIFETIME; } + // keepalive time must larger then 30 seconds + if (keepaliveTime < SECONDS.toMillis(30)) { + LOGGER.warn("{} - keepaliveTime is less than 30000ms, setting to default {}ms.", poolName, DEFAULT_KEEPALIVE_TIME); + keepaliveTime = DEFAULT_KEEPALIVE_TIME; + } + if (leakDetectionThreshold > 0 && !unitTest) { if (leakDetectionThreshold < SECONDS.toMillis(2) || (leakDetectionThreshold > maxLifetime && maxLifetime > 0)) { LOGGER.warn("{} - leakDetectionThreshold is less than 2000ms or more than maxLifetime, disabling it.", poolName); diff --git a/src/main/java/com/zaxxer/hikari/pool/HikariPool.java b/src/main/java/com/zaxxer/hikari/pool/HikariPool.java index 9b70d5359..e62aadf3f 100644 --- a/src/main/java/com/zaxxer/hikari/pool/HikariPool.java +++ b/src/main/java/com/zaxxer/hikari/pool/HikariPool.java @@ -491,6 +491,24 @@ private PoolEntry createPoolEntry() }, lifetime, MILLISECONDS)); } + if (isKeepalive) { + final long keepaliveTime = config.getKeepaliveTime(); + // variance up to 10% of the heartbeat time + final long variance = ThreadLocalRandom.current().nextLong(keepaliveTime / 10); + final long heartbeatTime = keepaliveTime - variance; + poolEntry.setKeepalive(houseKeepingExecutorService.scheduleWithFixedDelay( + () -> { + boolean isConnectionAlive = keepaliveConnection(poolEntry); + if (!isConnectionAlive) { + if (softEvictConnection(poolEntry, DEAD_CONNECTION_MESSAGE, false)) { + addBagItem(connectionBag.getWaitingThreadCount()); + } + } + if (logger.isDebugEnabled()) { + logger.debug("{} - keepalive heartbeat: Is connection alive? {}", poolName, isConnectionAlive); + } + }, heartbeatTime, heartbeatTime, MILLISECONDS)); + } return poolEntry; } From a2e52bb9832093b0a0fc21bf53a3ab6640059fa4 Mon Sep 17 00:00:00 2001 From: cadeeper Date: Mon, 23 Nov 2020 09:55:21 +0800 Subject: [PATCH 3/6] features: add heartbeat to keepalive connection if configure it --- src/main/java/com/zaxxer/hikari/pool/PoolEntry.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/com/zaxxer/hikari/pool/PoolEntry.java b/src/main/java/com/zaxxer/hikari/pool/PoolEntry.java index 07c670b83..716300ebd 100644 --- a/src/main/java/com/zaxxer/hikari/pool/PoolEntry.java +++ b/src/main/java/com/zaxxer/hikari/pool/PoolEntry.java @@ -48,6 +48,8 @@ final class PoolEntry implements IConcurrentBagEntry private volatile ScheduledFuture endOfLife; + private volatile ScheduledFuture keepalive; + private final FastList openStatements; private final HikariPool hikariPool; @@ -92,6 +94,10 @@ void setFutureEol(final ScheduledFuture endOfLife) this.endOfLife = endOfLife; } + public void setKeepalive(ScheduledFuture keepalive) { + this.keepalive = keepalive; + } + Connection createProxyConnection(final ProxyLeakTask leakTask, final long now) { return ProxyFactory.getProxyConnection(this, connection, openStatements, leakTask, now, isReadOnly, isAutoCommit); @@ -175,9 +181,15 @@ Connection close() LOGGER.warn("{} - maxLifeTime expiration task cancellation unexpectedly returned false for connection {}", getPoolName(), connection); } + ScheduledFuture ka = keepalive; + if (ka != null && !ka.isDone() && !ka.cancel(false)) { + LOGGER.warn("{} - keepalive expiration task cancellation unexpectedly returned false for connection {}", getPoolName(), connection); + } + Connection con = connection; connection = null; endOfLife = null; + keepalive = null; return con; } From 66da846817f2e62ad34a1250172e5c434c25172d Mon Sep 17 00:00:00 2001 From: cadeeper Date: Mon, 23 Nov 2020 15:21:37 +0800 Subject: [PATCH 4/6] features: keepalive unit test --- .../zaxxer/hikari/mocks/StubConnection.java | 38 +++++++++- .../zaxxer/hikari/mocks/StubDataSource.java | 14 +++- .../zaxxer/hikari/mocks/StubStatement.java | 2 + .../zaxxer/hikari/pool/TestConnections.java | 72 +++++++++++++++++++ 4 files changed, 120 insertions(+), 6 deletions(-) diff --git a/src/test/java/com/zaxxer/hikari/mocks/StubConnection.java b/src/test/java/com/zaxxer/hikari/mocks/StubConnection.java index f96624bd2..7991433b2 100644 --- a/src/test/java/com/zaxxer/hikari/mocks/StubConnection.java +++ b/src/test/java/com/zaxxer/hikari/mocks/StubConnection.java @@ -33,7 +33,7 @@ import java.sql.Struct; import java.util.Map; import java.util.Properties; -import java.util.concurrent.Executor; +import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; import com.zaxxer.hikari.util.UtilityElf; @@ -47,11 +47,16 @@ public class StubConnection extends StubBaseConnection implements Connection public static final AtomicInteger count = new AtomicInteger(); public static volatile boolean slowCreate; public static volatile boolean oldDriver; + private volatile boolean isClose = false; private static long foo; private boolean autoCommit; private int isolation = Connection.TRANSACTION_READ_COMMITTED; private String catalog; + private long waitTimeout; + + private ScheduledExecutorService connectionWaitTimeout = new ScheduledThreadPoolExecutor(1); + private ScheduledFuture waitTimeoutTask; static { foo = System.currentTimeMillis(); @@ -64,6 +69,20 @@ public StubConnection() { } } + public StubConnection(long waitTimeout) { + this.waitTimeout = waitTimeout; + count.incrementAndGet(); + if (slowCreate) { + UtilityElf.quietlySleep(1000); + } + try{ + refreshConnectionWaitTimeout(); + }catch (Exception e){ + //ignore + } + } + + /** {@inheritDoc} */ @SuppressWarnings("unchecked") @Override @@ -128,7 +147,19 @@ public boolean getAutoCommit() throws SQLException @Override public void commit() throws SQLException { + refreshConnectionWaitTimeout(); + } + private void refreshConnectionWaitTimeout() throws SQLException { + if (this.isClose) { + throw new SQLException("connection has been closed"); + } + if (waitTimeoutTask != null) { + waitTimeoutTask.cancel(true); + } + if (waitTimeout > 0) { + waitTimeoutTask = connectionWaitTimeout.schedule(() -> { this.isClose = true;}, waitTimeout, TimeUnit.MILLISECONDS); + } } /** {@inheritDoc} */ @@ -152,7 +183,7 @@ public boolean isClosed() throws SQLException if (throwException) { throw new SQLException(); } - return false; + return isClose; } /** {@inheritDoc} */ @@ -408,7 +439,8 @@ public boolean isValid(int timeout) throws SQLException if (throwException) { throw new SQLException(); } - return true; + refreshConnectionWaitTimeout(); + return !isClose; } /** {@inheritDoc} */ diff --git a/src/test/java/com/zaxxer/hikari/mocks/StubDataSource.java b/src/test/java/com/zaxxer/hikari/mocks/StubDataSource.java index c7da189bd..7e55580ca 100644 --- a/src/test/java/com/zaxxer/hikari/mocks/StubDataSource.java +++ b/src/test/java/com/zaxxer/hikari/mocks/StubDataSource.java @@ -38,6 +38,7 @@ public class StubDataSource implements DataSource private SQLException throwException; private long connectionAcquistionTime = 0; private int loginTimeout; + private int waitTimeout = 30000; public String getUser() { @@ -59,6 +60,14 @@ public void setPassword(String password) this.password = password; } + public int getWaitTimeout() { + return waitTimeout; + } + + public void setWaitTimeout(int waitTimeout) { + this.waitTimeout = waitTimeout; + } + public void setURL(String url) { // we don't care @@ -127,15 +136,14 @@ public Connection getConnection() throws SQLException if (connectionAcquistionTime > 0) { UtilityElf.quietlySleep(connectionAcquistionTime); } - - return new StubConnection(); + return new StubConnection(waitTimeout); } /** {@inheritDoc} */ @Override public Connection getConnection(String username, String password) throws SQLException { - return new StubConnection(); + return new StubConnection(waitTimeout); } public void setThrowException(SQLException e) diff --git a/src/test/java/com/zaxxer/hikari/mocks/StubStatement.java b/src/test/java/com/zaxxer/hikari/mocks/StubStatement.java index 99e7b1330..7378abc50 100644 --- a/src/test/java/com/zaxxer/hikari/mocks/StubStatement.java +++ b/src/test/java/com/zaxxer/hikari/mocks/StubStatement.java @@ -68,6 +68,7 @@ public ResultSet executeQuery(String sql) throws SQLException { checkClosed(); StubResultSet resultSet = new StubResultSet(); + connection.commit(); return resultSet; } @@ -179,6 +180,7 @@ public boolean execute(String sql) throws SQLException if (simulatedQueryTime > 0) { quietlySleep(simulatedQueryTime); } + connection.commit(); return false; } diff --git a/src/test/java/com/zaxxer/hikari/pool/TestConnections.java b/src/test/java/com/zaxxer/hikari/pool/TestConnections.java index 0275b1322..5328ac4bc 100644 --- a/src/test/java/com/zaxxer/hikari/pool/TestConnections.java +++ b/src/test/java/com/zaxxer/hikari/pool/TestConnections.java @@ -217,6 +217,78 @@ public void testMaxLifetime2() throws Exception } } + @Test + public void testKeepalive() throws Exception{ + HikariConfig config = newHikariConfig(); + config.setMinimumIdle(0); + config.setMaximumPoolSize(1); + config.setConnectionTimeout(2500); + config.setConnectionTestQuery("VALUES 1"); + StubDataSource sds = new StubDataSource(); + sds.setWaitTimeout(700); + config.setDataSource(sds); + + System.setProperty("com.zaxxer.hikari.housekeeping.periodMs", "100"); + + setConfigUnitTest(true); + try (HikariDataSource ds = new HikariDataSource(config)) { + getUnsealedConfig(ds).setKeepalive(true); + getUnsealedConfig(ds).setKeepaliveTime(500); + + HikariPool pool = getPool(ds); + Connection conn = pool.getConnection(); + Connection unwrap = conn.unwrap(Connection.class); + //recycle, change IN_USE state + conn.close(); + assertFalse("Connection should be open", unwrap.isClosed()); + quietlySleep(1200); + assertFalse("Connection should be open", unwrap.isClosed()); + } + finally { + setConfigUnitTest(false); + } + } + + @Test + public void testKeepalive2() throws Exception{ + HikariConfig config = newHikariConfig(); + config.setMinimumIdle(0); + config.setMaximumPoolSize(1); + config.setConnectionTimeout(2500); + config.setConnectionTestQuery("VALUES 1"); + StubDataSource sds = new StubDataSource(); + sds.setWaitTimeout(500); + config.setDataSource(sds); + + System.setProperty("com.zaxxer.hikari.housekeeping.periodMs", "100"); + + setConfigUnitTest(true); + try (HikariDataSource ds = new HikariDataSource(config)) { + getUnsealedConfig(ds).setKeepalive(true); + getUnsealedConfig(ds).setKeepaliveTime(700); + + HikariPool pool = getPool(ds); + Connection conn = pool.getConnection(); + Connection unwrap = conn.unwrap(Connection.class); + //recycle, change IN_USE state + conn.close(); + assertFalse("Connection should be open", unwrap.isClosed()); + quietlySleep(1200); + assertTrue("Connection should have closed:" + unwrap, unwrap.isClosed()); + + Connection conn2 = pool.getConnection(); + Connection unwrap2 = conn2.unwrap(Connection.class); + + assertNotSame("Expected a different connection", unwrap, unwrap2); + assertFalse("Connection should be open", unwrap2.isClosed()); + + conn2.close(); + } + finally { + setConfigUnitTest(false); + } + } + @Test public void testDoubleClose() throws Exception { From 53b14ca9ac1415d52cbd5f8f04f71df32ce72816 Mon Sep 17 00:00:00 2001 From: cadeeper Date: Fri, 8 Jan 2021 10:07:45 +0800 Subject: [PATCH 5/6] optimize code --- .../java/com/zaxxer/hikari/HikariConfig.java | 39 +++++++++++-------- .../com/zaxxer/hikari/pool/HikariPool.java | 30 +++++--------- .../com/zaxxer/hikari/pool/PoolEntry.java | 3 +- .../zaxxer/hikari/mocks/StubConnection.java | 19 +++++---- .../zaxxer/hikari/pool/TestConnections.java | 2 - 5 files changed, 45 insertions(+), 48 deletions(-) diff --git a/src/main/java/com/zaxxer/hikari/HikariConfig.java b/src/main/java/com/zaxxer/hikari/HikariConfig.java index 21ad98d39..6d8bf38b9 100644 --- a/src/main/java/com/zaxxer/hikari/HikariConfig.java +++ b/src/main/java/com/zaxxer/hikari/HikariConfig.java @@ -55,7 +55,7 @@ public class HikariConfig implements HikariConfigMXBean private static final long VALIDATION_TIMEOUT = SECONDS.toMillis(5); private static final long IDLE_TIMEOUT = MINUTES.toMillis(10); private static final long MAX_LIFETIME = MINUTES.toMillis(30); - private static final long DEFAULT_KEEPALIVE_TIME = MINUTES.toMillis(30); + private static final long DEFAULT_KEEPALIVE_TIME = 0L; private static final int DEFAULT_POOL_SIZE = 10; private static boolean unitTest = false; @@ -100,8 +100,6 @@ public class HikariConfig implements HikariConfigMXBean private Object healthCheckRegistry; private Properties healthCheckProperties; - private boolean isKeepalive; - private long keepaliveTime; private volatile boolean sealed; @@ -122,7 +120,6 @@ public HikariConfig() idleTimeout = IDLE_TIMEOUT; initializationFailTimeout = 1; isAutoCommit = true; - isKeepalive = false; keepaliveTime = DEFAULT_KEEPALIVE_TIME; String systemProp = System.getProperty("hikaricp.configurationFile"); @@ -727,20 +724,24 @@ public void addHealthCheckProperty(String key, String value) healthCheckProperties.setProperty(key, value); } - public boolean isKeepalive() { - return isKeepalive; - } - - public void setKeepalive(boolean keepalive) { - isKeepalive = keepalive; - } - + /** + * This property controls the keepalive interval for a connection in the pool. An in-use connection will never be + * tested by the keepalive thread, only when it is idle will it be tested. + * + * @return the interval in which connections will be tested for aliveness, thus keeping them alive by the act of checking. Value is in milliseconds, default is 0 (disabled). + */ public long getKeepaliveTime() { return keepaliveTime; } - public void setKeepaliveTime(long keepaliveTime) { - this.keepaliveTime = keepaliveTime; + /** + * This property controls the keepalive interval for a connection in the pool. An in-use connection will never be + * tested by the keepalive thread, only when it is idle will it be tested. + * + * @param keepaliveTimeMs the interval in which connections will be tested for aliveness, thus keeping them alive by the act of checking. Value is in milliseconds, default is 0 (disabled). + */ + public void setKeepaliveTime(long keepaliveTimeMs) { + this.keepaliveTime = keepaliveTimeMs; } /** @@ -1042,8 +1043,14 @@ private void validateNumerics() } // keepalive time must larger then 30 seconds - if (keepaliveTime < SECONDS.toMillis(30)) { - LOGGER.warn("{} - keepaliveTime is less than 30000ms, setting to default {}ms.", poolName, DEFAULT_KEEPALIVE_TIME); + if (keepaliveTime != 0 && keepaliveTime < SECONDS.toMillis(30)) { + LOGGER.warn("{} - keepaliveTime is less than 30000ms, disabling it.", poolName); + keepaliveTime = DEFAULT_KEEPALIVE_TIME; + } + + // keepalive time must be less than maxLifetime (if maxLifetime is enabled) + if (keepaliveTime != 0 && maxLifetime != 0 && keepaliveTime < maxLifetime) { + LOGGER.warn("{} - keepaliveTime is less than maxLifetime, disabling it.", poolName); keepaliveTime = DEFAULT_KEEPALIVE_TIME; } diff --git a/src/main/java/com/zaxxer/hikari/pool/HikariPool.java b/src/main/java/com/zaxxer/hikari/pool/HikariPool.java index e62aadf3f..2b66c105a 100644 --- a/src/main/java/com/zaxxer/hikari/pool/HikariPool.java +++ b/src/main/java/com/zaxxer/hikari/pool/HikariPool.java @@ -28,7 +28,7 @@ import com.zaxxer.hikari.util.ConcurrentBag; import com.zaxxer.hikari.util.ConcurrentBag.IBagStateListener; import com.zaxxer.hikari.util.SuspendResumeLock; -import com.zaxxer.hikari.util.UtilityElf.*; +import com.zaxxer.hikari.util.UtilityElf.DefaultThreadFactory; import io.micrometer.core.instrument.MeterRegistry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -477,7 +477,7 @@ private PoolEntry createPoolEntry() final PoolEntry poolEntry = newPoolEntry(); final long maxLifetime = config.getMaxLifetime(); - final boolean isKeepalive = config.isKeepalive(); + final long keepaliveTime = config.getKeepaliveTime(); if (maxLifetime > 0) { // variance up to 2.5% of the maxlifetime @@ -491,21 +491,21 @@ private PoolEntry createPoolEntry() }, lifetime, MILLISECONDS)); } - if (isKeepalive) { - final long keepaliveTime = config.getKeepaliveTime(); + + if (keepaliveTime > 0) { // variance up to 10% of the heartbeat time final long variance = ThreadLocalRandom.current().nextLong(keepaliveTime / 10); final long heartbeatTime = keepaliveTime - variance; poolEntry.setKeepalive(houseKeepingExecutorService.scheduleWithFixedDelay( () -> { - boolean isConnectionAlive = keepaliveConnection(poolEntry); - if (!isConnectionAlive) { - if (softEvictConnection(poolEntry, DEAD_CONNECTION_MESSAGE, false)) { + if (connectionBag.reserve(poolEntry)) { + if (!isConnectionAlive(poolEntry.connection)) { + softEvictConnection(poolEntry, DEAD_CONNECTION_MESSAGE, true); addBagItem(connectionBag.getWaitingThreadCount()); } - } - if (logger.isDebugEnabled()) { - logger.debug("{} - keepalive heartbeat: Is connection alive? {}", poolName, isConnectionAlive); + else { + logger.debug("{} - keepalive: connection {} is alive", poolName, poolEntry.connection); + } } }, heartbeatTime, heartbeatTime, MILLISECONDS)); } @@ -639,16 +639,6 @@ private boolean softEvictConnection(final PoolEntry poolEntry, final String reas return false; } - /** - * check if the connection is alive - * if the connection state is in use ,we suppose it is alive. then we'll check it by {@link PoolBase#isConnectionAlive} - * @param poolEntry the PoolEntry (/Connection) to be checked from the pool - * @return true if the connection is alive,false if it was dead - */ - private boolean keepaliveConnection(final PoolEntry poolEntry) { - return poolEntry.getState() == STATE_IN_USE || isConnectionAlive(poolEntry.connection); - } - /** * Create/initialize the Housekeeping service {@link ScheduledExecutorService}. If the user specified an Executor * to be used in the {@link HikariConfig}, then we use that. If no Executor was specified (typical), then create diff --git a/src/main/java/com/zaxxer/hikari/pool/PoolEntry.java b/src/main/java/com/zaxxer/hikari/pool/PoolEntry.java index 716300ebd..93f892b19 100644 --- a/src/main/java/com/zaxxer/hikari/pool/PoolEntry.java +++ b/src/main/java/com/zaxxer/hikari/pool/PoolEntry.java @@ -47,7 +47,6 @@ final class PoolEntry implements IConcurrentBagEntry private volatile boolean evict; private volatile ScheduledFuture endOfLife; - private volatile ScheduledFuture keepalive; private final FastList openStatements; @@ -183,7 +182,7 @@ Connection close() ScheduledFuture ka = keepalive; if (ka != null && !ka.isDone() && !ka.cancel(false)) { - LOGGER.warn("{} - keepalive expiration task cancellation unexpectedly returned false for connection {}", getPoolName(), connection); + LOGGER.warn("{} - keepalive task cancellation unexpectedly returned false for connection {}", getPoolName(), connection); } Connection con = connection; diff --git a/src/test/java/com/zaxxer/hikari/mocks/StubConnection.java b/src/test/java/com/zaxxer/hikari/mocks/StubConnection.java index 7991433b2..4660fbc82 100644 --- a/src/test/java/com/zaxxer/hikari/mocks/StubConnection.java +++ b/src/test/java/com/zaxxer/hikari/mocks/StubConnection.java @@ -47,7 +47,7 @@ public class StubConnection extends StubBaseConnection implements Connection public static final AtomicInteger count = new AtomicInteger(); public static volatile boolean slowCreate; public static volatile boolean oldDriver; - private volatile boolean isClose = false; + private volatile boolean isClosed = false; private static long foo; private boolean autoCommit; @@ -55,7 +55,7 @@ public class StubConnection extends StubBaseConnection implements Connection private String catalog; private long waitTimeout; - private ScheduledExecutorService connectionWaitTimeout = new ScheduledThreadPoolExecutor(1); + private static ScheduledExecutorService connectionWaitTimeout = new ScheduledThreadPoolExecutor(1); private ScheduledFuture waitTimeoutTask; static { @@ -75,9 +75,10 @@ public StubConnection(long waitTimeout) { if (slowCreate) { UtilityElf.quietlySleep(1000); } - try{ + + try { refreshConnectionWaitTimeout(); - }catch (Exception e){ + } catch (Exception e){ //ignore } } @@ -151,14 +152,16 @@ public void commit() throws SQLException } private void refreshConnectionWaitTimeout() throws SQLException { - if (this.isClose) { + if (this.isClosed) { throw new SQLException("connection has been closed"); } + if (waitTimeoutTask != null) { waitTimeoutTask.cancel(true); } + if (waitTimeout > 0) { - waitTimeoutTask = connectionWaitTimeout.schedule(() -> { this.isClose = true;}, waitTimeout, TimeUnit.MILLISECONDS); + waitTimeoutTask = connectionWaitTimeout.schedule(() -> { this.isClosed = true;}, waitTimeout, TimeUnit.MILLISECONDS); } } @@ -183,7 +186,7 @@ public boolean isClosed() throws SQLException if (throwException) { throw new SQLException(); } - return isClose; + return isClosed; } /** {@inheritDoc} */ @@ -440,7 +443,7 @@ public boolean isValid(int timeout) throws SQLException throw new SQLException(); } refreshConnectionWaitTimeout(); - return !isClose; + return !isClosed; } /** {@inheritDoc} */ diff --git a/src/test/java/com/zaxxer/hikari/pool/TestConnections.java b/src/test/java/com/zaxxer/hikari/pool/TestConnections.java index 5328ac4bc..f54417643 100644 --- a/src/test/java/com/zaxxer/hikari/pool/TestConnections.java +++ b/src/test/java/com/zaxxer/hikari/pool/TestConnections.java @@ -232,7 +232,6 @@ public void testKeepalive() throws Exception{ setConfigUnitTest(true); try (HikariDataSource ds = new HikariDataSource(config)) { - getUnsealedConfig(ds).setKeepalive(true); getUnsealedConfig(ds).setKeepaliveTime(500); HikariPool pool = getPool(ds); @@ -264,7 +263,6 @@ public void testKeepalive2() throws Exception{ setConfigUnitTest(true); try (HikariDataSource ds = new HikariDataSource(config)) { - getUnsealedConfig(ds).setKeepalive(true); getUnsealedConfig(ds).setKeepaliveTime(700); HikariPool pool = getPool(ds); From 626c4fb2deb1c5c8faba61279c6b9c8f159f1f0c Mon Sep 17 00:00:00 2001 From: cadeeper Date: Tue, 12 Jan 2021 10:53:51 +0800 Subject: [PATCH 6/6] optimize code --- src/main/java/com/zaxxer/hikari/HikariConfig.java | 4 ++-- src/main/java/com/zaxxer/hikari/pool/HikariPool.java | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/zaxxer/hikari/HikariConfig.java b/src/main/java/com/zaxxer/hikari/HikariConfig.java index 6d8bf38b9..e1a375c19 100644 --- a/src/main/java/com/zaxxer/hikari/HikariConfig.java +++ b/src/main/java/com/zaxxer/hikari/HikariConfig.java @@ -1049,8 +1049,8 @@ private void validateNumerics() } // keepalive time must be less than maxLifetime (if maxLifetime is enabled) - if (keepaliveTime != 0 && maxLifetime != 0 && keepaliveTime < maxLifetime) { - LOGGER.warn("{} - keepaliveTime is less than maxLifetime, disabling it.", poolName); + if (keepaliveTime != 0 && maxLifetime != 0 && keepaliveTime >= maxLifetime) { + LOGGER.warn("{} - keepaliveTime is greater than or equal to maxLifetime, disabling it.", poolName); keepaliveTime = DEFAULT_KEEPALIVE_TIME; } diff --git a/src/main/java/com/zaxxer/hikari/pool/HikariPool.java b/src/main/java/com/zaxxer/hikari/pool/HikariPool.java index 2b66c105a..5b49bec3b 100644 --- a/src/main/java/com/zaxxer/hikari/pool/HikariPool.java +++ b/src/main/java/com/zaxxer/hikari/pool/HikariPool.java @@ -504,6 +504,7 @@ private PoolEntry createPoolEntry() addBagItem(connectionBag.getWaitingThreadCount()); } else { + connectionBag.unreserve(poolEntry); logger.debug("{} - keepalive: connection {} is alive", poolName, poolEntry.connection); } }