@@ -80,6 +80,7 @@ export class RedisPubSub implements PubSubEngine {
80
80
81
81
this . subscriptionMap = { } ;
82
82
this . subsRefsMap = new Map < string , Set < number > > ( ) ;
83
+ this . subsPendingRefsMap = new Map < string , { refs : number [ ] , pending : Promise < number > } > ( ) ;
83
84
this . currentSubscriptionId = 0 ;
84
85
}
85
86
@@ -108,22 +109,44 @@ export class RedisPubSub implements PubSubEngine {
108
109
}
109
110
110
111
const refs = this . subsRefsMap . get ( triggerName ) ;
111
- if ( refs . size > 0 ) {
112
+
113
+ const pendingRefs = this . subsPendingRefsMap . get ( triggerName )
114
+ if ( pendingRefs != null ) {
115
+ // A pending remote subscribe call is currently in flight, piggyback on it
116
+ pendingRefs . refs . push ( id )
117
+ return pendingRefs . pending . then ( ( ) => id )
118
+ } else if ( refs . size > 0 ) {
119
+ // Already actively subscribed to redis
112
120
refs . add ( id ) ;
113
121
return Promise . resolve ( id ) ;
114
122
} else {
115
- return new Promise < number > ( ( resolve , reject ) => {
123
+ // New subscription.
124
+ // Keep a pending state until the remote subscribe call is completed
125
+ const pending = new Deferred ( )
126
+ const subsPendingRefsMap = this . subsPendingRefsMap
127
+ subsPendingRefsMap . set ( triggerName , { refs : [ ] , pending } ) ;
128
+
129
+ const sub = new Promise < number > ( ( resolve , reject ) => {
116
130
const subscribeFn = options [ 'pattern' ] ? this . redisSubscriber . psubscribe : this . redisSubscriber . subscribe ;
117
131
118
132
subscribeFn . call ( this . redisSubscriber , triggerName , err => {
119
133
if ( err ) {
134
+ subsPendingRefsMap . delete ( triggerName )
120
135
reject ( err ) ;
121
136
} else {
137
+ // Add ids of subscribe calls initiated when waiting for the remote call response
138
+ const pendingRefs = subsPendingRefsMap . get ( triggerName )
139
+ pendingRefs . refs . forEach ( ( id ) => refs . add ( id ) )
140
+ subsPendingRefsMap . delete ( triggerName )
141
+
122
142
refs . add ( id ) ;
123
143
resolve ( id ) ;
124
144
}
125
145
} ) ;
126
146
} ) ;
147
+ // Ensure waiting subscribe will complete
148
+ sub . then ( pending . resolve ) . catch ( pending . reject )
149
+ return sub ;
127
150
}
128
151
}
129
152
@@ -173,6 +196,7 @@ export class RedisPubSub implements PubSubEngine {
173
196
174
197
private readonly subscriptionMap : { [ subId : number ] : [ string , OnMessage < unknown > ] } ;
175
198
private readonly subsRefsMap : Map < string , Set < number > > ;
199
+ private readonly subsPendingRefsMap : Map < string , { refs : number [ ] , pending : Promise < number > } > ;
176
200
private currentSubscriptionId : number ;
177
201
178
202
private onMessage ( pattern : string , channel : string | Buffer , message : string | Buffer ) {
@@ -203,6 +227,19 @@ export class RedisPubSub implements PubSubEngine {
203
227
}
204
228
}
205
229
230
+ // Unexported deferrable promise used to complete waiting subscribe calls
231
+ function Deferred ( ) {
232
+ const p = this . promise = new Promise ( ( resolve , reject ) => {
233
+ this . resolve = resolve ;
234
+ this . reject = reject ;
235
+ } ) ;
236
+ this . then = p . then . bind ( p ) ;
237
+ this . catch = p . catch . bind ( p ) ;
238
+ if ( p . finally ) {
239
+ this . finally = p . finally . bind ( p ) ;
240
+ }
241
+ }
242
+
206
243
export type Path = Array < string | number > ;
207
244
export type Trigger = string | Path ;
208
245
export type TriggerTransform = (
0 commit comments