Skip to content

Commit 1c8cb85

Browse files
authoredJul 16, 2022
fix: always allow selecting a new node for cluster mode subscriptions when the current one fails (#1589)
1 parent 07ee6ea commit 1c8cb85

File tree

2 files changed

+35
-0
lines changed

2 files changed

+35
-0
lines changed
 

‎lib/cluster/ClusterSubscriber.ts

+33
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ export default class ClusterSubscriber {
1515
private connectionPool: ConnectionPool,
1616
private emitter: EventEmitter
1717
) {
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
1826
this.connectionPool.on("-node", (_, key: string) => {
1927
if (!this.started || !this.subscriber) {
2028
return;
@@ -54,16 +62,30 @@ export default class ClusterSubscriber {
5462
debug("stopped");
5563
}
5664

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+
5777
private selectSubscriber() {
5878
const lastActiveSubscriber = this.lastActiveSubscriber;
5979

6080
// Disconnect the previous subscriber even if there
6181
// will not be a new one.
6282
if (lastActiveSubscriber) {
83+
lastActiveSubscriber.off("end", this.onSubscriberEnd);
6384
lastActiveSubscriber.disconnect();
6485
}
6586

6687
if (this.subscriber) {
88+
this.subscriber.off("end", this.onSubscriberEnd);
6789
this.subscriber.disconnect();
6890
}
6991

@@ -97,11 +119,22 @@ export default class ClusterSubscriber {
97119
connectionName: getConnectionName("subscriber", options.connectionName),
98120
lazyConnect: true,
99121
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
100126
});
101127

102128
// Ignore the errors since they're handled in the connection pool.
103129
this.subscriber.on("error", noop);
104130

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+
105138
// Re-subscribe previous channels
106139
const previousChannels = { subscribe: [], psubscribe: [] };
107140
if (lastActiveSubscriber) {

‎test/functional/cluster/pub_sub.ts

+2
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ describe("cluster:pub/sub", function () {
4949
sub.subscribe("test cluster", function () {
5050
sub.set("foo", "bar").then((res) => {
5151
expect(res).to.eql("OK");
52+
sub.disconnect()
5253
done();
5354
});
5455
});
@@ -73,6 +74,7 @@ describe("cluster:pub/sub", function () {
7374
const sub = new Cluster([{ port: "30001", password: "abc" }]);
7475

7576
sub.subscribe("test cluster", function () {
77+
sub.disconnect()
7678
done();
7779
});
7880
});

0 commit comments

Comments
 (0)
Please sign in to comment.