Skip to content

Commit c4fa17c

Browse files
committed
Support connection maximum lifetime to recycle connections regularly
Adds `maxLifetime` to `PoolOptions` and alters `PooledConnection` to honor the maximum lifetime as well as the idle timeout.
1 parent 9830530 commit c4fa17c

File tree

4 files changed

+102
-14
lines changed

4 files changed

+102
-14
lines changed

vertx-sql-client/src/main/generated/io/vertx/sqlclient/PoolOptionsConverter.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,16 @@ public static void fromJson(Iterable<java.util.Map.Entry<String, Object>> json,
4545
obj.setIdleTimeoutUnit(java.util.concurrent.TimeUnit.valueOf((String)member.getValue()));
4646
}
4747
break;
48+
case "maxLifetime":
49+
if (member.getValue() instanceof Number) {
50+
obj.setMaxLifetime(((Number)member.getValue()).intValue());
51+
}
52+
break;
53+
case "maxLifetimeUnit":
54+
if (member.getValue() instanceof String) {
55+
obj.setMaxLifetimeUnit(java.util.concurrent.TimeUnit.valueOf((String)member.getValue()));
56+
}
57+
break;
4858
case "maxSize":
4959
if (member.getValue() instanceof Number) {
5060
obj.setMaxSize(((Number)member.getValue()).intValue());
@@ -88,6 +98,10 @@ public static void toJson(PoolOptions obj, java.util.Map<String, Object> json) {
8898
if (obj.getIdleTimeoutUnit() != null) {
8999
json.put("idleTimeoutUnit", obj.getIdleTimeoutUnit().name());
90100
}
101+
json.put("maxLifetime", obj.getMaxLifetime());
102+
if (obj.getMaxLifetimeUnit() != null) {
103+
json.put("maxLifetimeUnit", obj.getMaxLifetimeUnit().name());
104+
}
91105
json.put("maxSize", obj.getMaxSize());
92106
json.put("maxWaitQueueSize", obj.getMaxWaitQueueSize());
93107
if (obj.getName() != null) {

vertx-sql-client/src/main/java/io/vertx/sqlclient/PoolOptions.java

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
package io.vertx.sqlclient;
1919

2020
import io.vertx.codegen.annotations.DataObject;
21-
import io.vertx.core.http.HttpClientOptions;
2221
import io.vertx.core.impl.Arguments;
2322
import io.vertx.core.json.JsonObject;
2423

@@ -48,11 +47,21 @@ public class PoolOptions {
4847
*/
4948
public static final int DEFAULT_IDLE_TIMEOUT = 0;
5049

50+
/**
51+
* Default maximum connection lifetime in the pool = 0 (no lifetime)
52+
*/
53+
public static final int DEFAULT_MAXIMUM_LIFETIME = 0;
54+
5155
/**
5256
* Default connection idle time unit in the pool = seconds
5357
*/
5458
public static final TimeUnit DEFAULT_IDLE_TIMEOUT_TIME_UNIT = TimeUnit.SECONDS;
5559

60+
/**
61+
* Default maximum connection lifetime time unit in the pool = seconds
62+
*/
63+
public static final TimeUnit DEFAULT_MAXIMUM_LIFETIME_TIME_UNIT = TimeUnit.SECONDS;
64+
5665
/**
5766
* Default pool cleaner period = 1000 ms (1 second)
5867
*/
@@ -87,6 +96,8 @@ public class PoolOptions {
8796
private int maxWaitQueueSize = DEFAULT_MAX_WAIT_QUEUE_SIZE;
8897
private int idleTimeout = DEFAULT_IDLE_TIMEOUT;
8998
private TimeUnit idleTimeoutUnit = DEFAULT_IDLE_TIMEOUT_TIME_UNIT;
99+
private int maxLifetime = DEFAULT_MAXIMUM_LIFETIME;
100+
private TimeUnit maxLifetimeUnit = DEFAULT_MAXIMUM_LIFETIME_TIME_UNIT;
90101
private int poolCleanerPeriod = DEFAULT_POOL_CLEANER_PERIOD;
91102
private int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;
92103
private TimeUnit connectionTimeoutUnit = DEFAULT_CONNECTION_TIMEOUT_TIME_UNIT;
@@ -179,14 +190,50 @@ public int getIdleTimeout() {
179190
/**
180191
* Establish an idle timeout for pooled connections.
181192
*
182-
* @param idleTimeout the pool connection idle time unitq
193+
* @param idleTimeout the pool connection idle timeout
183194
* @return a reference to this, so the API can be used fluently
184195
*/
185196
public PoolOptions setIdleTimeout(int idleTimeout) {
186197
this.idleTimeout = idleTimeout;
187198
return this;
188199
}
189200

201+
/**
202+
* @return the pooled connection max lifetime unit
203+
*/
204+
public TimeUnit getMaxLifetimeUnit() {
205+
return maxLifetimeUnit;
206+
}
207+
208+
/**
209+
* Establish a max lifetime unit for pooled connections.
210+
*
211+
* @param maxLifetimeUnit pooled connection max lifetime unit
212+
* @return a reference to this, so the API can be used fluently
213+
*/
214+
public PoolOptions setMaxLifetimeUnit(TimeUnit maxLifetimeUnit) {
215+
this.maxLifetimeUnit = maxLifetimeUnit;
216+
return this;
217+
}
218+
219+
/**
220+
* @return pooled connection max lifetime
221+
*/
222+
public int getMaxLifetime() {
223+
return maxLifetime;
224+
}
225+
226+
/**
227+
* Establish a max lifetime for pooled connections.
228+
*
229+
* @param maxLifetime the pool connection max lifetime
230+
* @return a reference to this, so the API can be used fluently
231+
*/
232+
public PoolOptions setMaxLifetime(int maxLifetime) {
233+
this.maxLifetime = maxLifetime;
234+
return this;
235+
}
236+
190237
/**
191238
* @return the connection pool cleaner period in ms.
192239
*/

vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/PoolImpl.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public class PoolImpl extends SqlClientBase implements Pool, Closeable {
4545
private final CloseFuture closeFuture;
4646
private final long idleTimeout;
4747
private final long connectionTimeout;
48+
private final long maxLifetime;
4849
private final long cleanerPeriod;
4950
private final int pipeliningLimit;
5051
private volatile Handler<SqlConnectionPool.PooledConnection> connectionInitializer;
@@ -66,20 +67,23 @@ public PoolImpl(VertxInternal vertx,
6667

6768
this.idleTimeout = MILLISECONDS.convert(poolOptions.getIdleTimeout(), poolOptions.getIdleTimeoutUnit());
6869
this.connectionTimeout = MILLISECONDS.convert(poolOptions.getConnectionTimeout(), poolOptions.getConnectionTimeoutUnit());
70+
this.maxLifetime = MILLISECONDS.convert(poolOptions.getMaxLifetime(), poolOptions.getMaxLifetimeUnit());
6971
this.cleanerPeriod = poolOptions.getPoolCleanerPeriod();
7072
this.timerID = -1L;
7173
this.pipeliningLimit = pipeliningLimit;
7274
this.vertx = vertx;
73-
this.pool = new SqlConnectionPool(ctx -> connectionProvider.apply(ctx), () -> connectionInitializer, afterAcquire, beforeRecycle, vertx, idleTimeout, poolOptions.getMaxSize(), pipeliningLimit, poolOptions.getMaxWaitQueueSize(), poolOptions.getEventLoopSize());
75+
this.pool = new SqlConnectionPool(ctx -> connectionProvider.apply(ctx), () -> connectionInitializer,
76+
afterAcquire, beforeRecycle, vertx, idleTimeout, maxLifetime, poolOptions.getMaxSize(), pipeliningLimit,
77+
poolOptions.getMaxWaitQueueSize(), poolOptions.getEventLoopSize());
7478
this.closeFuture = closeFuture;
7579
}
7680

7781
public Pool init() {
7882
closeFuture.add(this);
79-
if (idleTimeout > 0 && cleanerPeriod > 0) {
83+
if ((idleTimeout > 0 || maxLifetime > 0) && cleanerPeriod > 0) {
8084
synchronized (this) {
8185
timerID = vertx.setTimer(cleanerPeriod, id -> {
82-
checkExpired();
86+
runEviction();
8387
});
8488
}
8589
}
@@ -94,17 +98,17 @@ public Pool connectionProvider(Function<Context, Future<SqlConnection>> connecti
9498
return this;
9599
}
96100

97-
private void checkExpired() {
101+
private void runEviction() {
98102
synchronized (this) {
99103
if (timerID == -1) {
100104
// Cancelled
101105
return;
102106
}
103107
timerID = vertx.setTimer(cleanerPeriod, id -> {
104-
checkExpired();
108+
runEviction();
105109
});
106110
}
107-
pool.checkExpired();
111+
pool.evict();
108112
}
109113

110114
@Override

vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/pool/SqlConnectionPool.java

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public class SqlConnectionPool {
4848
private final Function<Connection, Future<Void>> beforeRecycle;
4949
private final int pipeliningLimit;
5050
private final long idleTimeout;
51+
private final long maxLifetime;
5152
private final int maxSize;
5253

5354
public SqlConnectionPool(Function<Context, Future<SqlConnection>> connectionProvider,
@@ -56,6 +57,7 @@ public SqlConnectionPool(Function<Context, Future<SqlConnection>> connectionProv
5657
Function<Connection, Future<Void>> beforeRecycle,
5758
VertxInternal vertx,
5859
long idleTimeout,
60+
long maxLifetime,
5961
int maxSize,
6062
int pipeliningLimit,
6163
int maxWaitQueueSize,
@@ -72,7 +74,8 @@ public SqlConnectionPool(Function<Context, Future<SqlConnection>> connectionProv
7274
this.pool = ConnectionPool.pool(connector, new int[]{maxSize}, maxWaitQueueSize);
7375
this.vertx = vertx;
7476
this.pipeliningLimit = pipeliningLimit;
75-
this.idleTimeout = idleTimeout;
77+
this.idleTimeout = idleTimeout > 0 ? idleTimeout : Long.MAX_VALUE;
78+
this.maxLifetime = maxLifetime > 0 ? maxLifetime : Long.MAX_VALUE;
7679
this.maxSize = maxSize;
7780
this.hook = hook;
7881
this.connectionProvider = connectionProvider;
@@ -140,9 +143,9 @@ public int size() {
140143
return pool.size();
141144
}
142145

143-
public void checkExpired() {
146+
public void evict() {
144147
long now = System.currentTimeMillis();
145-
pool.evict(conn -> conn.expirationTimestamp < now, ar -> {
148+
pool.evict(conn -> conn.shouldEvict(now), ar -> {
146149
if (ar.succeeded()) {
147150
List<PooledConnection> res = ar.result();
148151
for (PooledConnection conn : res) {
@@ -166,7 +169,7 @@ public <R> Future<R> execute(ContextInternal context, CommandBase<R> cmd) {
166169
future = pooled.schedule(context, cmd);
167170
}
168171
return future.onComplete(v -> {
169-
pooled.expirationTimestamp = System.currentTimeMillis() + idleTimeout;
172+
pooled.refresh();
170173
lease.recycle();
171174
});
172175
});
@@ -261,12 +264,15 @@ public class PooledConnection implements Connection, Connection.Holder {
261264
private Holder holder;
262265
private Promise<ConnectResult<PooledConnection>> poolCallback;
263266
private Lease<PooledConnection> lease;
264-
public long expirationTimestamp;
267+
public long idleEvictionTimestamp;
268+
public long lifetimeEvictionTimestamp;
265269

266270
PooledConnection(ConnectionFactory factory, Connection conn, PoolConnector.Listener listener) {
267271
this.factory = factory;
268272
this.conn = conn;
269273
this.listener = listener;
274+
this.lifetimeEvictionTimestamp = System.currentTimeMillis() + maxLifetime;
275+
refresh();
270276
}
271277

272278
public ConnectionFactory factory() {
@@ -305,6 +311,10 @@ private void close(Promise<Void> promise) {
305311
conn.close(this, promise);
306312
}
307313

314+
public void refresh() {
315+
this.idleEvictionTimestamp = System.currentTimeMillis() + idleTimeout;
316+
}
317+
308318
@Override
309319
public void init(Holder holder) {
310320
if (this.holder != null) {
@@ -348,7 +358,7 @@ private void doClose(Holder holder, Promise<Void> promise) {
348358
private void cleanup(Promise<Void> promise) {
349359
Lease<PooledConnection> l = this.lease;
350360
this.lease = null;
351-
this.expirationTimestamp = System.currentTimeMillis() + idleTimeout;
361+
refresh();
352362
l.recycle();
353363
promise.complete();
354364
}
@@ -394,5 +404,18 @@ public int getSecretKey() {
394404
public Connection unwrap() {
395405
return conn;
396406
}
407+
408+
public boolean hasIdleExpired(long now) {
409+
return idleEvictionTimestamp < now;
410+
}
411+
412+
public boolean hasLifetimeExpired(long now) {
413+
return lifetimeEvictionTimestamp < now;
414+
}
415+
416+
public boolean shouldEvict(long now) {
417+
return hasIdleExpired(now) || hasLifetimeExpired(now);
418+
}
419+
397420
}
398421
}

0 commit comments

Comments
 (0)