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 e999ce2..094d2de 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 @@ -226,18 +226,13 @@ private PooledConnection _obtainConnection() throws InterruptedException, SQLExc hitCount++; // are other threads already waiting? (they get priority) if (waitingThreads == 0) { - PooledConnection freeConnection = extractFromFreeList(); - if (freeConnection != null) { - return freeConnection; + PooledConnection connection = extractFromFreeList(); + if (connection != null) { + return connection; } - if (busyList.size() < maxSize) { - // grow the connection pool - PooledConnection c = pool.createConnectionForQueue(connectionId++); - int busySize = registerBusyConnection(c); - if (Log.isLoggable(DEBUG)) { - Log.debug("DataSource [{0}] grow; id[{1}] busy[{2}] max[{3}]", name, c.name(), busySize, maxSize); - } - return c; + connection = createConnection(); + if (connection != null) { + return connection; } } try { @@ -257,6 +252,20 @@ private PooledConnection _obtainConnection() throws InterruptedException, SQLExc } } + private PooledConnection createConnection() throws SQLException { + if (busyList.size() < maxSize) { + // grow the connection pool + PooledConnection c = pool.createConnectionForQueue(connectionId++); + int busySize = registerBusyConnection(c); + if (Log.isLoggable(DEBUG)) { + Log.debug("DataSource [{0}] grow; id[{1}] busy[{2}] max[{3}]", name, c.name(), busySize, maxSize); + } + return c; + } else { + return null; + } + } + /** * Got into a loop waiting for connections to be returned to the pool. */ @@ -264,6 +273,11 @@ private PooledConnection _obtainConnectionWaitLoop() throws SQLException, Interr long nanos = MILLIS_TIME_UNIT.toNanos(waitTimeoutMillis); for (; ; ) { if (nanos <= 0) { + // We waited long enough, that a connection was returned, so we try to create a new connection. + PooledConnection conn = createConnection(); + if (conn != null) { + return conn; + } String msg = "Unsuccessfully waited [" + waitTimeoutMillis + "] millis for a connection to be returned." + " No connections are free. You need to Increase the max connections of [" + maxSize + "]" + " or look for a connection pool leak using datasource.xxx.capturestacktrace=true"; diff --git a/ebean-datasource/src/test/java/io/ebean/datasource/pool/ConnectionPoolRecoverTest.java b/ebean-datasource/src/test/java/io/ebean/datasource/pool/ConnectionPoolRecoverTest.java new file mode 100644 index 0000000..3f9428b --- /dev/null +++ b/ebean-datasource/src/test/java/io/ebean/datasource/pool/ConnectionPoolRecoverTest.java @@ -0,0 +1,43 @@ +package io.ebean.datasource.pool; + +import io.ebean.datasource.DataSourceBuilder; +import io.ebean.datasource.DataSourcePool; +import org.junit.jupiter.api.Test; + +import java.sql.Connection; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ConnectionPoolRecoverTest { + + @Test + void testHeavyLoadPool() throws Exception { + DataSourcePool pool = DataSourceBuilder.create() + .url("jdbc:h2:mem:testConnectionPoolFull") + .username("sa") + .password("sa") + .heartbeatFreqSecs(1) + .minConnections(1) + .maxConnections(1) + .trimPoolFreqSecs(1) + // .heartbeatMaxPoolExhaustedCount(1) + .failOnStart(false) + .build(); + try { + for (int i = 0; i < 5; i++) { + try (Connection conn = pool.getConnection()) { + Thread.sleep(2000); + conn.rollback(); + } + } + } finally { + pool.shutdown(); + } + } + +}