diff --git a/ebean-datasource/src/main/java/io/ebean/datasource/pool/ConnectionPool.java b/ebean-datasource/src/main/java/io/ebean/datasource/pool/ConnectionPool.java index 4cffbfb..59dd8c2 100644 --- a/ebean-datasource/src/main/java/io/ebean/datasource/pool/ConnectionPool.java +++ b/ebean-datasource/src/main/java/io/ebean/datasource/pool/ConnectionPool.java @@ -550,33 +550,16 @@ private boolean testConnection(Connection conn) throws SQLException { if (heartbeatsql == null) { return conn.isValid(heartbeatTimeoutSeconds); } - Statement stmt = null; - ResultSet rset = null; - try { - // It should only error IF the DataSource is down or a network issue - stmt = conn.createStatement(); + // It should only error IF the DataSource is down or a network issue + try (Statement stmt = conn.createStatement()) { if (heartbeatTimeoutSeconds > 0) { stmt.setQueryTimeout(heartbeatTimeoutSeconds); } - rset = stmt.executeQuery(heartbeatsql); - conn.commit(); - + stmt.execute(heartbeatsql); return true; - } finally { - try { - if (rset != null) { - rset.close(); - } - } catch (SQLException e) { - log.error("Error closing resultSet", e); - } - try { - if (stmt != null) { - stmt.close(); - } - } catch (SQLException e) { - log.error("Error closing statement", e); + if (!conn.getAutoCommit()) { + conn.rollback(); } } } diff --git a/ebean-datasource/src/main/java/io/ebean/datasource/pool/FreeConnectionBuffer.java b/ebean-datasource/src/main/java/io/ebean/datasource/pool/FreeConnectionBuffer.java index 0555d10..2dd9dca 100644 --- a/ebean-datasource/src/main/java/io/ebean/datasource/pool/FreeConnectionBuffer.java +++ b/ebean-datasource/src/main/java/io/ebean/datasource/pool/FreeConnectionBuffer.java @@ -1,9 +1,6 @@ package io.ebean.datasource.pool; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; +import java.util.*; /** * A buffer designed especially to hold free pooled connections. @@ -64,9 +61,9 @@ void closeAll(boolean logErrors) { /** * Trim any inactive connections that have not been used since usedSince. */ - int trim(long usedSince, long createdSince) { + int trim(int minSize, long usedSince, long createdSince) { int trimCount = 0; - Iterator iterator = freeBuffer.iterator(); + ListIterator iterator = freeBuffer.listIterator(minSize); while (iterator.hasNext()) { PooledConnection pooledConnection = iterator.next(); if (pooledConnection.shouldTrim(usedSince, createdSince)) { diff --git a/ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnectionQueue.java b/ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnectionQueue.java index 43b4a2d..7d7ac0e 100644 --- a/ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnectionQueue.java +++ b/ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnectionQueue.java @@ -158,7 +158,6 @@ void ensureMinimumConnections() throws SQLException { } notEmpty.signal(); } - } finally { lock.unlock(); } @@ -338,7 +337,7 @@ public void reset(long leakTimeMinutes) { public void trim(long maxInactiveMillis, long maxAgeMillis) { lock.lock(); try { - if (trimInactiveConnections(maxInactiveMillis, maxAgeMillis) > 0) { + if (trimInactiveConnections(maxInactiveMillis, maxAgeMillis)) { try { ensureMinimumConnections(); } catch (SQLException e) { @@ -353,15 +352,23 @@ public void trim(long maxInactiveMillis, long maxAgeMillis) { /** * Trim connections that have been not used for some time. */ - private int trimInactiveConnections(long maxInactiveMillis, long maxAgeMillis) { - long usedSince = System.currentTimeMillis() - maxInactiveMillis; - long createdSince = (maxAgeMillis == 0) ? 0 : System.currentTimeMillis() - maxAgeMillis; - - int trimmedCount = freeList.trim(usedSince, createdSince); - if (trimmedCount > 0) { + private boolean trimInactiveConnections(long maxInactiveMillis, long maxAgeMillis) { + final long createdSince = (maxAgeMillis == 0) ? 0 : System.currentTimeMillis() - maxAgeMillis; + final int trimmedCount; + if (freeList.size() > minSize) { + // trim on maxInactive and maxAge + long usedSince = System.currentTimeMillis() - maxInactiveMillis; + trimmedCount = freeList.trim(minSize, usedSince, createdSince); + } else if (createdSince > 0) { + // trim only on maxAge + trimmedCount = freeList.trim(0, createdSince, createdSince); + } else { + trimmedCount = 0; + } + if (trimmedCount > 0 && log.isDebugEnabled()) { log.debug("DataSourcePool [{}] trimmed [{}] inactive connections. New size[{}]", name, trimmedCount, totalConnections()); } - return trimmedCount; + return trimmedCount > 0 && freeList.size() < minSize; } /** diff --git a/ebean-datasource/src/test/java/io/ebean/datasource/FactoryTest.java b/ebean-datasource/src/test/java/io/ebean/datasource/FactoryTest.java index 633acf6..455e35d 100644 --- a/ebean-datasource/src/test/java/io/ebean/datasource/FactoryTest.java +++ b/ebean-datasource/src/test/java/io/ebean/datasource/FactoryTest.java @@ -1,9 +1,12 @@ package io.ebean.datasource; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.sql.Connection; import java.sql.PreparedStatement; +import java.util.List; +import java.util.ArrayList; public class FactoryTest { @@ -26,6 +29,43 @@ public void createPool() throws Exception { } } + @Disabled + @Test + void readOnly2() throws Exception { + DataSourceConfig config = new DataSourceConfig() + //.setName("testReadOnly") + .setUrl("jdbc:h2:mem:testReadOnly3") + .setUsername("sa") + .setPassword("") + .setReadOnly(true) + .setAutoCommit(true) + .setHeartbeatSql("select 2") + .setHeartbeatFreqSecs(4) + .setTrimPoolFreqSecs(4) + .setMaxInactiveTimeSecs(4); + + DataSourcePool pool = DataSourceFactory.create("testReadOnly", config); + + try (Connection connection = pool.getConnection()) { + try (PreparedStatement stmt = connection.prepareStatement("create table junk4 (acol varchar(10))")) { + stmt.execute(); + connection.commit(); + } + } + List connections = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + connections.add(pool.getConnection()); + } + for (Connection connection : connections) { + connection.close(); + } + for (int i = 0; i < 30; i++) { + Thread.sleep(1000); + System.out.println("."); + } + pool.shutdown(); + } + @Test public void dataSourceFactory_get_createPool() throws Exception {