@@ -170,12 +170,17 @@ public final class RedisConnection: RedisClient, RedisClientWithUserContext {
170
170
self . channel. closeFuture. whenSuccess {
171
171
// if our state is still open, that means we didn't cause the closeFuture to resolve.
172
172
// update state, metrics, and logging
173
- guard self . state. isConnected else { return }
174
-
173
+ let oldState = self . state
175
174
self . state = . closed
176
- self . logger. error ( " connection was closed unexpectedly " )
177
175
RedisMetrics . activeConnectionCount. decrement ( )
178
- self . onUnexpectedClosure ? ( )
176
+
177
+ switch oldState {
178
+ case . shuttingDown, . closed:
179
+ break
180
+ case . open, . pubsub:
181
+ logger. warning ( " connection was closed unexpectedly " )
182
+ self . onUnexpectedClosure ? ( )
183
+ }
179
184
}
180
185
181
186
self . logger. trace ( " connection created " )
@@ -333,62 +338,21 @@ extension RedisConnection {
333
338
// we're now in a shutdown state, starting with the command queue.
334
339
self . state = . shuttingDown
335
340
336
- let notification = self . sendQuitCommand ( logger: logger) // send "QUIT" so that all the responses are written out
337
- . flatMap { self . closeChannel ( ) } // close the channel from our end
341
+ // Inform ChannelHandler about close intent using "RedisGracefulConnectionCloseEvent"
342
+ let closePromise = self . eventLoop. makePromise ( of: Void . self)
343
+ let closeFuture = closePromise. futureResult
344
+ self . channel. triggerUserOutboundEvent ( RedisGracefulConnectionCloseEvent ( ) , promise: closePromise)
338
345
339
- notification . whenFailure {
346
+ closeFuture . whenFailure {
340
347
logger. error ( " error while closing connection " , metadata: [
341
348
RedisLogging . MetadataKeys. error: " \( $0) "
342
349
] )
343
350
}
344
- notification. whenSuccess {
345
- self . state = . closed
351
+ closeFuture. whenSuccess {
346
352
logger. trace ( " connection is now closed " )
347
- RedisMetrics . activeConnectionCount. decrement ( )
348
353
}
349
354
350
- return notification
351
- }
352
-
353
- /// Bypasses everything for a normal command and explicitly just sends a "QUIT" command to Redis.
354
- /// - Note: If the command fails, the `NIO.EventLoopFuture` will still succeed - as it's not critical for the command to succeed.
355
- private func sendQuitCommand( logger: Logger ) -> EventLoopFuture < Void > {
356
- let promise = channel. eventLoop. makePromise ( of: RESPValue . self)
357
- let command = RedisCommand (
358
- message: . array( [ RESPValue ( bulk: " QUIT " ) ] ) ,
359
- responsePromise: promise
360
- )
361
-
362
- logger. trace ( " sending QUIT command " )
363
-
364
- return channel. writeAndFlush ( command) // write the command
365
- . flatMap { promise. futureResult } // chain the callback to the response's
366
- . map { _ in logger. trace ( " sent QUIT command " ) } // ignore the result's value
367
- . recover { _ in logger. debug ( " recovered from error sending QUIT " ) } // if there's an error, just return to void
368
- }
369
-
370
- /// Attempts to close the `NIO.Channel`.
371
- /// SwiftNIO throws a `NIO.EventLoopError.shutdown` if the channel is already closed,
372
- /// so that case is captured to let this method's `NIO.EventLoopFuture` still succeed.
373
- private func closeChannel( ) -> EventLoopFuture < Void > {
374
- let promise = self . channel. eventLoop. makePromise ( of: Void . self)
375
-
376
- self . channel. close ( promise: promise)
377
-
378
- // if we succeed, great, if not - check the error that happened
379
- return promise. futureResult
380
- . flatMapError { error in
381
- guard let e = error as? EventLoopError else {
382
- return self . eventLoop. makeFailedFuture ( error)
383
- }
384
-
385
- // if the error is that the channel is already closed, great - just succeed.
386
- // otherwise, fail the chain
387
- switch e {
388
- case . shutdown: return self . eventLoop. makeSucceededFuture ( ( ) )
389
- default : return self . eventLoop. makeFailedFuture ( e)
390
- }
391
- }
355
+ return closeFuture
392
356
}
393
357
}
394
358
0 commit comments