@@ -15,6 +15,14 @@ export default class ClusterSubscriber {
15
15
private connectionPool : ConnectionPool ,
16
16
private emitter : EventEmitter
17
17
) {
18
+ // If the current node we're using as the subscriber disappears
19
+ // from the node pool for some reason, we will select a new one
20
+ // to connect to.
21
+ // Note that this event is only triggered if the connection to
22
+ // the node has been used; cluster subscriptions are setup with
23
+ // lazyConnect = true. It's possible for the subscriber node to
24
+ // disappear without this method being called!
25
+ // See https://github.com/luin/ioredis/pull/1589
18
26
this . connectionPool . on ( "-node" , ( _ , key : string ) => {
19
27
if ( ! this . started || ! this . subscriber ) {
20
28
return ;
@@ -54,16 +62,30 @@ export default class ClusterSubscriber {
54
62
debug ( "stopped" ) ;
55
63
}
56
64
65
+ private onSubscriberEnd = ( ) => {
66
+ if ( ! this . started ) {
67
+ debug ( "subscriber has disconnected, but ClusterSubscriber is not started, so not reconnecting." ) ;
68
+ return ;
69
+ }
70
+ // If the subscriber closes whilst it's still the active connection,
71
+ // we might as well try to connecting to a new node if possible to
72
+ // minimise the number of missed publishes.
73
+ debug ( "subscriber has disconnected, selecting a new one..." ) ;
74
+ this . selectSubscriber ( ) ;
75
+ }
76
+
57
77
private selectSubscriber ( ) {
58
78
const lastActiveSubscriber = this . lastActiveSubscriber ;
59
79
60
80
// Disconnect the previous subscriber even if there
61
81
// will not be a new one.
62
82
if ( lastActiveSubscriber ) {
83
+ lastActiveSubscriber . off ( "end" , this . onSubscriberEnd ) ;
63
84
lastActiveSubscriber . disconnect ( ) ;
64
85
}
65
86
66
87
if ( this . subscriber ) {
88
+ this . subscriber . off ( "end" , this . onSubscriberEnd ) ;
67
89
this . subscriber . disconnect ( ) ;
68
90
}
69
91
@@ -97,11 +119,22 @@ export default class ClusterSubscriber {
97
119
connectionName : getConnectionName ( "subscriber" , options . connectionName ) ,
98
120
lazyConnect : true ,
99
121
tls : options . tls ,
122
+ // Don't try to reconnect the subscriber connection. If the connection fails
123
+ // we will get an end event (handled below), at which point we'll pick a new
124
+ // node from the pool and try to connect to that as the subscriber connection.
125
+ retryStrategy : null
100
126
} ) ;
101
127
102
128
// Ignore the errors since they're handled in the connection pool.
103
129
this . subscriber . on ( "error" , noop ) ;
104
130
131
+ // The node we lost connection to may not come back up in a
132
+ // reasonable amount of time (e.g. a slave that's taken down
133
+ // for maintainence), we could potentially miss many published
134
+ // messages so we should reconnect as quickly as possible, to
135
+ // a different node if needed.
136
+ this . subscriber . once ( "end" , this . onSubscriberEnd ) ;
137
+
105
138
// Re-subscribe previous channels
106
139
const previousChannels = { subscribe : [ ] , psubscribe : [ ] } ;
107
140
if ( lastActiveSubscriber ) {
0 commit comments