Skip to content

Commit b98aaee

Browse files
committed
Internal heartbeat Timer, use Virtual Thread for Java 21
Using Multi-Version jar, use Virtual Thread instead of Timer for executing the heartbeat test
1 parent 535ca43 commit b98aaee

File tree

3 files changed

+104
-15
lines changed

3 files changed

+104
-15
lines changed

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

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@
2626
*/
2727
final class ConnectionPool implements DataSourcePool {
2828

29+
@FunctionalInterface
30+
interface Heartbeat {
31+
32+
void stop();
33+
}
34+
2935
private static final String APPLICATION_NAME = "ApplicationName";
3036
private final ReentrantLock heartbeatLock = new ReentrantLock(false);
3137
private final ReentrantLock notifyLock = new ReentrantLock(false);
@@ -80,7 +86,7 @@ final class ConnectionPool implements DataSourcePool {
8086
private final int waitTimeoutMillis;
8187
private final int pstmtCacheSize;
8288
private final PooledConnectionQueue queue;
83-
private Timer heartBeatTimer;
89+
private Heartbeat heartbeat;
8490
private int heartbeatPoolExhaustedCount;
8591
private final ExecutorService executor;
8692

@@ -161,13 +167,6 @@ void pstmtCacheMetrics(PstmtCache pstmtCache) {
161167
pscRem.add(pstmtCache.removeCount());
162168
}
163169

164-
final class HeartBeatRunnable extends TimerTask {
165-
@Override
166-
public void run() {
167-
heartBeat();
168-
}
169-
}
170-
171170
@Override
172171
public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException {
173172
throw new SQLFeatureNotSupportedException("We do not support java.util.logging");
@@ -387,7 +386,7 @@ private void trimIdleConnections() {
387386
* This is called by the HeartbeatRunnable which should be scheduled to
388387
* run periodically (every heartbeatFreqSecs seconds).
389388
*/
390-
private void heartBeat() {
389+
void heartbeat() {
391390
trimIdleConnections();
392391
if (validateOnHeartbeat) {
393392
testConnection();
@@ -727,11 +726,10 @@ private void startHeartBeatIfStopped() {
727726
heartbeatLock.lock();
728727
try {
729728
// only start if it is not already running
730-
if (heartBeatTimer == null) {
729+
if (heartbeat == null) {
731730
int freqMillis = heartbeatFreqSecs * 1000;
732731
if (freqMillis > 0) {
733-
heartBeatTimer = new Timer(name + ".heartBeat", true);
734-
heartBeatTimer.scheduleAtFixedRate(new HeartBeatRunnable(), freqMillis, freqMillis);
732+
heartbeat = ExecutorFactory.newHeartBeat(this, freqMillis);
735733
}
736734
}
737735
} finally {
@@ -743,9 +741,9 @@ private void stopHeartBeatIfRunning() {
743741
heartbeatLock.lock();
744742
try {
745743
// only stop if it was running
746-
if (heartBeatTimer != null) {
747-
heartBeatTimer.cancel();
748-
heartBeatTimer = null;
744+
if (heartbeat != null) {
745+
heartbeat.stop();
746+
heartbeat = null;
749747
}
750748
} finally {
751749
heartbeatLock.unlock();

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

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package io.ebean.datasource.pool;
22

3+
import io.ebean.datasource.pool.ConnectionPool.Heartbeat;
4+
5+
import java.util.Timer;
6+
import java.util.TimerTask;
37
import java.util.concurrent.ExecutorService;
48
import java.util.concurrent.Executors;
59

@@ -8,4 +12,41 @@ final class ExecutorFactory {
812
static ExecutorService newExecutor() {
913
return Executors.newSingleThreadExecutor();
1014
}
15+
16+
/**
17+
* Return a new Heartbeat for the pool.
18+
*/
19+
static Heartbeat newHeartBeat(ConnectionPool pool, int freqMillis) {
20+
final Timer timer = new Timer(pool.name() + ".heartbeat", true);
21+
timer.scheduleAtFixedRate(new HeartbeatTask(pool), freqMillis, freqMillis);
22+
return new TimerHeartbeat(timer);
23+
}
24+
25+
private static final class TimerHeartbeat implements Heartbeat {
26+
27+
private final Timer timer;
28+
29+
private TimerHeartbeat(Timer timer) {
30+
this.timer = timer;
31+
}
32+
33+
@Override
34+
public void stop() {
35+
timer.cancel();
36+
}
37+
}
38+
39+
private static final class HeartbeatTask extends TimerTask {
40+
41+
private final ConnectionPool pool;
42+
43+
private HeartbeatTask(ConnectionPool pool) {
44+
this.pool = pool;
45+
}
46+
47+
@Override
48+
public void run() {
49+
pool.heartbeat();
50+
}
51+
}
1152
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,61 @@
11
package io.ebean.datasource.pool;
22

3+
import io.ebean.datasource.pool.ConnectionPool.Heartbeat;
4+
35
import java.util.concurrent.ExecutorService;
46
import java.util.concurrent.Executors;
7+
import java.util.concurrent.atomic.AtomicBoolean;
58

69
final class ExecutorFactory {
710

811
static ExecutorService newExecutor() {
912
return Executors.newVirtualThreadPerTaskExecutor();
1013
}
14+
15+
static Heartbeat newHeartBeat(ConnectionPool pool, int freqMillis) {
16+
return new VTHeartbeat(pool, freqMillis).start();
17+
}
18+
19+
private static final class VTHeartbeat implements Heartbeat {
20+
21+
private final AtomicBoolean running = new AtomicBoolean(false);
22+
private final ConnectionPool pool;
23+
private final int freqMillis;
24+
private final Thread thread;
25+
26+
private VTHeartbeat(ConnectionPool pool, int freqMillis) {
27+
this.pool = pool;
28+
this.freqMillis = freqMillis;
29+
this.thread = Thread.ofVirtual()
30+
.name(pool.name() + ".heartbeat")
31+
.unstarted(this::run);
32+
}
33+
34+
private void run() {
35+
while (running.get()) {
36+
try {
37+
Thread.sleep(freqMillis);
38+
pool.heartbeat();
39+
} catch (InterruptedException e) {
40+
Thread.currentThread().interrupt();
41+
break;
42+
} catch (Exception e) {
43+
// continue heartbeat
44+
Log.warn("Error during heartbeat", e);
45+
}
46+
}
47+
}
48+
49+
private Heartbeat start() {
50+
running.set(true);
51+
thread.start();
52+
return this;
53+
}
54+
55+
@Override
56+
public void stop() {
57+
running.set(false);
58+
thread.interrupt();
59+
}
60+
}
1161
}

0 commit comments

Comments
 (0)