@@ -21,7 +21,7 @@ import NIO
21
21
import NIOConcurrencyHelpers
22
22
23
23
extension RedisConnection {
24
-
24
+
25
25
/// Creates a new connection with provided configuration and sychronization objects.
26
26
///
27
27
/// If you would like to specialize the `NIO.ClientBootstrap` that the connection communicates on, override the default by passing it in as `configuredTCPClient`.
@@ -55,7 +55,7 @@ extension RedisConnection {
55
55
configuredTCPClient client: ClientBootstrap ? = nil
56
56
) -> EventLoopFuture < RedisConnection > {
57
57
let client = client ?? . makeRedisTCPClient( group: eventLoop)
58
-
58
+
59
59
var future = client
60
60
. connect ( to: config. address)
61
61
. map { return RedisConnection ( configuredRESPChannel: $0, context: config. defaultLogger) }
@@ -73,7 +73,7 @@ extension RedisConnection {
73
73
return connection. select ( database: database) . map { connection }
74
74
}
75
75
}
76
-
76
+
77
77
return future
78
78
}
79
79
}
@@ -157,14 +157,14 @@ public final class RedisConnection: RedisClient, RedisClientWithUserContext {
157
157
get { return _stateLock. withLock { self . _state } }
158
158
set ( newValue) { _stateLock. withLockVoid { self . _state = newValue } }
159
159
}
160
-
160
+
161
161
deinit {
162
162
if isConnected {
163
163
assertionFailure ( " close() was not called before deinit! " )
164
164
self . logger. warning ( " connection was not properly shutdown before deinit " )
165
165
}
166
166
}
167
-
167
+
168
168
internal init ( configuredRESPChannel: Channel , context: Context ) {
169
169
self . channel = configuredRESPChannel
170
170
// there is a mix of verbiage here as the API is forward thinking towards "baggage context"
@@ -176,14 +176,14 @@ public final class RedisConnection: RedisClient, RedisClientWithUserContext {
176
176
177
177
RedisMetrics . activeConnectionCount. increment ( )
178
178
RedisMetrics . totalConnectionCount. increment ( )
179
-
179
+
180
180
// attach a callback to the channel to capture situations where the channel might be closed out from under
181
181
// the connection
182
182
self . channel. closeFuture. whenSuccess {
183
183
// if our state is still open, that means we didn't cause the closeFuture to resolve.
184
184
// update state, metrics, and logging
185
185
guard self . state. isConnected else { return }
186
-
186
+
187
187
self . state = . closed
188
188
self . logger. error ( " connection was closed unexpectedly " )
189
189
RedisMetrics . activeConnectionCount. decrement ( )
@@ -192,13 +192,13 @@ public final class RedisConnection: RedisClient, RedisClientWithUserContext {
192
192
193
193
self . logger. trace ( " connection created " )
194
194
}
195
-
195
+
196
196
internal enum ConnectionState {
197
197
case open
198
198
case pubsub( RedisPubSubHandler )
199
199
case shuttingDown
200
200
case closed
201
-
201
+
202
202
var isConnected : Bool {
203
203
switch self {
204
204
case . open, . pubsub: return true
@@ -242,40 +242,40 @@ extension RedisConnection {
242
242
return self . channel. eventLoop. makeFailedFuture ( error)
243
243
}
244
244
logger. trace ( " received command request " )
245
-
245
+
246
246
logger. debug ( " sending command " , metadata: [
247
247
RedisLogging . MetadataKeys. commandKeyword: " \( command) " ,
248
248
RedisLogging . MetadataKeys. commandArguments: " \( arguments) "
249
249
] )
250
-
250
+
251
251
var message : [ RESPValue ] = [ . init( bulk: command) ]
252
252
message. append ( contentsOf: arguments)
253
-
253
+
254
254
let promise = channel. eventLoop. makePromise ( of: RESPValue . self)
255
255
let command = RedisCommand (
256
256
message: . array( message) ,
257
257
responsePromise: promise
258
258
)
259
-
259
+
260
260
let startTime = DispatchTime . now ( ) . uptimeNanoseconds
261
261
promise. futureResult. whenComplete { result in
262
262
let duration = DispatchTime . now ( ) . uptimeNanoseconds - startTime
263
263
RedisMetrics . commandRoundTripTime. recordNanoseconds ( duration)
264
-
264
+
265
265
// log data based on the result
266
266
switch result {
267
267
case let . failure( error) :
268
268
logger. error ( " command failed " , metadata: [
269
269
RedisLogging . MetadataKeys. error: " \( error. localizedDescription) "
270
270
] )
271
-
271
+
272
272
case let . success( value) :
273
273
logger. debug ( " command succeeded " , metadata: [
274
274
RedisLogging . MetadataKeys. commandResult: " \( value) "
275
275
] )
276
276
}
277
277
}
278
-
278
+
279
279
defer { logger. trace ( " command sent " ) }
280
280
281
281
if self . sendCommandsImmediately {
@@ -310,10 +310,10 @@ extension RedisConnection {
310
310
311
311
// we're now in a shutdown state, starting with the command queue.
312
312
self . state = . shuttingDown
313
-
313
+
314
314
let notification = self . sendQuitCommand ( logger: logger) // send "QUIT" so that all the responses are written out
315
315
. flatMap { self . closeChannel ( ) } // close the channel from our end
316
-
316
+
317
317
notification. whenFailure {
318
318
logger. error ( " error while closing connection " , metadata: [
319
319
RedisLogging . MetadataKeys. error: " \( $0) "
@@ -324,10 +324,10 @@ extension RedisConnection {
324
324
logger. trace ( " connection is now closed " )
325
325
RedisMetrics . activeConnectionCount. decrement ( )
326
326
}
327
-
327
+
328
328
return notification
329
329
}
330
-
330
+
331
331
/// Bypasses everything for a normal command and explicitly just sends a "QUIT" command to Redis.
332
332
/// - Note: If the command fails, the `NIO.EventLoopFuture` will still succeed - as it's not critical for the command to succeed.
333
333
private func sendQuitCommand( logger: Logger ) -> EventLoopFuture < Void > {
@@ -344,22 +344,22 @@ extension RedisConnection {
344
344
. map { _ in logger. trace ( " sent QUIT command " ) } // ignore the result's value
345
345
. recover { _ in logger. debug ( " recovered from error sending QUIT " ) } // if there's an error, just return to void
346
346
}
347
-
347
+
348
348
/// Attempts to close the `NIO.Channel`.
349
349
/// SwiftNIO throws a `NIO.EventLoopError.shutdown` if the channel is already closed,
350
350
/// so that case is captured to let this method's `NIO.EventLoopFuture` still succeed.
351
351
private func closeChannel( ) -> EventLoopFuture < Void > {
352
352
let promise = self . channel. eventLoop. makePromise ( of: Void . self)
353
-
353
+
354
354
self . channel. close ( promise: promise)
355
-
355
+
356
356
// if we succeed, great, if not - check the error that happened
357
357
return promise. futureResult
358
358
. flatMapError { error in
359
359
guard let e = error as? EventLoopError else {
360
360
return self . eventLoop. makeFailedFuture ( error)
361
361
}
362
-
362
+
363
363
// if the error is that the channel is already closed, great - just succeed.
364
364
// otherwise, fail the chain
365
365
switch e {
@@ -395,7 +395,7 @@ extension RedisConnection {
395
395
) -> EventLoopFuture < Void > {
396
396
return self . _subscribe ( . channels( channels) , receiver, subscribeHandler, unsubscribeHandler, nil )
397
397
}
398
-
398
+
399
399
public func psubscribe(
400
400
to patterns: [ String ] ,
401
401
messageReceiver receiver: @escaping RedisSubscriptionMessageReceiver ,
@@ -404,7 +404,7 @@ extension RedisConnection {
404
404
) -> EventLoopFuture < Void > {
405
405
return self . _subscribe ( . patterns( patterns) , receiver, subscribeHandler, unsubscribeHandler, nil )
406
406
}
407
-
407
+
408
408
internal func subscribe(
409
409
to channels: [ RedisChannelName ] ,
410
410
messageReceiver receiver: @escaping RedisSubscriptionMessageReceiver ,
@@ -414,7 +414,7 @@ extension RedisConnection {
414
414
) -> EventLoopFuture < Void > {
415
415
return self . _subscribe ( . channels( channels) , receiver, subscribeHandler, unsubscribeHandler, context)
416
416
}
417
-
417
+
418
418
internal func psubscribe(
419
419
to patterns: [ String ] ,
420
420
messageReceiver receiver: @escaping RedisSubscriptionMessageReceiver ,
@@ -424,7 +424,7 @@ extension RedisConnection {
424
424
) -> EventLoopFuture < Void > {
425
425
return self . _subscribe ( . patterns( patterns) , receiver, subscribeHandler, unsubscribeHandler, context)
426
426
}
427
-
427
+
428
428
private func _subscribe(
429
429
_ target: RedisSubscriptionTarget ,
430
430
_ receiver: @escaping RedisSubscriptionMessageReceiver ,
@@ -433,9 +433,9 @@ extension RedisConnection {
433
433
_ logger: Logger ?
434
434
) -> EventLoopFuture < Void > {
435
435
let logger = self . prepareLoggerForUse ( logger)
436
-
436
+
437
437
logger. trace ( " received subscribe request " )
438
-
438
+
439
439
// if we're closed, just error out
440
440
guard self . state. isConnected else { return self . eventLoop. makeFailedFuture ( RedisClientError . connectionClosed) }
441
441
@@ -483,7 +483,7 @@ extension RedisConnection {
483
483
logger. debug ( " the connection is now in pubsub mode " )
484
484
}
485
485
}
486
-
486
+
487
487
// add the subscription and just ignore the subscription count
488
488
return handler
489
489
. addSubscription ( for: target, messageReceiver: receiver, onSubscribe: onSubscribe, onUnsubscribe: onUnsubscribe)
@@ -497,27 +497,27 @@ extension RedisConnection {
497
497
public func unsubscribe( from channels: [ RedisChannelName ] ) -> EventLoopFuture < Void > {
498
498
return self . _unsubscribe ( . channels( channels) , nil )
499
499
}
500
-
500
+
501
501
public func punsubscribe( from patterns: [ String ] ) -> EventLoopFuture < Void > {
502
502
return self . _unsubscribe ( . patterns( patterns) , nil )
503
503
}
504
-
504
+
505
505
internal func unsubscribe( from channels: [ RedisChannelName ] , context: Context ? ) -> EventLoopFuture < Void > {
506
506
return self . _unsubscribe ( . channels( channels) , context)
507
507
}
508
-
508
+
509
509
internal func punsubscribe( from patterns: [ String ] , context: Context ? ) -> EventLoopFuture < Void > {
510
510
return self . _unsubscribe ( . patterns( patterns) , context)
511
511
}
512
-
512
+
513
513
private func _unsubscribe( _ target: RedisSubscriptionTarget , _ logger: Logger ? ) -> EventLoopFuture < Void > {
514
514
let logger = self . prepareLoggerForUse ( logger)
515
-
515
+
516
516
logger. trace ( " received unsubscribe request " )
517
517
518
518
// if we're closed, just error out
519
519
guard self . state. isConnected else { return self . eventLoop. makeFailedFuture ( RedisClientError . connectionClosed) }
520
-
520
+
521
521
// if we're not in pubsub mode, then we just succeed as a no-op
522
522
guard case let . pubsub( handler) = self . state else {
523
523
// but we still assert just to give some notification to devs at debug
@@ -526,11 +526,11 @@ extension RedisConnection {
526
526
] )
527
527
return self . eventLoop. makeSucceededFuture ( ( ) )
528
528
}
529
-
529
+
530
530
logger. trace ( " removing subscription " , metadata: [
531
531
RedisLogging . MetadataKeys. pubsubTarget: " \( target. debugDescription) "
532
532
] )
533
-
533
+
534
534
// remove the subscription
535
535
return handler. removeSubscription ( for: target)
536
536
. flatMap {
0 commit comments