From ca9b82970cd6b8b5267ff6656bb00ef24559df4e Mon Sep 17 00:00:00 2001 From: Brett Wooldridge Date: Tue, 21 Oct 2014 13:24:11 +0900 Subject: [PATCH] Exploit Connection network timeout if available. --- .../com/zaxxer/hikari/pool/HikariPool.java | 23 +++++++++++--- .../com/zaxxer/hikari/util/PoolUtilities.java | 31 +++++++++++++++++-- .../com/zaxxer/hikari/pool/HikariPool.java | 30 +++++++++++------- .../com/zaxxer/hikari/util/PoolUtilities.java | 24 ++++++++++++-- 4 files changed, 86 insertions(+), 22 deletions(-) diff --git a/hikaricp-java6/src/main/java/com/zaxxer/hikari/pool/HikariPool.java b/hikaricp-java6/src/main/java/com/zaxxer/hikari/pool/HikariPool.java index 6f9a7a77..7ec31739 100644 --- a/hikaricp-java6/src/main/java/com/zaxxer/hikari/pool/HikariPool.java +++ b/hikaricp-java6/src/main/java/com/zaxxer/hikari/pool/HikariPool.java @@ -94,6 +94,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener private volatile boolean isShutdown; private volatile long connectionTimeout; + private volatile boolean isJdbc41Compliant; /** * Construct a HikariPool with the specified configuration. @@ -430,6 +431,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener connection = (username == null && password == null) ? dataSource.getConnection() : dataSource.getConnection(username, password); transactionIsolation = (transactionIsolation < 0 ? connection.getTransactionIsolation() : transactionIsolation); connectionCustomizer.customize(connection); + isJdbc41Compliant = PoolUtilities.isJdbc41Compliant(connection); executeSqlAutoCommit(connection, configuration.getConnectionInitSql()); @@ -474,21 +476,32 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener return connection.isValid((int) TimeUnit.MILLISECONDS.toSeconds(timeoutMs)); } + int networkTimeout = 0; + if (isJdbc41Compliant) { + networkTimeout = connection.getNetworkTimeout(); + connection.setNetworkTimeout(houseKeepingExecutorService, timeoutEnabled ? (int) timeoutMs : 0); + } + Statement statement = connection.createStatement(); try { statement.setQueryTimeout((int) TimeUnit.MILLISECONDS.toSeconds(timeoutMs)); statement.executeQuery(configuration.getConnectionTestQuery()); - return true; } finally { statement.close(); - if (isIsolateInternalQueries && !isAutoCommit) { - connection.rollback(); - } } + + if (isIsolateInternalQueries && !isAutoCommit) { + connection.rollback(); + } + if (isJdbc41Compliant) { + connection.setNetworkTimeout(houseKeepingExecutorService, networkTimeout); + } + + return true; } catch (SQLException e) { - LOGGER.warn("Exception during keep alive check, that means the connection must be dead.", e); + LOGGER.warn("Exception during keep alive check, that means the connection ({}) must be dead.", connection, e); return false; } } diff --git a/hikaricp-java6/src/main/java/com/zaxxer/hikari/util/PoolUtilities.java b/hikaricp-java6/src/main/java/com/zaxxer/hikari/util/PoolUtilities.java index 49a138b1..8ef0c0ab 100644 --- a/hikaricp-java6/src/main/java/com/zaxxer/hikari/util/PoolUtilities.java +++ b/hikaricp-java6/src/main/java/com/zaxxer/hikari/util/PoolUtilities.java @@ -19,6 +19,9 @@ public final class PoolUtilities { public static final boolean IS_JAVA7; + private static volatile boolean IS_JDBC4; + private static volatile boolean jdbc4checked; + static { boolean b = false; try { @@ -30,7 +33,12 @@ public final class PoolUtilities IS_JAVA7 = b; } - public static void quietlyCloseConnection(Connection connection) + /** + * Close connection and eat any exception. + * + * @param connection the connection to close + */ + public static void quietlyCloseConnection(final Connection connection) { if (connection != null) { try { @@ -164,10 +172,27 @@ public final class PoolUtilities threadFactory = new DefaultThreadFactory(threadName, true); } - int processors = Math.max(1, Runtime.getRuntime().availableProcessors() / 4); LinkedBlockingQueue queue = new LinkedBlockingQueue(queueSize); - ThreadPoolExecutor executor = new ThreadPoolExecutor(processors, processors, 5, TimeUnit.SECONDS, queue, threadFactory, policy); + ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 5, TimeUnit.SECONDS, queue, threadFactory, policy); executor.allowCoreThreadTimeOut(true); return executor; } + + public static boolean isJdbc41Compliant(Connection connection) + { + if (jdbc4checked) { + return IS_JDBC4; + } + + try { + connection.getNetworkTimeout(); // This will throw AbstractMethodError or SQLException in the case of a non-JDBC 41 compliant driver + IS_JDBC4 = true; + } + catch (Exception e) { + IS_JDBC4 = false; + } + + jdbc4checked = true; + return IS_JDBC4; + } } diff --git a/hikaricp/src/main/java/com/zaxxer/hikari/pool/HikariPool.java b/hikaricp/src/main/java/com/zaxxer/hikari/pool/HikariPool.java index 112c490c..9ab172c4 100644 --- a/hikaricp/src/main/java/com/zaxxer/hikari/pool/HikariPool.java +++ b/hikaricp/src/main/java/com/zaxxer/hikari/pool/HikariPool.java @@ -93,6 +93,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener private volatile boolean isShutdown; private volatile long connectionTimeout; + private volatile boolean isJdbc41Compliant; /** * Construct a HikariPool with the specified configuration. @@ -368,11 +369,7 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener @Override public void closeIdleConnections() { - connectionBag.values(STATE_NOT_IN_USE).forEach(bagEntry -> { - if (connectionBag.reserve(bagEntry)) { - closeConnection(bagEntry); - } - }); + connectionBag.values(STATE_NOT_IN_USE).stream().filter(p -> connectionBag.reserve(p)).forEach(bagEntry -> closeConnection(bagEntry)); } /** {@inheritDoc} */ @@ -414,10 +411,10 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener Connection connection = null; try { - connection = (username == null && password == null) ? dataSource.getConnection() : dataSource.getConnection(username, password); transactionIsolation = (transactionIsolation < 0 ? connection.getTransactionIsolation() : transactionIsolation); connectionCustomizer.customize(connection); + isJdbc41Compliant = PoolUtilities.isJdbc41Compliant(connection); executeSqlAutoCommit(connection, configuration.getConnectionInitSql()); @@ -462,19 +459,28 @@ public final class HikariPool implements HikariPoolMBean, IBagStateListener return connection.isValid((int) TimeUnit.MILLISECONDS.toSeconds(timeoutMs)); } + int networkTimeout = 0; + if (isJdbc41Compliant) { + networkTimeout = connection.getNetworkTimeout(); + connection.setNetworkTimeout(houseKeepingExecutorService, timeoutEnabled ? (int) timeoutMs : 0); + } + try (Statement statement = connection.createStatement()) { statement.setQueryTimeout((int) TimeUnit.MILLISECONDS.toSeconds(timeoutMs)); statement.executeQuery(configuration.getConnectionTestQuery()); - return true; } - finally { - if (isIsolateInternalQueries && !isAutoCommit) { - connection.rollback(); - } + + if (isIsolateInternalQueries && !isAutoCommit) { + connection.rollback(); + } + if (isJdbc41Compliant) { + connection.setNetworkTimeout(houseKeepingExecutorService, networkTimeout); } + + return true; } catch (SQLException e) { - LOGGER.warn("Exception during keep alive check, that means the connection (" + connection + ") must be dead.", e); + LOGGER.warn("Exception during keep alive check, that means the connection ({}) must be dead.", connection, e); return false; } } diff --git a/hikaricp/src/main/java/com/zaxxer/hikari/util/PoolUtilities.java b/hikaricp/src/main/java/com/zaxxer/hikari/util/PoolUtilities.java index 38e4468b..2bec5808 100644 --- a/hikaricp/src/main/java/com/zaxxer/hikari/util/PoolUtilities.java +++ b/hikaricp/src/main/java/com/zaxxer/hikari/util/PoolUtilities.java @@ -16,6 +16,9 @@ import javax.sql.DataSource; public final class PoolUtilities { + private static volatile boolean IS_JDBC4; + private static volatile boolean jdbc4checked; + /** * Close connection and eat any exception. * @@ -151,10 +154,27 @@ public final class PoolUtilities threadFactory = new DefaultThreadFactory(threadName, true); } - int processors = Math.max(1, Runtime.getRuntime().availableProcessors() / 4); LinkedBlockingQueue queue = new LinkedBlockingQueue(queueSize); - ThreadPoolExecutor executor = new ThreadPoolExecutor(processors, processors, 5, TimeUnit.SECONDS, queue, threadFactory, policy); + ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 5, TimeUnit.SECONDS, queue, threadFactory, policy); executor.allowCoreThreadTimeOut(true); return executor; } + + public static boolean isJdbc41Compliant(Connection connection) + { + if (jdbc4checked) { + return IS_JDBC4; + } + + try { + connection.getNetworkTimeout(); // This will throw AbstractMethodError or SQLException in the case of a non-JDBC 41 compliant driver + IS_JDBC4 = true; + } + catch (AbstractMethodError | SQLException e) { + IS_JDBC4 = false; + } + + jdbc4checked = true; + return IS_JDBC4; + } }