@@ -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
@@ -102,22 +103,44 @@ export class RedisPubSub implements PubSubEngine {
102
103
}
103
104
104
105
const refs = this . subsRefsMap . get ( triggerName ) ;
105
- if ( refs . size > 0 ) {
106
+
107
+ const pendingRefs = this . subsPendingRefsMap . get ( triggerName )
108
+ if ( pendingRefs != null ) {
109
+ // A pending remote subscribe call is currently in flight, piggyback on it
110
+ pendingRefs . refs . push ( id )
111
+ return pendingRefs . pending . then ( ( ) => id )
112
+ } else if ( refs . size > 0 ) {
113
+ // Already actively subscribed to redis
106
114
refs . add ( id ) ;
107
115
return Promise . resolve ( id ) ;
108
116
} else {
109
- return new Promise < number > ( ( resolve , reject ) => {
117
+ // New subscription.
118
+ // Keep a pending state until the remote subscribe call is completed
119
+ const pending = new Deferred ( )
120
+ const subsPendingRefsMap = this . subsPendingRefsMap
121
+ subsPendingRefsMap . set ( triggerName , { refs : [ ] , pending } ) ;
122
+
123
+ const sub = new Promise < number > ( ( resolve , reject ) => {
110
124
const subscribeFn = options [ 'pattern' ] ? this . redisSubscriber . psubscribe : this . redisSubscriber . subscribe ;
111
125
112
126
subscribeFn . call ( this . redisSubscriber , triggerName , err => {
113
127
if ( err ) {
128
+ subsPendingRefsMap . delete ( triggerName )
114
129
reject ( err ) ;
115
130
} else {
131
+ // Add ids of subscribe calls initiated when waiting for the remote call response
132
+ const pendingRefs = subsPendingRefsMap . get ( triggerName )
133
+ pendingRefs . refs . forEach ( ( id ) => refs . add ( id ) )
134
+ subsPendingRefsMap . delete ( triggerName )
135
+
116
136
refs . add ( id ) ;
117
137
resolve ( id ) ;
118
138
}
119
139
} ) ;
120
140
} ) ;
141
+ // Ensure waiting subscribe will complete
142
+ sub . then ( pending . resolve ) . catch ( pending . reject )
143
+ return sub ;
121
144
}
122
145
}
123
146
@@ -167,6 +190,7 @@ export class RedisPubSub implements PubSubEngine {
167
190
168
191
private readonly subscriptionMap : { [ subId : number ] : [ string , OnMessage < unknown > ] } ;
169
192
private readonly subsRefsMap : Map < string , Set < number > > ;
193
+ private readonly subsPendingRefsMap : Map < string , { refs : number [ ] , pending : Promise < number > } > ;
170
194
private currentSubscriptionId : number ;
171
195
172
196
private onMessage ( pattern : string , channel : string , message : string ) {
@@ -191,6 +215,19 @@ export class RedisPubSub implements PubSubEngine {
191
215
}
192
216
}
193
217
218
+ // Unexported deferrable promise used to complete waiting subscribe calls
219
+ function Deferred ( ) {
220
+ const p = this . promise = new Promise ( ( resolve , reject ) => {
221
+ this . resolve = resolve ;
222
+ this . reject = reject ;
223
+ } ) ;
224
+ this . then = p . then . bind ( p ) ;
225
+ this . catch = p . catch . bind ( p ) ;
226
+ if ( p . finally ) {
227
+ this . finally = p . finally . bind ( p ) ;
228
+ }
229
+ }
230
+
194
231
export type Path = Array < string | number > ;
195
232
export type Trigger = string | Path ;
196
233
export type TriggerTransform = (
0 commit comments