Skip to content

Commit ff20c78

Browse files
committed
Change to use internal Timer for heartBeat
1 parent 60a58ea commit ff20c78

5 files changed

Lines changed: 175 additions & 150 deletions

File tree

src/main/java/org/avaje/datasource/pool/ConnectionPool.java

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import java.util.Map.Entry;
2121
import java.util.Properties;
2222
import java.util.Set;
23+
import java.util.Timer;
24+
import java.util.TimerTask;
2325

2426
/**
2527
* A robust DataSource implementation.
@@ -159,17 +161,17 @@ public class ConnectionPool implements DataSourcePool {
159161

160162
private final PooledConnectionQueue queue;
161163

164+
private final Timer heartBeatTimer;
165+
162166
/**
163167
* Used to find and close() leaked connections. Leaked connections are
164168
* thought to be busy but have not been used for some time. Each time a
165169
* connection is used it sets it's lastUsedTime.
166170
*/
167171
private long leakTimeMinutes;
168172

169-
private final Runnable heartbeatRunnable = new HeartBeatRunnable();
170-
171-
public ConnectionPool(String name, DataSourceConfig params, DataSourceAlert notify) {
172-
this(name, params, notify, null);
173+
public ConnectionPool(String name, DataSourceConfig params) {
174+
this(name, params, null, null);
173175
}
174176

175177
public ConnectionPool(String name, DataSourceConfig params, DataSourceAlert notify, DataSourcePoolListener listener) {
@@ -222,12 +224,17 @@ public ConnectionPool(String name, DataSourceConfig params, DataSourceAlert noti
222224

223225
try {
224226
initialise();
227+
int freqMillis = heartbeatFreqSecs * 1000;
228+
heartBeatTimer = new Timer(name+".heartBeat");
229+
if (freqMillis > 0) {
230+
heartBeatTimer.scheduleAtFixedRate(new HeartBeatRunnable(), freqMillis, freqMillis);
231+
}
225232
} catch (SQLException ex) {
226233
throw new RuntimeException(ex);
227234
}
228235
}
229236

230-
class HeartBeatRunnable implements Runnable {
237+
class HeartBeatRunnable extends TimerTask {
231238
@Override
232239
public void run() {
233240
checkDataSource();
@@ -356,24 +363,6 @@ private void notifyDataSourceIsUp() {
356363
}
357364
}
358365

359-
360-
/**
361-
* Return the heartbeat frequency in seconds.
362-
* <p>
363-
* This is the frequency that the heartbeat runnable should be run.
364-
* </p>
365-
*/
366-
public int getHeartbeatFreqSecs() {
367-
return heartbeatFreqSecs;
368-
}
369-
370-
/**
371-
* Returns the Runnable used to check the dataSource using a heartbeat query.
372-
*/
373-
public Runnable getHeartbeatRunnable() {
374-
return heartbeatRunnable;
375-
}
376-
377366
/**
378367
* Trim connections (in the free list) based on idle time and maximum age.
379368
*/
@@ -760,6 +749,7 @@ public void testAlert() {
760749
*/
761750
@Override
762751
public void shutdown(boolean deregisterDriver) {
752+
heartBeatTimer.cancel();
763753
queue.shutdown();
764754
if (deregisterDriver) {
765755
deregisterDriver();
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package org.avaje.datasource.pool;
2+
3+
import org.avaje.datasource.DataSourceConfig;
4+
import org.slf4j.Logger;
5+
import org.slf4j.LoggerFactory;
6+
import org.testng.annotations.AfterClass;
7+
import org.testng.annotations.Test;
8+
9+
import java.sql.Connection;
10+
import java.sql.SQLException;
11+
import java.util.Random;
12+
13+
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
14+
15+
public class ConnectionPoolSpeedTest {
16+
17+
private Logger logger = LoggerFactory.getLogger(ConnectionPoolSpeedTest.class);
18+
19+
private ConnectionPool pool;
20+
21+
private Random random = new Random();
22+
23+
private int total;
24+
25+
ConnectionPoolSpeedTest() {
26+
pool = createPool();
27+
}
28+
29+
private ConnectionPool createPool() {
30+
31+
DataSourceConfig config = new DataSourceConfig();
32+
config.setDriver("org.h2.Driver");
33+
config.setUrl("jdbc:h2:mem:tests");
34+
config.setUsername("sa");
35+
config.setPassword("");
36+
config.setMinConnections(2);
37+
config.setMaxConnections(100);
38+
39+
return new ConnectionPool("testspeed", config);
40+
}
41+
42+
@AfterClass
43+
public void after() {
44+
pool.shutdown(false);
45+
}
46+
47+
@Test
48+
public void test() throws SQLException, InterruptedException {
49+
50+
warm();
51+
52+
total = 0;
53+
54+
long startNano = System.nanoTime();
55+
56+
perform();
57+
58+
long exeNano = System.nanoTime() - startNano;
59+
long avgNanos = exeNano / total;
60+
61+
logger.info("avgNanos[{}] total[{}]", avgNanos, total);
62+
63+
assertThat(avgNanos).isLessThan(300);
64+
}
65+
66+
private void perform() throws SQLException {
67+
for (int i = 0; i < 10000000; i++) {
68+
getAndCloseSome();
69+
}
70+
}
71+
72+
private void warm() throws SQLException {
73+
for (int i = 0; i < 50; i++) {
74+
getAndCloseSome();
75+
}
76+
}
77+
78+
private void getAndCloseSome() throws SQLException {
79+
80+
int num = random.nextInt(5);
81+
Connection[] bunch = new Connection[num];
82+
for (int i = 0; i < num; i++) {
83+
bunch[i] = pool.getConnection();
84+
}
85+
86+
for (Connection connection : bunch) {
87+
connection.close();
88+
}
89+
90+
total += num;
91+
}
92+
93+
}

src/test/java/org/avaje/datasource/pool/ConnectionPoolTest.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import org.avaje.datasource.DataSourceConfig;
44
import org.avaje.datasource.PoolStatistics;
5+
import org.testng.annotations.AfterClass;
56
import org.testng.annotations.Test;
67

78
import java.sql.Connection;
@@ -27,7 +28,12 @@ private ConnectionPool createPool() {
2728
config.setMinConnections(2);
2829
config.setMaxConnections(4);
2930

30-
return new ConnectionPool("test", config, null);
31+
return new ConnectionPool("test", config);
32+
}
33+
34+
@AfterClass
35+
public void after() {
36+
pool.shutdown(false);
3137
}
3238

3339
@Test
@@ -56,7 +62,7 @@ public void getConnection_expect_poolGrowsAboveMin() throws SQLException {
5662
assertThat(pool.getStatus(false).getFree()).isEqualTo(3);
5763
}
5864

59-
@Test
65+
@Test(dependsOnMethods = "getConnection_expect_poolGrowsAboveMin")
6066
public void getConnection_getStatistics() throws SQLException, InterruptedException {
6167

6268
pool.getStatistics(true);
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package org.avaje.datasource.pool;
2+
3+
import org.avaje.datasource.DataSourceConfig;
4+
import org.testng.annotations.AfterClass;
5+
import org.testng.annotations.Test;
6+
7+
import java.sql.Connection;
8+
import java.sql.SQLException;
9+
10+
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
11+
12+
public class ConnectionPoolTrimIdleTest {
13+
14+
private ConnectionPool pool;
15+
16+
ConnectionPoolTrimIdleTest() {
17+
pool = createPool();
18+
}
19+
20+
private ConnectionPool createPool() {
21+
22+
DataSourceConfig config = new DataSourceConfig();
23+
config.setDriver("org.h2.Driver");
24+
config.setUrl("jdbc:h2:mem:tests");
25+
config.setUsername("sa");
26+
config.setPassword("");
27+
config.setMinConnections(2);
28+
config.setMaxConnections(10);
29+
config.setMaxInactiveTimeSecs(1);
30+
config.setTrimPoolFreqSecs(1);
31+
config.setHeartbeatFreqSecs(1);
32+
33+
return new ConnectionPool("testidle", config);
34+
}
35+
36+
@AfterClass
37+
public void after() {
38+
pool.shutdown(false);
39+
}
40+
41+
@Test
42+
public void test() throws SQLException, InterruptedException {
43+
44+
Connection con1 = pool.getConnection();
45+
Connection con2 = pool.getConnection();
46+
Connection con3 = pool.getConnection();
47+
Connection con4 = pool.getConnection();
48+
49+
con1.close();
50+
con2.close();
51+
con3.close();
52+
con4.close();
53+
54+
assertThat(pool.getStatus(false).getFree()).isEqualTo(4);
55+
56+
Thread.sleep(5000);
57+
58+
assertThat(pool.getStatus(false).getFree()).isEqualTo(2);
59+
60+
}
61+
}

src/test/java/org/avaje/datasource/pool/TestDataSourceMax.java

Lines changed: 0 additions & 125 deletions
This file was deleted.

0 commit comments

Comments
 (0)