@@ -101,6 +101,8 @@ public enum RedisSubscriptionTarget: Equatable, CustomDebugStringConvertible {
101
101
public final class RedisPubSubHandler {
102
102
private var state : State = . default
103
103
104
+ // each key in the following maps _must_ be prefixed as there can be clashes between patterns and channel names
105
+
104
106
/// A map of channel names or patterns and their respective event registration.
105
107
private var subscriptions : [ String : Subscription ]
106
108
/// A queue of subscribe changes awaiting notification of completion.
@@ -135,30 +137,35 @@ public final class RedisPubSubHandler {
135
137
extension RedisPubSubHandler {
136
138
private func handleSubscribeMessage(
137
139
withSubscriptionKey subscriptionKey: String ,
138
- reportedSubscriptionCount subscriptionCount: Int
140
+ reportedSubscriptionCount subscriptionCount: Int ,
141
+ keyPrefix: String
139
142
) {
140
- defer { self . pendingSubscribes. removeValue ( forKey: subscriptionKey) ? . succeed ( subscriptionCount) }
143
+ let prefixedKey = self . prefixKey ( subscriptionKey, with: keyPrefix)
144
+
145
+ defer { self . pendingSubscribes. removeValue ( forKey: prefixedKey) ? . succeed ( subscriptionCount) }
141
146
142
- guard let subscription = self . subscriptions [ subscriptionKey ] else { return }
147
+ guard let subscription = self . subscriptions [ prefixedKey ] else { return }
143
148
144
149
subscription. onSubscribe ? ( subscriptionKey, subscriptionCount)
145
150
subscription. onSubscribe = nil // nil to free memory
146
- self . subscriptions [ subscriptionKey ] = subscription
151
+ self . subscriptions [ prefixedKey ] = subscription
147
152
148
153
subscription. type. gauge. increment ( )
149
154
}
150
155
151
156
private func handleUnsubscribeMessage(
152
157
withSubscriptionKey subscriptionKey: String ,
153
158
reportedSubscriptionCount subscriptionCount: Int ,
154
- unsubscribeFromAllKey: String
159
+ unsubscribeFromAllKey: String ,
160
+ keyPrefix: String
155
161
) {
156
- guard let subscription = self . subscriptions. removeValue ( forKey: subscriptionKey) else { return }
162
+ let prefixedKey = self . prefixKey ( subscriptionKey, with: keyPrefix)
163
+ guard let subscription = self . subscriptions. removeValue ( forKey: prefixedKey) else { return }
157
164
158
165
subscription. onUnsubscribe ? ( subscriptionKey, subscriptionCount)
159
166
subscription. type. gauge. decrement ( )
160
167
161
- switch self . pendingUnsubscribes. removeValue ( forKey: subscriptionKey ) {
168
+ switch self . pendingUnsubscribes. removeValue ( forKey: prefixedKey ) {
162
169
// we found a specific pattern/channel was being removed, so just fulfill the notification
163
170
case let . some( promise) :
164
171
promise. succeed ( subscriptionCount)
@@ -178,9 +185,10 @@ extension RedisPubSubHandler {
178
185
private func handleMessage(
179
186
_ message: RESPValue ,
180
187
from channel: RedisChannelName ,
181
- withSubscriptionKey subscriptionKey: String
188
+ withSubscriptionKey subscriptionKey: String ,
189
+ keyPrefix: String
182
190
) {
183
- guard let subscription = self . subscriptions [ subscriptionKey] else { return }
191
+ guard let subscription = self . subscriptions [ self . prefixKey ( subscriptionKey, with : keyPrefix ) ] else { return }
184
192
subscription. onMessage ( channel, message)
185
193
RedisMetrics . subscriptionMessagesReceivedCount. increment ( )
186
194
}
@@ -232,7 +240,8 @@ extension RedisPubSubHandler {
232
240
subscribeHandler: subscribeHandler,
233
241
unsubscribeHandler: unsubscribeHandler
234
242
)
235
- guard self . subscriptions. updateValue ( subscription, forKey: targetKey) == nil else { return nil }
243
+ let prefixedKey = self . prefixKey ( targetKey, with: target. keyPrefix)
244
+ guard self . subscriptions. updateValue ( subscription, forKey: prefixedKey) == nil else { return nil }
236
245
return targetKey
237
246
}
238
247
@@ -245,7 +254,8 @@ extension RedisPubSubHandler {
245
254
return self . sendSubscriptionChange (
246
255
subscriptionChangeKeyword: target. subscribeKeyword,
247
256
subscriptionTargets: newSubscriptionTargets,
248
- queue: \. pendingSubscribes
257
+ queue: \. pendingSubscribes,
258
+ keyPrefix: target. keyPrefix
249
259
)
250
260
}
251
261
}
@@ -274,14 +284,16 @@ extension RedisPubSubHandler {
274
284
return self . sendSubscriptionChange (
275
285
subscriptionChangeKeyword: target. unsubscribeKeyword,
276
286
subscriptionTargets: target. values,
277
- queue: \. pendingUnsubscribes
287
+ queue: \. pendingUnsubscribes,
288
+ keyPrefix: target. keyPrefix
278
289
)
279
290
}
280
291
281
292
private func sendSubscriptionChange(
282
293
subscriptionChangeKeyword keyword: String ,
283
294
subscriptionTargets targets: [ String ] ,
284
- queue pendingQueue: ReferenceWritableKeyPath < RedisPubSubHandler , PendingSubscriptionChangeQueue >
295
+ queue pendingQueue: ReferenceWritableKeyPath < RedisPubSubHandler , PendingSubscriptionChangeQueue > ,
296
+ keyPrefix: String
285
297
) -> EventLoopFuture < Int > {
286
298
self . eventLoop. assertInEventLoop ( )
287
299
@@ -298,7 +310,7 @@ extension RedisPubSubHandler {
298
310
299
311
// create them
300
312
let pendingSubscriptions : [ ( String , EventLoopPromise < Int > ) ] = targets. map {
301
- return ( $0 , self . eventLoop. makePromise ( ) )
313
+ return ( self . prefixKey ( $0 , with : keyPrefix ) , self . eventLoop. makePromise ( ) )
302
314
}
303
315
// add the subscription change handler to the appropriate queue for each individual subscription target
304
316
pendingSubscriptions. forEach { self [ keyPath: pendingQueue] . updateValue ( $1, forKey: $0) }
@@ -399,28 +411,53 @@ extension RedisPubSubHandler: ChannelInboundHandler {
399
411
// if we have a match, we're definitely in a pubsub message and we should handle it
400
412
401
413
switch messageKeyword {
402
- case " message " : self . handleMessage ( message, from: . init( channelOrPattern) , withSubscriptionKey: channelOrPattern)
414
+ case " message " :
415
+ self . handleMessage (
416
+ message,
417
+ from: . init( channelOrPattern) ,
418
+ withSubscriptionKey: channelOrPattern,
419
+ keyPrefix: kSubscriptionKeyPrefixChannel
420
+ )
403
421
404
- // the channel name is stored as the 3rd element in the array in 'pmessage' streams
405
- case " pmessage " : self . handleMessage ( message, from: . init( array [ 2 ] . string!) , withSubscriptionKey: channelOrPattern)
422
+
423
+ case " pmessage " :
424
+ self . handleMessage (
425
+ message,
426
+ from: . init( array [ 2 ] . string!) , // the channel name is stored as the 3rd element in the array in 'pmessage' streams
427
+ withSubscriptionKey: channelOrPattern,
428
+ keyPrefix: kSubscriptionKeyPrefixPattern
429
+ )
406
430
407
431
// if the message keyword is for subscribing or unsubscribing,
408
432
// the message is guaranteed to be the count of subscriptions the connection still has
409
- case " subscribe " , " psubscribe " :
410
- self . handleSubscribeMessage ( withSubscriptionKey: channelOrPattern, reportedSubscriptionCount: message. int!)
433
+ case " subscribe " :
434
+ self . handleSubscribeMessage (
435
+ withSubscriptionKey: channelOrPattern,
436
+ reportedSubscriptionCount: message. int!,
437
+ keyPrefix: kSubscriptionKeyPrefixChannel
438
+ )
439
+
440
+ case " psubscribe " :
441
+ self . handleSubscribeMessage (
442
+ withSubscriptionKey: channelOrPattern,
443
+ reportedSubscriptionCount: message. int!,
444
+ keyPrefix: kSubscriptionKeyPrefixPattern
445
+ )
411
446
412
447
case " unsubscribe " :
413
448
self . handleUnsubscribeMessage (
414
449
withSubscriptionKey: channelOrPattern,
415
450
reportedSubscriptionCount: message. int!,
416
- unsubscribeFromAllKey: kUnsubscribeAllChannelsKey
451
+ unsubscribeFromAllKey: kUnsubscribeAllChannelsKey,
452
+ keyPrefix: kSubscriptionKeyPrefixChannel
417
453
)
418
454
419
455
case " punsubscribe " :
420
456
self . handleUnsubscribeMessage (
421
457
withSubscriptionKey: channelOrPattern,
422
458
reportedSubscriptionCount: message. int!,
423
- unsubscribeFromAllKey: kUnsubscribeAllPatternsKey
459
+ unsubscribeFromAllKey: kUnsubscribeAllPatternsKey,
460
+ keyPrefix: kSubscriptionKeyPrefixPattern
424
461
)
425
462
426
463
// if we don't have a match, fire a channel read to forward to the next handler
@@ -507,6 +544,13 @@ extension RedisPubSubHandler {
507
544
508
545
// MARK: Subscription Management Helpers
509
546
547
+ private let kSubscriptionKeyPrefixChannel = " __RS_CS "
548
+ private let kSubscriptionKeyPrefixPattern = " __RS_PS "
549
+
550
+ extension RedisPubSubHandler {
551
+ private func prefixKey( _ key: String , with prefix: String ) -> String { " \( prefix) _ \( key) " }
552
+ }
553
+
510
554
extension RedisSubscriptionTarget {
511
555
fileprivate var unsubscribeAllKey : String {
512
556
switch self {
@@ -515,6 +559,13 @@ extension RedisSubscriptionTarget {
515
559
}
516
560
}
517
561
562
+ fileprivate var keyPrefix : String {
563
+ switch self {
564
+ case . channels: return kSubscriptionKeyPrefixChannel
565
+ case . patterns: return kSubscriptionKeyPrefixPattern
566
+ }
567
+ }
568
+
518
569
fileprivate var subscriptionType : SubscriptionType {
519
570
switch self {
520
571
case . channels: return . channel
0 commit comments