Skip to content

Commit b04d1d7

Browse files
committed
Move expensive operations outside lock
1 parent 6f606a1 commit b04d1d7

File tree

2 files changed

+82
-72
lines changed

2 files changed

+82
-72
lines changed

ebean-datasource/src/main/java/io/ebean/datasource/pool/ConnectionBuffer.java

Lines changed: 25 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -239,29 +239,44 @@ PooledConnection removeFree(Object affinityId) {
239239
return pc;
240240
}
241241

242+
242243
/**
243-
* Close all free connections in this buffer.
244+
* Clears the freelist and return the connections.
244245
*/
245-
void closeAllFree(boolean logErrors) {
246+
List<PooledConnection> clearFreeList() {
246247
List<PooledConnection> tempList = new ArrayList<>();
247248

248249
freeList.forEach(pc -> {
249250
pc.unlink();
250251
tempList.add(pc);
251252
});
253+
return tempList;
254+
}
252255

253-
if (Log.isLoggable(System.Logger.Level.TRACE)) {
254-
Log.trace("... closing all {0} connections from the free list with logErrors: {1}", tempList.size(), logErrors);
255-
}
256-
for (PooledConnection connection : tempList) {
257-
connection.closeConnectionFully(logErrors);
258-
}
256+
257+
/**
258+
* Clears the busy list and return the connections that should be considered leaked.
259+
*/
260+
List<PooledConnection> cleanupBusyList(long leakTimeMinutes) {
261+
long olderThanTime = System.currentTimeMillis() - (leakTimeMinutes * 60000);
262+
List<PooledConnection> tempList = new ArrayList<>();
263+
264+
busyList.forEach(pc -> {
265+
if (pc.lastUsedTime() > olderThanTime) {
266+
// PooledConnection has been used recently or
267+
// expected to be longRunning so not closing...
268+
} else {
269+
pc.unlink();
270+
tempList.add(pc);
271+
}
272+
});
273+
return tempList;
259274
}
260275

261276
/**
262277
* Trim any inactive connections that have not been used since usedSince.
263278
*/
264-
int trim(int minSize, long usedSince, long createdSince) {
279+
List<PooledConnection> trim(int minSize, long usedSince, long createdSince) {
265280
int toTrim = freeSize() - minSize;
266281

267282
List<PooledConnection> ret = new ArrayList<>(toTrim);
@@ -274,35 +289,7 @@ int trim(int minSize, long usedSince, long createdSince) {
274289
ret.add(pc);
275290
}
276291
}
277-
ret.forEach(pc -> pc.closeConnectionFully(true));
278-
return ret.size();
279-
}
280-
281-
/**
282-
* Close connections that should be considered leaked.
283-
*/
284-
void closeBusyConnections(long leakTimeMinutes) {
285-
long olderThanTime = System.currentTimeMillis() - (leakTimeMinutes * 60000);
286-
Log.debug("Closing busy connections using leakTimeMinutes {0}", leakTimeMinutes);
287-
busyList.forEach(pc -> {
288-
if (pc.lastUsedTime() > olderThanTime) {
289-
// PooledConnection has been used recently or
290-
// expected to be longRunning so not closing...
291-
} else {
292-
pc.unlink();
293-
closeBusyConnection(pc);
294-
}
295-
});
296-
}
297-
298-
private void closeBusyConnection(PooledConnection pc) {
299-
try {
300-
Log.warn("DataSource closing busy connection? {0}", pc.fullDescription());
301-
System.out.println("CLOSING busy connection: " + pc.fullDescription());
302-
pc.closeConnectionFully(false);
303-
} catch (Exception ex) {
304-
Log.error("Error when closing potentially leaked connection " + pc.description(), ex);
305-
}
292+
return ret;
306293
}
307294

308295
/**

ebean-datasource/src/main/java/io/ebean/datasource/pool/PooledConnectionQueue.java

Lines changed: 57 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import io.ebean.datasource.pool.ConnectionPool.Status;
66

77
import java.sql.SQLException;
8+
import java.util.Collections;
9+
import java.util.List;
810
import java.util.concurrent.TimeUnit;
911
import java.util.concurrent.locks.Condition;
1012
import java.util.concurrent.locks.ReentrantLock;
@@ -107,15 +109,10 @@ PoolStatus status(boolean reset) {
107109
}
108110

109111
void setMaxSize(int maxSize) {
110-
lock.lock();
111-
try {
112-
if (maxSize < this.minSize) {
113-
throw new IllegalArgumentException("maxSize " + maxSize + " < minSize " + this.minSize);
114-
}
115-
this.maxSize = maxSize;
116-
} finally {
117-
lock.unlock();
112+
if (maxSize < this.minSize) {
113+
throw new IllegalArgumentException("maxSize " + maxSize + " < minSize " + this.minSize);
118114
}
115+
this.maxSize = maxSize;
119116
}
120117

121118
private int totalConnections() {
@@ -141,20 +138,24 @@ void ensureMinimumConnections() throws SQLException {
141138
* Return a PooledConnection.
142139
*/
143140
void returnPooledConnection(PooledConnection c, boolean forceClose) {
141+
boolean closeConnection = false;
144142
lock.lock();
145143
try {
146144
if (!buffer.removeBusy(c)) {
147145
Log.error("Connection [{0}] not found in BusyList?", c);
148146
}
149147
if (forceClose || c.shouldTrimOnReturn(lastResetTime, maxAgeMillis)) {
150-
c.closeConnectionFully(false);
148+
closeConnection = true;
151149
} else {
152150
buffer.addFree(c);
153151
notEmpty.signal();
154152
}
155153
} finally {
156154
lock.unlock();
157155
}
156+
if (closeConnection) {
157+
c.closeConnectionFully(false);
158+
}
158159
}
159160

160161
private PooledConnection extractFromFreeList(Object affinitiyId) {
@@ -356,17 +357,12 @@ void reset(long leakTimeMinutes) {
356357
}
357358

358359
void trim(long maxInactiveMillis, long maxAgeMillis) {
359-
lock.lock();
360-
try {
361-
if (trimInactiveConnections(maxInactiveMillis, maxAgeMillis)) {
362-
try {
363-
ensureMinimumConnections();
364-
} catch (SQLException e) {
365-
Log.error("Error trying to ensure minimum connections", e);
366-
}
360+
if (trimInactiveConnections(maxInactiveMillis, maxAgeMillis)) {
361+
try {
362+
ensureMinimumConnections();
363+
} catch (SQLException e) {
364+
Log.error("Error trying to ensure minimum connections", e);
367365
}
368-
} finally {
369-
lock.unlock();
370366
}
371367
}
372368

@@ -375,33 +371,50 @@ void trim(long maxInactiveMillis, long maxAgeMillis) {
375371
*/
376372
private boolean trimInactiveConnections(long maxInactiveMillis, long maxAgeMillis) {
377373
final long createdSince = (maxAgeMillis == 0) ? 0 : System.currentTimeMillis() - maxAgeMillis;
378-
final int trimmedCount;
379-
if (buffer.freeSize() > minSize) {
380-
// trim on maxInactive and maxAge
381-
long usedSince = System.currentTimeMillis() - maxInactiveMillis;
382-
trimmedCount = buffer.trim(minSize, usedSince, createdSince);
383-
} else if (createdSince > 0) {
384-
// trim only on maxAge
385-
trimmedCount = buffer.trim(0, createdSince, createdSince);
386-
} else {
387-
trimmedCount = 0;
374+
final List<PooledConnection> toTrim;
375+
lock.lock();
376+
try {
377+
if (buffer.freeSize() > minSize) {
378+
// trim on maxInactive and maxAge
379+
long usedSince = System.currentTimeMillis() - maxInactiveMillis;
380+
toTrim = buffer.trim(minSize, usedSince, createdSince);
381+
} else if (createdSince > 0) {
382+
// trim only on maxAge
383+
toTrim = buffer.trim(0, createdSince, createdSince);
384+
} else {
385+
toTrim = Collections.emptyList();
386+
}
387+
} finally {
388+
lock.unlock();
389+
}
390+
if (toTrim.isEmpty()) {
391+
return false;
388392
}
389-
if (trimmedCount > 0 && Log.isLoggable(DEBUG)) {
390-
Log.debug("DataSource [{0}] trimmed [{1}] inactive connections. New size[{2}]", name, trimmedCount, totalConnections());
393+
toTrim.forEach(pc -> pc.closeConnectionFully(true));
394+
if (Log.isLoggable(DEBUG)) {
395+
Log.debug("DataSource [{0}] trimmed [{1}] inactive connections. New size[{2}]", name, toTrim.size(), totalConnections());
391396
}
392-
return trimmedCount > 0 && buffer.freeSize() < minSize;
397+
return buffer.freeSize() < minSize;
393398
}
394399

395400
/**
396401
* Close all the connections that are in the free list.
397402
*/
398403
private void closeFreeConnections(boolean logErrors) {
404+
List<PooledConnection> tempList;
399405
lock.lock();
400406
try {
401-
buffer.closeAllFree(logErrors);
407+
tempList = buffer.clearFreeList();
402408
} finally {
403409
lock.unlock();
404410
}
411+
// closing the connections is done outside lock
412+
if (Log.isLoggable(System.Logger.Level.TRACE)) {
413+
Log.trace("... closing all {0} connections from the free list with logErrors: {1}", tempList.size(), logErrors);
414+
}
415+
for (PooledConnection connection : tempList) {
416+
connection.closeConnectionFully(logErrors);
417+
}
405418
}
406419

407420
/**
@@ -415,12 +428,22 @@ private void closeFreeConnections(boolean logErrors) {
415428
* closed and put back into the pool.
416429
*/
417430
void closeBusyConnections(long leakTimeMinutes) {
431+
List<PooledConnection> tempList;
418432
lock.lock();
419433
try {
420-
buffer.closeBusyConnections(leakTimeMinutes);
434+
tempList = buffer.cleanupBusyList(leakTimeMinutes);
421435
} finally {
422436
lock.unlock();
423437
}
438+
for (PooledConnection pc : tempList) {
439+
try {
440+
Log.warn("DataSource closing busy connection? {0}", pc.fullDescription());
441+
System.out.println("CLOSING busy connection: " + pc.fullDescription());
442+
pc.closeConnectionFully(false);
443+
} catch (Exception ex) {
444+
Log.error("Error when closing potentially leaked connection " + pc.description(), ex);
445+
}
446+
}
424447
}
425448

426449
String getBusyConnectionInformation() {

0 commit comments

Comments
 (0)