Skip to content

Commit eb66e05

Browse files
authored
Merge pull request #136 from ebean-orm/feature/initialConnections
Add support for initialConnections
2 parents 2289099 + 209b298 commit eb66e05

File tree

8 files changed

+83
-15
lines changed

8 files changed

+83
-15
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ ebean-profiling*.xml
1313
/db
1414
/mydb.db
1515
!src/test/ddl-review/*.sql
16-
16+
.DS_Store
1717

1818
# Intellij project files
1919
*.iml

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,17 @@ default DataSourceBuilder minConnections(int minConnections) {
329329
@Deprecated(forRemoval = true)
330330
DataSourceBuilder setMinConnections(int minConnections);
331331

332+
/**
333+
* Set the number of initial connections to create when starting.
334+
* <p>
335+
* When not set the initial number of connections will be min connections.
336+
* <p>
337+
* The benefit of setting an initial number of connections is for smoother
338+
* deployment into an active production system where an application will get
339+
* assigned production load.
340+
*/
341+
DataSourceBuilder initialConnections(int initialConnections);
342+
332343
/**
333344
* Set the maximum number of connections the pool can reach. Defaults to 200 when not set.
334345
*/
@@ -901,6 +912,11 @@ default String driverClassName() {
901912
*/
902913
int getMaxConnections();
903914

915+
/**
916+
* Return the number of initial connections to create on startup.
917+
*/
918+
int getInitialConnections();
919+
904920
/**
905921
* Return the alert implementation to use.
906922
*/

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
import java.util.Properties;
1111
import java.util.function.Consumer;
1212

13+
import static java.lang.Math.max;
14+
import static java.lang.Math.min;
15+
1316
/**
1417
* Configuration information for a DataSource.
1518
*
@@ -56,6 +59,7 @@ public class DataSourceConfig implements DataSourceBuilder.Settings {
5659
*/
5760
private String ownerPassword;
5861
private int minConnections = UNSET; // defaults to 2
62+
private int initialConnections = UNSET; // defaults to 2
5963
private int maxConnections = UNSET; // defaults to 200
6064
private int isolationLevel = Connection.TRANSACTION_READ_COMMITTED;
6165
private boolean autoCommit;
@@ -116,6 +120,7 @@ public DataSourceConfig copy() {
116120
copy.driverClassName = driverClassName;
117121
copy.applicationName = applicationName;
118122
copy.minConnections = minConnections;
123+
copy.initialConnections = initialConnections;
119124
copy.maxConnections = maxConnections;
120125
copy.isolationLevel = isolationLevel;
121126
copy.autoCommit = autoCommit;
@@ -186,6 +191,9 @@ public DataSourceConfig setDefaults(DataSourceBuilder builder) {
186191
if (minConnections == UNSET) {
187192
minConnections = other.getMinConnections();
188193
}
194+
if (initialConnections == UNSET) {
195+
initialConnections = other.getInitialConnections();
196+
}
189197
if (maxConnections == UNSET) {
190198
maxConnections = other.getMaxConnections();
191199
}
@@ -423,6 +431,19 @@ public DataSourceConfig setMinConnections(int minConnections) {
423431
return this;
424432
}
425433

434+
@Override
435+
public int getInitialConnections() {
436+
int min = getMinConnections();
437+
int max = getMaxConnections();
438+
return initialConnections == UNSET ? min : min(max(min, initialConnections), max);
439+
}
440+
441+
@Override
442+
public DataSourceConfig initialConnections(int initialConnections) {
443+
this.initialConnections = initialConnections;
444+
return this;
445+
}
446+
426447
@Override
427448
public int getMaxConnections() {
428449
return maxConnections == UNSET ? 200 : maxConnections;
@@ -810,6 +831,7 @@ private void loadSettings(ConfigPropertiesHelper properties) {
810831
trimPoolFreqSecs = properties.getInt("trimPoolFreqSecs", trimPoolFreqSecs);
811832
maxAgeMinutes = properties.getInt("maxAgeMinutes", maxAgeMinutes);
812833
minConnections = properties.getInt("minConnections", minConnections);
834+
initialConnections = properties.getInt("initialConnections", initialConnections);
813835
maxConnections = properties.getInt("maxConnections", maxConnections);
814836
pstmtCacheSize = properties.getInt("pstmtCacheSize", pstmtCacheSize);
815837
cstmtCacheSize = properties.getInt("cstmtCacheSize", cstmtCacheSize);

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

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,19 @@ public void isEmpty() {
107107
assertThat(config.isEmpty()).isFalse();
108108
}
109109

110+
@Test
111+
void initial_expect_inRangeMinMax() {
112+
DataSourceConfig readOnly = new DataSourceConfig();
113+
readOnly.setMinConnections(10);
114+
readOnly.setMaxConnections(30);
115+
116+
readOnly.initialConnections(1);
117+
assertThat(readOnly.getInitialConnections()).isEqualTo(10);
118+
119+
readOnly.initialConnections(100);
120+
assertThat(readOnly.getInitialConnections()).isEqualTo(30);
121+
}
122+
110123
@Test
111124
public void copy() {
112125

@@ -134,6 +147,7 @@ public void copy() {
134147
assertEquals("sch", copy.getSchema());
135148
assertEquals("cat", copy.catalog());
136149
assertEquals(42, copy.getMinConnections());
150+
assertEquals(42, copy.getInitialConnections());
137151
assertEquals(45, copy.getMaxConnections());
138152

139153
customSource.put("a", "modifiedA");
@@ -148,6 +162,7 @@ public void copy() {
148162
public void defaults() {
149163

150164
DataSourceConfig config = create();
165+
config.initialConnections(6);
151166

152167
var readOnly = new DataSourceConfig().setDefaults(config);
153168

@@ -158,6 +173,7 @@ public void defaults() {
158173
assertThat(readOnly.getSchema()).isEqualTo(config.getSchema());
159174
assertThat(readOnly.catalog()).isEqualTo(config.catalog());
160175
assertThat(readOnly.getMinConnections()).isEqualTo(config.getMinConnections());
176+
assertThat(readOnly.getInitialConnections()).isEqualTo(config.getInitialConnections());
161177
assertThat(readOnly.getCustomProperties()).containsKeys("useSSL");
162178
}
163179

@@ -166,17 +182,20 @@ void setDefaults_expect_connectionsDefault() {
166182
DataSourceConfig readOnly = new DataSourceConfig();
167183
readOnly.setDefaults(create());
168184
assertThat(readOnly.getMinConnections()).isEqualTo(1);
185+
assertThat(readOnly.getInitialConnections()).isEqualTo(1);
169186
assertThat(readOnly.getMaxConnections()).isEqualTo(20);
170187
}
171188

172189
@Test
173190
void setDefaults_when_explicit() {
174191
DataSourceConfig readOnly = new DataSourceConfig();
175192
readOnly.setMinConnections(21);
176-
readOnly.setMaxConnections(22);
193+
readOnly.initialConnections(25);
194+
readOnly.setMaxConnections(32);
177195
readOnly.setDefaults(create());
178196
assertThat(readOnly.getMinConnections()).isEqualTo(21);
179-
assertThat(readOnly.getMaxConnections()).isEqualTo(22);
197+
assertThat(readOnly.getInitialConnections()).isEqualTo(25);
198+
assertThat(readOnly.getMaxConnections()).isEqualTo(32);
180199
}
181200

182201
@Test
@@ -189,6 +208,7 @@ void setDefaults_when_explicitSameAsNormalDefaults() {
189208
readOnly.setDefaults(create());
190209

191210
assertThat(readOnly.getMinConnections()).isEqualTo(2);
211+
assertThat(readOnly.getInitialConnections()).isEqualTo(2);
192212
assertThat(readOnly.getMaxConnections()).isEqualTo(200);
193213
}
194214

@@ -210,6 +230,7 @@ public void defaults_someOverride() {
210230
assertThat(readOnly.getUrl()).isEqualTo("jdbc:postgresql://127.0.0.2:5432/unit");
211231
assertThat(readOnly.getUsername()).isEqualTo("foo2");
212232
assertThat(readOnly.getMinConnections()).isEqualTo(3);
233+
assertThat(readOnly.getInitialConnections()).isEqualTo(3);
213234
assertThat(readOnly.getMaxConnections()).isEqualTo(20);
214235
assertThat(readOnly.isShutdownOnJvmExit()).isFalse();
215236
assertThat(readOnly.isValidateOnHeartbeat()).isFalse();
@@ -269,6 +290,7 @@ public void from_prefix() throws IOException {
269290

270291
var builder = DataSourceBuilder.from(props, "bar");
271292
assertConfigValues(builder.settings());
293+
assertThat(builder.settings().getInitialConnections()).isEqualTo(12);
272294
}
273295

274296
@Test
@@ -288,16 +310,19 @@ public void alsoIf() {
288310

289311
assertThat(builder.settings().getMaxConnections()).isEqualTo(100);
290312
assertThat(builder.settings().getMinConnections()).isEqualTo(3);
313+
assertThat(builder.settings().getInitialConnections()).isEqualTo(3);
291314
}
292315

293316
@Test
294317
public void alsoIf_notApplied() {
295318
var builder = DataSourceBuilder.create()
296319
.alsoIf(() -> false, this::myConfig)
297-
.minConnections(3);
320+
.minConnections(3)
321+
.initialConnections(6);
298322

299323
assertThat(builder.settings().getMaxConnections()).isEqualTo(200);
300324
assertThat(builder.settings().getMinConnections()).isEqualTo(3);
325+
assertThat(builder.settings().getInitialConnections()).isEqualTo(6);
301326
}
302327

303328
private void myConfig(DataSourceBuilder.Settings builder) {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ bar.url=myUrl
66
bar.readOnlyUrl=myReadOnlyUrl
77
bar.applicationName=myApp
88
bar.clientInfo=ClientUser=ciu;ClientHostname=cih
9+
bar.initialConnections=12

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ interface Heartbeat {
8282
private final AtomicBoolean dataSourceUp = new AtomicBoolean(false);
8383
private SQLException dataSourceDownReason;
8484
private final int minConnections;
85+
private final int initialConnections;
8586
private int maxConnections;
8687
private final int waitTimeoutMillis;
8788
private final int pstmtCacheSize;
@@ -121,6 +122,7 @@ interface Heartbeat {
121122
this.maxStackTraceSize = params.getMaxStackTraceSize();
122123
this.pstmtCacheSize = params.getPstmtCacheSize();
123124
this.minConnections = params.getMinConnections();
125+
this.initialConnections = params.getInitialConnections();
124126
this.maxConnections = params.getMaxConnections();
125127
this.waitTimeoutMillis = params.getWaitTimeoutMillis();
126128
this.heartbeatFreqSecs = params.getHeartbeatFreqSecs();
@@ -175,7 +177,7 @@ public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedE
175177
private void tryEnsureMinimumConnections() {
176178
notifyLock.lock();
177179
try {
178-
queue.ensureMinimumConnections();
180+
queue.createConnections(initialConnections);
179181
// if we successfully come up without an exception, send datasource up
180182
// notification. This makes it easier, because the application needs not
181183
// to implement special handling, if the db comes up the first time or not.
@@ -193,7 +195,7 @@ private void initialiseConnections() throws SQLException {
193195
long start = System.currentTimeMillis();
194196
dataSourceUp.set(true);
195197
if (failOnStart) {
196-
queue.ensureMinimumConnections();
198+
queue.createConnections(initialConnections);
197199
} else {
198200
tryEnsureMinimumConnections();
199201
}

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

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -131,16 +131,13 @@ private int totalConnections() {
131131
return freeList.size() + busyList.size();
132132
}
133133

134-
void ensureMinimumConnections() throws SQLException {
134+
void createConnections(int numberToAdd) throws SQLException {
135135
lock.lock();
136136
try {
137-
int add = minSize - totalConnections();
138-
if (add > 0) {
139-
for (int i = 0; i < add; i++) {
140-
freeList.add(pool.createConnectionForQueue(connectionId++));
141-
}
142-
notEmpty.signal();
137+
for (int i = 0; i < numberToAdd; i++) {
138+
freeList.add(pool.createConnectionForQueue(connectionId++));
143139
}
140+
notEmpty.signal();
144141
} finally {
145142
lock.unlock();
146143
}
@@ -360,7 +357,11 @@ void trim(long maxInactiveMillis, long maxAgeMillis) {
360357
try {
361358
if (trimInactiveConnections(maxInactiveMillis, maxAgeMillis)) {
362359
try {
363-
ensureMinimumConnections();
360+
// ensure there are the min connections
361+
int add = minSize - totalConnections();
362+
if (add > 0) {
363+
createConnections(add);
364+
}
364365
} catch (SQLException e) {
365366
Log.error("Error trying to ensure minimum connections", e);
366367
}

ebean-datasource/src/test/java/io/ebean/datasource/pool/ConnectionPoolTrimIdleTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ private ConnectionPool createPool() {
2020
config.setUsername("sa");
2121
config.setPassword("");
2222
config.setMinConnections(1);
23+
config.initialConnections(3);
2324
config.setMaxConnections(10);
2425
config.setMaxInactiveTimeSecs(1);
2526
config.setTrimPoolFreqSecs(1);
@@ -32,7 +33,7 @@ private ConnectionPool createPool() {
3233
public void test() throws SQLException, InterruptedException {
3334

3435
ConnectionPool pool = createPool();
35-
assertThat(pool.size()).isEqualTo(1);
36+
assertThat(pool.size()).isEqualTo(3);
3637
try {
3738
Connection con1 = pool.getConnection();
3839
Connection con2 = pool.getConnection();

0 commit comments

Comments
 (0)