Skip to content

Commit 40d1a58

Browse files
tanner0101Mordil
authored andcommitted
Add optional promise parameter to RedisConnectionPool.close
Motivation: The actual process for closing a connection pool involves closing each individual connection, which is all asynchronous. There is currently no way to work off of the event when all of the connections have completed their close process. Modifications: - Add: `poolHasActiveConnections` error value to `RedisConnectionPoolError` - Add: `promise` parameter to the `RedisConnectionPool.close` method - Add: Documentation comments for the close method Result: Developers should now have a way of chaining callbacks when all connections in a pool have been closed.
1 parent ff25332 commit 40d1a58

File tree

3 files changed

+20
-7
lines changed

3 files changed

+20
-7
lines changed

Sources/RediStack/Connection Pool/ConnectionPool.swift

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -145,12 +145,12 @@ internal final class ConnectionPool {
145145

146146
/// Deactivates this connection pool. Once this is called, no further connections can be obtained
147147
/// from the pool. Leased connections are not deactivated and can continue to be used.
148-
func close() {
148+
func close(promise: EventLoopPromise<Void>? = nil) {
149149
if self.loop.inEventLoop {
150-
self.closePool()
150+
self.closePool(promise: promise)
151151
} else {
152152
self.loop.execute {
153-
self.closePool()
153+
self.closePool(promise: promise)
154154
}
155155
}
156156
}
@@ -308,12 +308,13 @@ extension ConnectionPool {
308308
waiter.succeed(connection)
309309
}
310310

311-
private func closePool() {
311+
private func closePool(promise: EventLoopPromise<Void>?) {
312312
self.loop.preconditionInEventLoop()
313313

314314
// Pool closure must be monotonic.
315315
guard case .active = self.state else {
316316
self.logger.warning("Duplicate attempt to close connection pool")
317+
promise?.succeed(())
317318
return
318319
}
319320

@@ -322,7 +323,7 @@ extension ConnectionPool {
322323
// To close the pool we need to drop all active connections.
323324
let connections = self.availableConnections
324325
self.availableConnections = []
325-
connections.forEach { _ = $0.close() }
326+
let closeFutures = connections.map { $0.close() }
326327

327328
// We also cancel all pending leases.
328329
while let pendingLease = self.connectionWaiters.popFirst() {
@@ -331,12 +332,15 @@ extension ConnectionPool {
331332

332333
guard self.activeConnectionCount == 0 else {
333334
self.logger.trace("Waiting for connections to be returned to the pool, not closing.", metadata: ["active-connections": "\(self.activeConnectionCount)"])
335+
promise?.fail(RedisConnectionPoolError.poolHasActiveConnections)
334336
return
335337
}
336338

337339
// That was all the connections, so this is now closed.
338340
self.logger.trace("Pool closed")
339341
self.state = .closed
342+
EventLoopFuture<Void>.andAllSucceed(closeFutures, on: self.loop)
343+
.cascade(to: promise)
340344
}
341345

342346
/// This is the on-thread implementation for leasing connections out to users. Here we work out how to get a new

Sources/RediStack/Connection Pool/ConnectionPoolErrors.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public struct RedisConnectionPoolError: LocalizedError, Equatable {
2626
case poolClosed
2727
case timedOutWaitingForConnection
2828
case noAvailableConnectionTargets
29+
case poolHasActiveConnections
2930
}
3031

3132
/// The connection pool has already been closed, but the user has attempted to perform another operation on it.
@@ -36,4 +37,7 @@ public struct RedisConnectionPoolError: LocalizedError, Equatable {
3637

3738
/// The pool has been configured without available connection targets, so there is nowhere to connect to.
3839
public static let noAvailableConnectionTargets = RedisConnectionPoolError(baseError: .noAvailableConnectionTargets)
40+
41+
/// Waiting for connections to be returned to the pool, not closing.
42+
public static let poolHasActiveConnections = RedisConnectionPoolError(baseError: .poolHasActiveConnections)
3943
}

Sources/RediStack/RedisConnectionPool.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,14 @@ extension RedisConnectionPool {
120120
}
121121
}
122122

123-
public func close() {
123+
/// Closes all connections in the pool and deactivates the pool from creating new connections.
124+
///
125+
/// This method is safe to call multiple times.
126+
/// - Important: If the pool has connections in active use, the close process will not complete.
127+
/// - Parameter promise: A notification promise to resolve once the close process has completed.
128+
public func close(promise: EventLoopPromise<Void>? = nil) {
124129
self.loop.execute {
125-
self.pool?.close()
130+
self.pool?.close(promise: promise)
126131

127132
// This breaks the cycle between us and the pool.
128133
self.pool = nil

0 commit comments

Comments
 (0)