Skip to content

Commit 2833b4f

Browse files
committed
Add validateOnHeartbeat with the ability to turn this off (typically for use with Lambda)
With Lambda we might want to turn off validateOnHeartbeat such that the heartbeat only trims the connection pool and does not perform a background test on a connection.
1 parent a710c89 commit 2833b4f

File tree

7 files changed

+88
-7
lines changed

7 files changed

+88
-7
lines changed

ebean-datasource-api/src/main/java/io/ebean/datasource/DataSourceBuilder.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,15 @@ default DataSourceBuilder initDatabaseForPlatform(String platform) {
696696
*/
697697
DataSourceBuilder shutdownOnJvmExit(boolean shutdownOnJvmExit);
698698

699+
/**
700+
* Set whether the connection pool should be validated periodically.
701+
* <p>
702+
* This is enabled by default. Generally we only want to turn this
703+
* off when using the pool with a Lambda function.
704+
* @param validateOnHeartbeat Use false to disable heartbeat validation.
705+
*/
706+
DataSourceBuilder validateOnHeartbeat(boolean validateOnHeartbeat);
707+
699708
/**
700709
* EXPERIMENTAL feature - Set to true when using in Lambda to enable an extra
701710
* check to detect when the function has been restored from suspension.
@@ -753,6 +762,11 @@ interface Settings extends DataSourceBuilder {
753762
*/
754763
boolean isShutdownOnJvmExit();
755764

765+
/**
766+
* When true validate the pool when the heartbeat runs.
767+
*/
768+
boolean isValidateOnHeartbeat();
769+
756770
/**
757771
* Return the connection properties including credentials and custom parameters.
758772
*/

ebean-datasource-api/src/main/java/io/ebean/datasource/DataSourceConfig.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ public class DataSourceConfig implements DataSourceBuilder.Settings {
8181
private String applicationName;
8282
private boolean shutdownOnJvmExit;
8383
private boolean useLambdaCheck;
84+
private boolean validateOnHeartbeat = true;
8485

8586
@Override
8687
public Settings settings() {
@@ -146,7 +147,7 @@ public DataSourceConfig copy() {
146147

147148
@Override
148149
public DataSourceConfig setDefaults(DataSourceBuilder builder) {
149-
DataSourceBuilder.Settings other = builder.settings();
150+
Settings other = builder.settings();
150151
if (driver == null) {
151152
driver = other.driver();
152153
}
@@ -704,6 +705,17 @@ public DataSourceConfig shutdownOnJvmExit(boolean shutdownOnJvmExit) {
704705
return this;
705706
}
706707

708+
@Override
709+
public boolean isValidateOnHeartbeat() {
710+
return validateOnHeartbeat;
711+
}
712+
713+
@Override
714+
public DataSourceConfig validateOnHeartbeat(boolean validateOnHeartbeat) {
715+
this.validateOnHeartbeat = validateOnHeartbeat;
716+
return this;
717+
}
718+
707719
@Override
708720
public DataSourceBuilder useLambdaCheck(boolean useLambda) {
709721
this.useLambdaCheck = useLambda;
@@ -768,6 +780,7 @@ private void loadSettings(ConfigPropertiesHelper properties) {
768780
poolListener = properties.get("poolListener", poolListener);
769781
offline = properties.getBoolean("offline", offline);
770782
shutdownOnJvmExit = properties.getBoolean("shutdownOnJvmExit", shutdownOnJvmExit);
783+
validateOnHeartbeat = properties.getBoolean("validateOnHeartbeat", validateOnHeartbeat);
771784
useLambdaCheck = properties.getBoolean("useLambdaCheck", useLambdaCheck);
772785

773786
String isoLevel = properties.get("isolationLevel", _isolationLevel(isolationLevel));

ebean-datasource-api/src/test/java/io/ebean/datasource/DataSourceConfigTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ public void loadSettings() throws IOException {
170170
var config = new DataSourceConfig().loadSettings(props, "foo");
171171
assertConfigValues(config);
172172
assertThat(config.isShutdownOnJvmExit()).isTrue();
173+
assertThat(config.isValidateOnHeartbeat()).isTrue();
173174
assertThat(config.useLambdaCheck()).isTrue();
174175
}
175176

@@ -182,6 +183,7 @@ public void load_prefix() throws IOException {
182183
assertConfigValues(config);
183184
assertThat(config.isShutdownOnJvmExit()).isFalse();
184185
assertThat(config.useLambdaCheck()).isFalse();
186+
assertThat(config.useLambdaCheck()).isFalse();
185187
}
186188

187189
@Test

ebean-datasource-api/src/test/resources/example.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ datasource.foo.applicationName=myApp
77
datasource.foo.clientInfo=ClientUser=ciu;ClientHostname=cih
88
datasource.foo.shutdownOnJvmExit=true
99
datasource.foo.useLambdaCheck=true
10+
datasource.foo.validateOnHeartbeat=true

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ final class ConnectionPool implements DataSourcePool {
4242
private final List<String> initSql;
4343
private final String user;
4444
private final String schema;
45-
private final String heartbeatsql;
45+
private final String heartbeatSql;
4646
private final int heartbeatFreqSecs;
4747
private final int heartbeatTimeoutSeconds;
4848
private final long trimPoolFreqMillis;
@@ -62,6 +62,7 @@ final class ConnectionPool implements DataSourcePool {
6262
private final String applicationName;
6363
private final DataSource source;
6464
private final int lambdaTrimMillis;
65+
private final boolean validateOnHeartbeat;
6566
private long nextTrimTime;
6667
private long nextLambdaTrimTime;
6768

@@ -111,10 +112,11 @@ final class ConnectionPool implements DataSourcePool {
111112
this.pstmtCacheSize = params.getPstmtCacheSize();
112113
this.minConnections = params.getMinConnections();
113114
this.maxConnections = params.getMaxConnections();
114-
this.heartbeatsql = params.getHeartbeatSql();
115115
this.waitTimeoutMillis = params.getWaitTimeoutMillis();
116116
this.heartbeatFreqSecs = params.getHeartbeatFreqSecs();
117117
this.heartbeatTimeoutSeconds = params.getHeartbeatTimeoutSeconds();
118+
this.heartbeatSql = params.getHeartbeatSql();
119+
this.validateOnHeartbeat = params.isValidateOnHeartbeat();
118120
this.trimPoolFreqMillis = 1000L * params.getTrimPoolFreqSecs();
119121
this.applicationName = params.getApplicationName();
120122
this.clientInfo = params.getClientInfo();
@@ -374,7 +376,9 @@ private void trimIdleConnections() {
374376
*/
375377
private void heartBeat() {
376378
trimIdleConnections();
377-
testConnection();
379+
if (validateOnHeartbeat) {
380+
testConnection();
381+
}
378382
}
379383

380384
private void testConnection() {
@@ -501,15 +505,15 @@ long maxAgeMillis() {
501505
}
502506

503507
private boolean testConnection(Connection conn) throws SQLException {
504-
if (heartbeatsql == null) {
508+
if (heartbeatSql == null) {
505509
return conn.isValid(heartbeatTimeoutSeconds);
506510
}
507511
// It should only error IF the DataSource is down or a network issue
508512
try (Statement stmt = conn.createStatement()) {
509513
if (heartbeatTimeoutSeconds > 0) {
510514
stmt.setQueryTimeout(heartbeatTimeoutSeconds);
511515
}
512-
stmt.execute(heartbeatsql);
516+
stmt.execute(heartbeatSql);
513517
return true;
514518
} finally {
515519
if (!conn.getAutoCommit()) {

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,6 @@ void returnPooledConnection(PooledConnection c, boolean forceClose) {
174174
}
175175
if (forceClose || c.shouldTrimOnReturn(lastResetTime, maxAgeMillis)) {
176176
c.closeConnectionFully(false);
177-
178177
} else {
179178
freeList.add(c);
180179
notEmpty.signal();

ebean-datasource/src/test/java/io/ebean/datasource/test/FactoryTest.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,54 @@ void readOnly() throws Exception {
5353
pool.shutdown();
5454
}
5555

56+
// @Disabled
57+
@Test
58+
void heartbeatTrimOnly() throws Exception {
59+
DataSourcePool pool = DataSourcePool.builder()
60+
.name("heartbeatTrimOnly")
61+
.url("jdbc:h2:mem:heartbeatTrimOnly")
62+
.username("sa")
63+
.password("")
64+
.readOnly(true)
65+
.autoCommit(true)
66+
.validateOnHeartbeat(false)
67+
.heartbeatFreqSecs(1)
68+
.trimPoolFreqSecs(1)
69+
.maxInactiveTimeSecs(3)
70+
.build();
71+
72+
try (Connection connection = pool.getConnection()) {
73+
try (PreparedStatement stmt = connection.prepareStatement("create table junk4 (acol varchar(10))")) {
74+
stmt.execute();
75+
connection.commit();
76+
}
77+
}
78+
List<Connection> connections = new ArrayList<>();
79+
for (int i = 0; i < 10; i++) {
80+
connections.add(pool.getConnection());
81+
}
82+
for (Connection connection : connections) {
83+
connection.close();
84+
}
85+
Thread.sleep(1000);
86+
List<Connection> connections2 = new ArrayList<>();
87+
for (int i = 0; i < 4; i++) {
88+
Connection c = pool.getConnection();
89+
connections2.add(c);
90+
}
91+
for (Connection connection : connections2) {
92+
try (PreparedStatement ps = connection.prepareStatement("select * from junk4")) {
93+
ps.execute();
94+
}
95+
connection.close();
96+
}
97+
for (int i = 0; i < 5; i++) {
98+
Thread.sleep(1000);
99+
System.out.println(".");
100+
}
101+
pool.shutdown();
102+
}
103+
56104
@Disabled
57105
@Test
58106
void readOnly2() throws Exception {

0 commit comments

Comments
 (0)