ioredis 4.28.5 → 4.29.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,7 +12,7 @@
12
12
 
13
13
  A robust, performance-focused and full-featured [Redis](http://redis.io) client for [Node.js](https://nodejs.org).
14
14
 
15
- Supports Redis >= 2.6.12 and (Node.js >= 6). Completely compatible with Redis 6.x.
15
+ Supports Redis >= 2.6.12 and (Node.js >= 8). Completely compatible with Redis 7.x.
16
16
 
17
17
  # Features
18
18
 
@@ -89,6 +89,14 @@ class DataHandler {
89
89
  this.redis.emit("pmessageBuffer", pattern, reply[2], reply[3]);
90
90
  break;
91
91
  }
92
+ case "smessage": {
93
+ if (this.redis.listeners("smessage").length > 0) {
94
+ this.redis.emit("smessage", reply[1].toString(), reply[2] ? reply[2].toString() : "");
95
+ }
96
+ this.redis.emit("smessageBuffer", reply[1], reply[2]);
97
+ break;
98
+ }
99
+ case "ssubscribe":
92
100
  case "subscribe":
93
101
  case "psubscribe": {
94
102
  const channel = reply[1].toString();
@@ -102,6 +110,7 @@ class DataHandler {
102
110
  }
103
111
  break;
104
112
  }
113
+ case "sunsubscribe":
105
114
  case "unsubscribe":
106
115
  case "punsubscribe": {
107
116
  const channel = reply[1] ? reply[1].toString() : null;
@@ -11,6 +11,7 @@ class SubscriptionSet {
11
11
  this.set = {
12
12
  subscribe: {},
13
13
  psubscribe: {},
14
+ ssubscribe: {},
14
15
  };
15
16
  }
16
17
  add(set, channel) {
@@ -24,7 +25,8 @@ class SubscriptionSet {
24
25
  }
25
26
  isEmpty() {
26
27
  return (this.channels("subscribe").length === 0 &&
27
- this.channels("psubscribe").length === 0);
28
+ this.channels("psubscribe").length === 0 &&
29
+ this.channels("ssubscribe").length === 0);
28
30
  }
29
31
  }
30
32
  exports.default = SubscriptionSet;
@@ -35,5 +37,8 @@ function mapSet(set) {
35
37
  if (set === "punsubscribe") {
36
38
  return "psubscribe";
37
39
  }
40
+ if (set === "sunsubscribe") {
41
+ return "ssubscribe";
42
+ }
38
43
  return set;
39
44
  }
@@ -16,8 +16,10 @@ exports.notAllowedAutoPipelineCommands = [
16
16
  "multi",
17
17
  "subscribe",
18
18
  "psubscribe",
19
+ "ssubscribe",
19
20
  "unsubscribe",
20
- "unpsubscribe",
21
+ "punsubscribe",
22
+ "sunsubscribe",
21
23
  ];
22
24
  function executeAutoPipeline(client, slotKey) {
23
25
  /*
@@ -19,4 +19,5 @@ exports.DEFAULT_CLUSTER_OPTIONS = {
19
19
  enableAutoPipelining: false,
20
20
  autoPipeliningIgnoredCommands: [],
21
21
  maxScriptsCachingTime: 60000,
22
+ shardedSubscribers: false,
22
23
  };
@@ -5,11 +5,13 @@ const utils_1 = require("../utils");
5
5
  const redis_1 = require("../redis");
6
6
  const debug = utils_1.Debug("cluster:subscriber");
7
7
  class ClusterSubscriber {
8
- constructor(connectionPool, emitter) {
8
+ constructor(connectionPool, emitter, isSharded = false) {
9
9
  this.connectionPool = connectionPool;
10
10
  this.emitter = emitter;
11
+ this.isSharded = isSharded;
11
12
  this.started = false;
12
13
  this.subscriber = null;
14
+ this.slotRange = [];
13
15
  this.connectionPool.on("-node", (_, key) => {
14
16
  if (!this.started || !this.subscriber) {
15
17
  return;
@@ -57,31 +59,38 @@ class ClusterSubscriber {
57
59
  * provided for the subscriber is correct, and if not, the current subscriber
58
60
  * will be disconnected and a new subscriber will be selected.
59
61
  */
62
+ let connectionPrefix = "subscriber";
63
+ if (this.isSharded)
64
+ connectionPrefix = "ssubscriber";
60
65
  this.subscriber = new redis_1.default({
61
66
  port: options.port,
62
67
  host: options.host,
63
68
  username: options.username,
64
69
  password: options.password,
65
70
  enableReadyCheck: true,
66
- connectionName: util_1.getConnectionName("subscriber", options.connectionName),
71
+ connectionName: util_1.getConnectionName(connectionPrefix, options.connectionName),
67
72
  lazyConnect: true,
68
73
  tls: options.tls,
69
74
  });
70
75
  // Ignore the errors since they're handled in the connection pool.
71
76
  this.subscriber.on("error", utils_1.noop);
72
77
  // Re-subscribe previous channels
73
- const previousChannels = { subscribe: [], psubscribe: [] };
78
+ const previousChannels = { subscribe: [], psubscribe: [], ssubscribe: [] };
74
79
  if (lastActiveSubscriber) {
75
80
  const condition = lastActiveSubscriber.condition || lastActiveSubscriber.prevCondition;
76
81
  if (condition && condition.subscriber) {
77
82
  previousChannels.subscribe = condition.subscriber.channels("subscribe");
78
- previousChannels.psubscribe = condition.subscriber.channels("psubscribe");
83
+ previousChannels.psubscribe =
84
+ condition.subscriber.channels("psubscribe");
85
+ previousChannels.ssubscribe =
86
+ condition.subscriber.channels("ssubscribe");
79
87
  }
80
88
  }
81
89
  if (previousChannels.subscribe.length ||
82
- previousChannels.psubscribe.length) {
90
+ previousChannels.psubscribe.length ||
91
+ previousChannels.ssubscribe.length) {
83
92
  let pending = 0;
84
- for (const type of ["subscribe", "psubscribe"]) {
93
+ for (const type of ["subscribe", "psubscribe", "ssubscribe"]) {
85
94
  const channels = previousChannels[type];
86
95
  if (channels.length) {
87
96
  pending += 1;
@@ -112,6 +121,28 @@ class ClusterSubscriber {
112
121
  this.emitter.emit(event, arg1, arg2, arg3);
113
122
  });
114
123
  }
124
+ if (this.isSharded == true) {
125
+ for (const event of ["smessage", "smessageBuffer"]) {
126
+ this.subscriber.on(event, (arg1, arg2, arg3) => {
127
+ this.emitter.emit(event, arg1, arg2, arg3);
128
+ });
129
+ }
130
+ }
131
+ }
132
+ /**
133
+ * Associate this subscriber to a specific slot range.
134
+ *
135
+ * Returns the range or an empty array if the slot range couldn't be associated.
136
+ *
137
+ * BTW: This is more for debugging and testing purposes.
138
+ *
139
+ * @param range
140
+ */
141
+ associateSlotRange(range) {
142
+ if (this.isSharded) {
143
+ this.slotRange = range;
144
+ }
145
+ return this.slotRange;
115
146
  }
116
147
  start() {
117
148
  this.started = true;
@@ -126,5 +157,8 @@ class ClusterSubscriber {
126
157
  }
127
158
  debug("stopped");
128
159
  }
160
+ isStarted() {
161
+ return this.started;
162
+ }
129
163
  }
130
164
  exports.default = ClusterSubscriber;
@@ -0,0 +1,224 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const utils_1 = require("../utils");
4
+ const ClusterSubscriber_1 = require("./ClusterSubscriber");
5
+ const ConnectionPool_1 = require("./ConnectionPool");
6
+ const util_1 = require("./util");
7
+ const calculateSlot = require("cluster-key-slot");
8
+ const debug = utils_1.Debug("cluster:subscriberGroup");
9
+ /**
10
+ * Redis differs between "normal" and sharded PubSub. If using the "normal" PubSub feature, exactly one
11
+ * ClusterSubscriber exists per cluster instance. This works because the Redis cluster bus forwards m
12
+ * messages between shards. However, this has scalability limitations, which is the reason why the sharded
13
+ * PubSub feature was added to Redis. With sharded PubSub, each shard is responsible for its own messages.
14
+ * Given that, we need at least one ClusterSubscriber per master endpoint/node.
15
+ *
16
+ * This class leverages the previously exising ClusterSubscriber by adding support for multiple such subscribers
17
+ * in alignment to the master nodes of the cluster. The ClusterSubscriber class was extended in a non-breaking way
18
+ * to support this feature.
19
+ */
20
+ class ClusterSubscriberGroup {
21
+ /**
22
+ * Register callbacks
23
+ *
24
+ * @param cluster
25
+ */
26
+ constructor(cluster) {
27
+ this.cluster = cluster;
28
+ this.shardedSubscribers = new Map();
29
+ this.clusterSlots = [];
30
+ //Simple [min, max] slot ranges aren't enough because you can migrate single slots
31
+ this.subscriberToSlotsIndex = new Map();
32
+ this.channels = new Map();
33
+ cluster.on("+node", (redis) => {
34
+ this._addSubscriber(redis);
35
+ });
36
+ cluster.on("-node", (redis) => {
37
+ this._removeSubscriber(redis);
38
+ });
39
+ cluster.on("refresh", () => {
40
+ this._refreshSlots(cluster);
41
+ });
42
+ }
43
+ /**
44
+ * Get the responsible subscriber.
45
+ *
46
+ * Returns null if no subscriber was found
47
+ *
48
+ * @param slot
49
+ */
50
+ getResponsibleSubscriber(slot) {
51
+ const nodeKey = this.clusterSlots[slot][0];
52
+ return this.shardedSubscribers.get(nodeKey);
53
+ }
54
+ /**
55
+ * Adds a channel for which this subscriber group is responsible
56
+ *
57
+ * @param channels
58
+ */
59
+ addChannels(channels) {
60
+ const slot = calculateSlot(channels[0]);
61
+ //Check if the all channels belong to the same slot and otherwise reject the operation
62
+ channels.forEach((c) => {
63
+ if (calculateSlot(c) != slot)
64
+ return -1;
65
+ });
66
+ const currChannels = this.channels.get(slot);
67
+ if (!currChannels) {
68
+ this.channels.set(slot, channels);
69
+ }
70
+ else {
71
+ this.channels.set(slot, currChannels.concat(channels));
72
+ }
73
+ return Array.from(this.channels.values()).reduce((sum, array) => sum + array.length, 0);
74
+ }
75
+ /**
76
+ * Removes channels for which the subscriber group is responsible by optionally unsubscribing
77
+ * @param channels
78
+ */
79
+ removeChannels(channels) {
80
+ const slot = calculateSlot(channels[0]);
81
+ //Check if the all channels belong to the same slot and otherwise reject the operation
82
+ channels.forEach((c) => {
83
+ if (calculateSlot(c) != slot)
84
+ return -1;
85
+ });
86
+ const slotChannels = this.channels.get(slot);
87
+ if (slotChannels) {
88
+ const updatedChannels = slotChannels.filter((c) => !channels.includes(c));
89
+ this.channels.set(slot, updatedChannels);
90
+ }
91
+ return Array.from(this.channels.values()).reduce((sum, array) => sum + array.length, 0);
92
+ }
93
+ /**
94
+ * Disconnect all subscribers
95
+ */
96
+ stop() {
97
+ for (const s of this.shardedSubscribers.values()) {
98
+ s.stop();
99
+ }
100
+ }
101
+ /**
102
+ * Start all not yet started subscribers
103
+ */
104
+ start() {
105
+ for (const s of this.shardedSubscribers.values()) {
106
+ if (!s.isStarted()) {
107
+ s.start();
108
+ }
109
+ }
110
+ }
111
+ /**
112
+ * Add a subscriber to the group of subscribers
113
+ *
114
+ * @param redis
115
+ */
116
+ _addSubscriber(redis) {
117
+ const pool = new ConnectionPool_1.default(redis.options);
118
+ if (pool.addMasterNode(redis)) {
119
+ const sub = new ClusterSubscriber_1.default(pool, this.cluster, true);
120
+ const nodeKey = util_1.getNodeKey(redis.options);
121
+ this.shardedSubscribers.set(nodeKey, sub);
122
+ sub.start();
123
+ // We need to attempt to resubscribe them in case the new node serves their slot
124
+ this._resubscribe();
125
+ this.cluster.emit("+subscriber");
126
+ return sub;
127
+ }
128
+ return null;
129
+ }
130
+ /**
131
+ * Removes a subscriber from the group
132
+ * @param redis
133
+ */
134
+ _removeSubscriber(redis) {
135
+ const nodeKey = util_1.getNodeKey(redis.options);
136
+ const sub = this.shardedSubscribers.get(nodeKey);
137
+ if (sub) {
138
+ sub.stop();
139
+ this.shardedSubscribers.delete(nodeKey);
140
+ // Even though the subscriber to this node is going down, we might have another subscriber
141
+ // handling the same slots, so we need to attempt to subscribe the orphaned channels
142
+ this._resubscribe();
143
+ this.cluster.emit("-subscriber");
144
+ }
145
+ return this.shardedSubscribers;
146
+ }
147
+ /**
148
+ * Refreshes the subscriber-related slot ranges
149
+ *
150
+ * Returns false if no refresh was needed
151
+ *
152
+ * @param cluster
153
+ */
154
+ _refreshSlots(cluster) {
155
+ //If there was an actual change, then reassign the slot ranges
156
+ if (this._slotsAreEqual(cluster.slots)) {
157
+ debug("Nothing to refresh because the new cluster map is equal to the previous one.");
158
+ }
159
+ else {
160
+ debug("Refreshing the slots of the subscriber group.");
161
+ //Rebuild the slots index
162
+ this.subscriberToSlotsIndex = new Map();
163
+ for (let slot = 0; slot < cluster.slots.length; slot++) {
164
+ const node = cluster.slots[slot][0];
165
+ if (!this.subscriberToSlotsIndex.has(node)) {
166
+ this.subscriberToSlotsIndex.set(node, []);
167
+ }
168
+ this.subscriberToSlotsIndex.get(node).push(Number(slot));
169
+ }
170
+ //Update the subscribers from the index
171
+ this._resubscribe();
172
+ //Update the cached slots map
173
+ this.clusterSlots = JSON.parse(JSON.stringify(cluster.slots));
174
+ this.cluster.emit("subscribersReady");
175
+ return true;
176
+ }
177
+ return false;
178
+ }
179
+ /**
180
+ * Resubscribes to the previous channels
181
+ *
182
+ * @private
183
+ */
184
+ _resubscribe() {
185
+ if (this.shardedSubscribers) {
186
+ this.shardedSubscribers.forEach((s, nodeKey) => {
187
+ const subscriberSlots = this.subscriberToSlotsIndex.get(nodeKey);
188
+ if (subscriberSlots) {
189
+ //More for debugging purposes
190
+ s.associateSlotRange(subscriberSlots);
191
+ //Resubscribe on the underlying connection
192
+ subscriberSlots.forEach((ss) => {
193
+ //Might return null if being disconnected
194
+ const redis = s.getInstance();
195
+ const channels = this.channels.get(ss);
196
+ if (channels && channels.length > 0) {
197
+ //Try to subscribe now
198
+ if (redis) {
199
+ redis.ssubscribe(channels);
200
+ //If the instance isn't ready yet, then register the re-subscription for later
201
+ redis.on("ready", () => {
202
+ redis.ssubscribe(channels);
203
+ });
204
+ }
205
+ }
206
+ });
207
+ }
208
+ });
209
+ }
210
+ }
211
+ /**
212
+ * Deep equality of the cluster slots objects
213
+ *
214
+ * @param other
215
+ * @private
216
+ */
217
+ _slotsAreEqual(other) {
218
+ if (this.clusterSlots === undefined)
219
+ return false;
220
+ else
221
+ return JSON.stringify(this.clusterSlots) === JSON.stringify(other);
222
+ }
223
+ }
224
+ exports.default = ClusterSubscriberGroup;
@@ -29,6 +29,39 @@ class ConnectionPool extends events_1.EventEmitter {
29
29
  const sampleKey = utils_1.sample(keys);
30
30
  return this.nodes[role][sampleKey];
31
31
  }
32
+ /**
33
+ * Add a master node to the pool
34
+ * @param node
35
+ */
36
+ addMasterNode(node) {
37
+ const key = util_1.getNodeKey(node.options);
38
+ const redis = this.createRedisFromOptions(node, node.options.readOnly);
39
+ //Master nodes aren't read-only
40
+ if (!node.options.readOnly) {
41
+ this.nodes.all[key] = redis;
42
+ this.nodes.master[key] = redis;
43
+ return true;
44
+ }
45
+ return false;
46
+ }
47
+ /**
48
+ * Creates a Redis connection instance from the node options
49
+ * @param node
50
+ * @param readOnly
51
+ */
52
+ createRedisFromOptions(node, readOnly) {
53
+ return new redis_1.default(utils_1.defaults({
54
+ // Never try to reconnect when a node is lose,
55
+ // instead, waiting for a `MOVED` error and
56
+ // fetch the slots again.
57
+ retryStrategy: null,
58
+ // Offline queue should be enabled so that
59
+ // we don't need to wait for the `ready` event
60
+ // before sending commands to the node.
61
+ enableOfflineQueue: true,
62
+ readOnly: readOnly,
63
+ }, node, this.redisOptions, { lazyConnect: true }));
64
+ }
32
65
  /**
33
66
  * Find or create a connection to the node
34
67
  *
@@ -65,17 +98,7 @@ class ConnectionPool extends events_1.EventEmitter {
65
98
  }
66
99
  else {
67
100
  debug("Connecting to %s as %s", key, readOnly ? "slave" : "master");
68
- redis = new redis_1.default(utils_1.defaults({
69
- // Never try to reconnect when a node is lose,
70
- // instead, waiting for a `MOVED` error and
71
- // fetch the slots again.
72
- retryStrategy: null,
73
- // Offline queue should be enabled so that
74
- // we don't need to wait for the `ready` event
75
- // before sending commands to the node.
76
- enableOfflineQueue: true,
77
- readOnly: readOnly,
78
- }, node, this.redisOptions, { lazyConnect: true }));
101
+ redis = this.createRedisFromOptions(node, readOnly);
79
102
  this.nodes.all[key] = redis;
80
103
  this.nodes[readOnly ? "slave" : "master"][key] = redis;
81
104
  redis.once("end", () => {
@@ -13,11 +13,12 @@ const standard_as_callback_1 = require("standard-as-callback");
13
13
  const PromiseContainer = require("../promiseContainer");
14
14
  const ClusterOptions_1 = require("./ClusterOptions");
15
15
  const utils_2 = require("../utils");
16
- const commands = require("redis-commands");
16
+ const commands_1 = require("@ioredis/commands");
17
17
  const command_1 = require("../command");
18
18
  const redis_1 = require("../redis");
19
19
  const commander_1 = require("../commander");
20
20
  const Deque = require("denque");
21
+ const ClusterSubscriberGroup_1 = require("./ClusterSubscriberGroup");
21
22
  const debug = utils_1.Debug("cluster");
22
23
  /**
23
24
  * Client for the official Redis Cluster
@@ -61,6 +62,8 @@ class Cluster extends events_1.EventEmitter {
61
62
  commander_1.default.call(this);
62
63
  this.startupNodes = startupNodes;
63
64
  this.options = utils_1.defaults({}, options, ClusterOptions_1.DEFAULT_CLUSTER_OPTIONS, this.options);
65
+ if (this.options.shardedSubscribers == true)
66
+ this.shardedSubscribers = new ClusterSubscriberGroup_1.default(this);
64
67
  // validate options
65
68
  if (typeof this.options.scaleReads !== "function" &&
66
69
  ["all", "master", "slave"].indexOf(this.options.scaleReads) === -1) {
@@ -202,6 +205,9 @@ class Cluster extends events_1.EventEmitter {
202
205
  }
203
206
  }.bind(this));
204
207
  this.subscriber.start();
208
+ if (this.options.shardedSubscribers) {
209
+ this.shardedSubscribers.start();
210
+ }
205
211
  })
206
212
  .catch((err) => {
207
213
  this.setStatus("close");
@@ -262,6 +268,9 @@ class Cluster extends events_1.EventEmitter {
262
268
  }
263
269
  this.clearNodesRefreshInterval();
264
270
  this.subscriber.stop();
271
+ if (this.options.shardedSubscribers) {
272
+ this.shardedSubscribers.stop();
273
+ }
265
274
  if (status === "wait") {
266
275
  this.setStatus("close");
267
276
  this.handleCloseEvent();
@@ -475,8 +484,7 @@ class Cluster extends events_1.EventEmitter {
475
484
  let to = this.options.scaleReads;
476
485
  if (to !== "master") {
477
486
  const isCommandReadOnly = command.isReadOnly ||
478
- (commands.exists(command.name) &&
479
- commands.hasFlag(command.name, "readonly"));
487
+ (commands_1.exists(command.name) && commands_1.hasFlag(command.name, "readonly"));
480
488
  if (!isCommandReadOnly) {
481
489
  to = "master";
482
490
  }
@@ -538,7 +546,24 @@ class Cluster extends events_1.EventEmitter {
538
546
  }
539
547
  else if (command_1.default.checkFlag("ENTER_SUBSCRIBER_MODE", command.name) ||
540
548
  command_1.default.checkFlag("EXIT_SUBSCRIBER_MODE", command.name)) {
541
- redis = _this.subscriber.getInstance();
549
+ if (_this.options.shardedSubscribers == true &&
550
+ (command.name == "ssubscribe" || command.name == "sunsubscribe")) {
551
+ const sub = _this.shardedSubscribers.getResponsibleSubscriber(targetSlot);
552
+ let status = -1;
553
+ if (command.name == "ssubscribe")
554
+ status = _this.shardedSubscribers.addChannels(command.getKeys());
555
+ if (command.name == "sunsubscribe")
556
+ status = _this.shardedSubscribers.removeChannels(command.getKeys());
557
+ if (status !== -1) {
558
+ redis = sub.getInstance();
559
+ }
560
+ else {
561
+ command.reject(new redis_errors_1.AbortError("Can't add or remove the given channels. Are they in the same slot?"));
562
+ }
563
+ }
564
+ else {
565
+ redis = _this.subscriber.getInstance();
566
+ }
542
567
  if (!redis) {
543
568
  command.reject(new redis_errors_1.AbortError("No subscriber for the cluster"));
544
569
  return;
package/built/command.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const commands = require("redis-commands");
3
+ const commands_1 = require("@ioredis/commands");
4
4
  const calculateSlot = require("cluster-key-slot");
5
5
  const standard_as_callback_1 = require("standard-as-callback");
6
6
  const utils_1 = require("./utils");
@@ -131,8 +131,9 @@ class Command {
131
131
  _iterateKeys(transform = (key) => key) {
132
132
  if (typeof this.keys === "undefined") {
133
133
  this.keys = [];
134
- if (commands.exists(this.name)) {
135
- const keyIndexes = commands.getKeyIndexes(this.name, this.args);
134
+ if (commands_1.exists(this.name)) {
135
+ // @ts-ignore
136
+ const keyIndexes = commands_1.getKeyIndexes(this.name, this.args);
136
137
  for (const index of keyIndexes) {
137
138
  this.args[index] = transform(this.args[index]);
138
139
  this.keys.push(this.args[index]);
@@ -271,12 +272,14 @@ Command.FLAGS = {
271
272
  "psubscribe",
272
273
  "unsubscribe",
273
274
  "punsubscribe",
275
+ "ssubscribe",
276
+ "sunsubscribe",
274
277
  "ping",
275
278
  "quit",
276
279
  ],
277
280
  VALID_IN_MONITOR_MODE: ["monitor", "auth"],
278
- ENTER_SUBSCRIBER_MODE: ["subscribe", "psubscribe"],
279
- EXIT_SUBSCRIBER_MODE: ["unsubscribe", "punsubscribe"],
281
+ ENTER_SUBSCRIBER_MODE: ["subscribe", "psubscribe", "ssubscribe"],
282
+ EXIT_SUBSCRIBER_MODE: ["unsubscribe", "punsubscribe", "sunsubscribe"],
280
283
  WILL_DISCONNECT: ["quit"],
281
284
  };
282
285
  Command._transformer = {
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const lodash_1 = require("./utils/lodash");
4
+ const commands_1 = require("@ioredis/commands");
4
5
  const command_1 = require("./command");
5
6
  const script_1 = require("./script");
6
7
  const PromiseContainer = require("./promiseContainer");
@@ -26,9 +27,7 @@ function Commander() {
26
27
  this.addedBuiltinSet = new Set();
27
28
  }
28
29
  exports.default = Commander;
29
- const commands = require("redis-commands").list.filter(function (command) {
30
- return command !== "monitor";
31
- });
30
+ const commands = commands_1.list.filter((command) => command !== "monitor");
32
31
  commands.push("sentinel");
33
32
  /**
34
33
  * Return supported builtin commands
package/built/pipeline.js CHANGED
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const command_1 = require("./command");
4
4
  const util_1 = require("util");
5
5
  const standard_as_callback_1 = require("standard-as-callback");
6
- const redis_commands_1 = require("redis-commands");
6
+ const commands_1 = require("@ioredis/commands");
7
7
  const calculateSlot = require("cluster-key-slot");
8
8
  const pMap = require("p-map");
9
9
  const PromiseContainer = require("./promiseContainer");
@@ -104,7 +104,7 @@ Pipeline.prototype.fillResult = function (value, position) {
104
104
  }
105
105
  }
106
106
  else if (!command.inTransaction) {
107
- const isReadOnly = redis_commands_1.exists(command.name) && redis_commands_1.hasFlag(command.name, "readonly");
107
+ const isReadOnly = commands_1.exists(command.name) && commands_1.hasFlag(command.name, "readonly");
108
108
  if (!isReadOnly) {
109
109
  retriable = false;
110
110
  break;
@@ -243,6 +243,11 @@ function readyHandler(self) {
243
243
  debug("psubscribe %d channels", psubscribeChannels.length);
244
244
  self.psubscribe(psubscribeChannels);
245
245
  }
246
+ const ssubscribeChannels = condition.subscriber.channels("ssubscribe");
247
+ if (ssubscribeChannels.length) {
248
+ debug("ssubscribe %d channels", ssubscribeChannels.length);
249
+ self.ssubscribe(ssubscribeChannels);
250
+ }
246
251
  }
247
252
  }
248
253
  if (self.prevCommandQueue) {
@@ -11,7 +11,7 @@ const standard_as_callback_1 = require("standard-as-callback");
11
11
  const eventHandler = require("./event_handler");
12
12
  const connectors_1 = require("../connectors");
13
13
  const ScanStream_1 = require("../ScanStream");
14
- const commands = require("redis-commands");
14
+ const commands_1 = require("@ioredis/commands");
15
15
  const PromiseContainer = require("../promiseContainer");
16
16
  const transaction_1 = require("../transaction");
17
17
  const RedisOptions_1 = require("./RedisOptions");
@@ -650,8 +650,8 @@ Redis.prototype.sendCommand = function (command, stream) {
650
650
  let writable = this.status === "ready" ||
651
651
  (!stream &&
652
652
  this.status === "connect" &&
653
- commands.exists(command.name) &&
654
- commands.hasFlag(command.name, "loading"));
653
+ commands_1.exists(command.name) &&
654
+ commands_1.hasFlag(command.name, "loading"));
655
655
  if (!this.stream) {
656
656
  writable = false;
657
657
  }