ioredis 4.23.0 → 4.24.2

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/Changelog.md CHANGED
@@ -1,3 +1,31 @@
1
+ ## [4.24.2](https://github.com/luin/ioredis/compare/v4.24.1...v4.24.2) (2021-03-14)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * properly handle instant stream errors ([#1299](https://github.com/luin/ioredis/issues/1299)) ([0327ef5](https://github.com/luin/ioredis/commit/0327ef5a57481042d3f7d306917f55ef04f3a6cc))
7
+
8
+ ## [4.24.1](https://github.com/luin/ioredis/compare/v4.24.0...v4.24.1) (2021-03-14)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **cluster:** reconnect when failing to refresh slots cache for all nodes ([8524eea](https://github.com/luin/ioredis/commit/8524eeaedaa2542f119f2b65ab8e2f15644b474e))
14
+
15
+ # [4.24.0](https://github.com/luin/ioredis/compare/v4.23.1...v4.24.0) (2021-03-14)
16
+
17
+
18
+ ### Features
19
+
20
+ * **cluster:** support retrying MOVED with a delay ([#1254](https://github.com/luin/ioredis/issues/1254)) ([8599981](https://github.com/luin/ioredis/commit/8599981141e8357f5ae2706fffb55010490bf002))
21
+
22
+ ## [4.23.1](https://github.com/luin/ioredis/compare/v4.23.0...v4.23.1) (2021-03-14)
23
+
24
+
25
+ ### Bug Fixes
26
+
27
+ * **cluster:** issues when code is processed by babel ([#1298](https://github.com/luin/ioredis/issues/1298)) ([bfc194d](https://github.com/luin/ioredis/commit/bfc194dcad2af527e802d6f5b060f0b0779e840d)), closes [#1288](https://github.com/luin/ioredis/issues/1288)
28
+
1
29
  # [4.23.0](https://github.com/luin/ioredis/compare/v4.22.0...v4.23.0) (2021-02-25)
2
30
 
3
31
 
package/README.md CHANGED
@@ -889,9 +889,12 @@ cluster.get("foo", (err, res) => {
889
889
  will resend the commands after the specified time (in ms).
890
890
  - `retryDelayOnTryAgain`: If this option is a number (by default, it is `100`), the client
891
891
  will resend the commands rejected with `TRYAGAIN` error after the specified time (in ms).
892
+ - `retryDelayOnMoved`: By default, this value is `0` (in ms), which means when a `MOVED` error is received, the client will resend
893
+ the command instantly to the node returned together with the `MOVED` error. However, sometimes it takes time for a cluster to become
894
+ state stabilized after a failover, so adding a delay before resending can prevent a ping pong effect.
892
895
  - `redisOptions`: Default options passed to the constructor of `Redis` when connecting to a node.
893
- - `slotsRefreshTimeout`: Milliseconds before a timeout occurs while refreshing slots from the cluster (default `1000`)
894
- - `slotsRefreshInterval`: Milliseconds between every automatic slots refresh (default `5000`)
896
+ - `slotsRefreshTimeout`: Milliseconds before a timeout occurs while refreshing slots from the cluster (default `1000`).
897
+ - `slotsRefreshInterval`: Milliseconds between every automatic slots refresh (default `5000`).
895
898
 
896
899
  ### Read-write splitting
897
900
 
@@ -7,6 +7,7 @@ exports.DEFAULT_CLUSTER_OPTIONS = {
7
7
  enableReadyCheck: true,
8
8
  scaleReads: "master",
9
9
  maxRedirections: 16,
10
+ retryDelayOnMoved: 0,
10
11
  retryDelayOnFailover: 100,
11
12
  retryDelayOnClusterDown: 100,
12
13
  retryDelayOnTryAgain: 100,
@@ -185,12 +185,7 @@ class Cluster extends events_1.EventEmitter {
185
185
  this.once("refresh", refreshListener);
186
186
  this.once("close", closeListener);
187
187
  this.once("close", this.handleCloseEvent.bind(this));
188
- this.refreshSlotsCache(function (err) {
189
- if (err && err.message === "Failed to refresh slots cache.") {
190
- redis_1.default.prototype.silentEmit.call(this, "error", err);
191
- this.connectionPool.reset([]);
192
- }
193
- }.bind(this));
188
+ this.refreshSlotsCache();
194
189
  this.subscriber.start();
195
190
  })
196
191
  .catch((err) => {
@@ -390,6 +385,8 @@ class Cluster extends events_1.EventEmitter {
390
385
  function tryNode(index) {
391
386
  if (index === nodes.length) {
392
387
  const error = new ClusterAllFailedError_1.default("Failed to refresh slots cache.", lastNodeError);
388
+ redis_1.default.prototype.silentEmit.call(_this, "error", error);
389
+ _this.connectionPool.reset([]);
393
390
  return wrapper(error);
394
391
  }
395
392
  const node = nodes[index];
@@ -608,8 +605,17 @@ class Cluster extends events_1.EventEmitter {
608
605
  return;
609
606
  }
610
607
  const errv = error.message.split(" ");
611
- if (errv[0] === "MOVED" || errv[0] === "ASK") {
612
- handlers[errv[0] === "MOVED" ? "moved" : "ask"](errv[1], errv[2]);
608
+ if (errv[0] === "MOVED") {
609
+ const timeout = this.options.retryDelayOnMoved;
610
+ if (timeout && typeof timeout === "number") {
611
+ this.delayQueue.push("moved", handlers.moved.bind(null, errv[1], errv[2]), { timeout });
612
+ }
613
+ else {
614
+ handlers.moved(errv[1], errv[2]);
615
+ }
616
+ }
617
+ else if (errv[0] === "ASK") {
618
+ handlers.ask(errv[1], errv[2]);
613
619
  }
614
620
  else if (errv[0] === "TRYAGAIN") {
615
621
  this.delayQueue.push("tryagain", handlers.tryagain, {
package/built/command.js CHANGED
@@ -274,9 +274,7 @@ const msetArgumentTransformer = function (args) {
274
274
  }
275
275
  return args;
276
276
  };
277
- Command.setArgumentTransformer("mset", msetArgumentTransformer);
278
- Command.setArgumentTransformer("msetnx", msetArgumentTransformer);
279
- Command.setArgumentTransformer("hmset", function (args) {
277
+ const hsetArgumentTransformer = function (args) {
280
278
  if (args.length === 2) {
281
279
  if (typeof Map !== "undefined" && args[1] instanceof Map) {
282
280
  return [args[0]].concat(utils_1.convertMapToArray(args[1]));
@@ -286,7 +284,11 @@ Command.setArgumentTransformer("hmset", function (args) {
286
284
  }
287
285
  }
288
286
  return args;
289
- });
287
+ };
288
+ Command.setArgumentTransformer("mset", msetArgumentTransformer);
289
+ Command.setArgumentTransformer("msetnx", msetArgumentTransformer);
290
+ Command.setArgumentTransformer("hset", hsetArgumentTransformer);
291
+ Command.setArgumentTransformer("hmset", hsetArgumentTransformer);
290
292
  Command.setReplyTransformer("hgetall", function (result) {
291
293
  if (Array.isArray(result)) {
292
294
  const obj = {};
@@ -297,17 +299,6 @@ Command.setReplyTransformer("hgetall", function (result) {
297
299
  }
298
300
  return result;
299
301
  });
300
- Command.setArgumentTransformer("hset", function (args) {
301
- if (args.length === 2) {
302
- if (typeof Map !== "undefined" && args[1] instanceof Map) {
303
- return [args[0]].concat(utils_1.convertMapToArray(args[1]));
304
- }
305
- if (typeof args[1] === "object" && args[1] !== null) {
306
- return [args[0]].concat(utils_1.convertObjectToArray(args[1]));
307
- }
308
- }
309
- return args;
310
- });
311
302
  class MixedBuffers {
312
303
  constructor() {
313
304
  this.length = 0;
@@ -78,6 +78,9 @@ class SentinelConnector extends AbstractConnector_1.default {
78
78
  else {
79
79
  this.stream = net_1.createConnection(resolved);
80
80
  }
81
+ this.stream.once("error", (err) => {
82
+ this.firstError = err;
83
+ });
81
84
  this.sentinelIterator.reset(true);
82
85
  resolve(this.stream);
83
86
  }
@@ -62,6 +62,9 @@ class StandaloneConnector extends AbstractConnector_1.default {
62
62
  reject(err);
63
63
  return;
64
64
  }
65
+ this.stream.once("error", (err) => {
66
+ this.firstError = err;
67
+ });
65
68
  resolve(this.stream);
66
69
  });
67
70
  });
package/built/pipeline.js CHANGED
@@ -27,7 +27,8 @@ function generateMultiWithNodes(redis, keys) {
27
27
  function Pipeline(redis) {
28
28
  commander_1.default.call(this);
29
29
  this.redis = redis;
30
- this.isCluster = this.redis.constructor.name === "Cluster";
30
+ this.isCluster =
31
+ this.redis.constructor.name === "Cluster" || this.redis.isCluster;
31
32
  this.isPipeline = true;
32
33
  this.options = redis.options;
33
34
  this._queue = [];
@@ -320,11 +320,22 @@ Redis.prototype.connect = function (callback) {
320
320
  });
321
321
  }
322
322
  }
323
+ else if (stream.destroyed) {
324
+ const firstError = _this.connector.firstError;
325
+ if (firstError) {
326
+ process.nextTick(() => {
327
+ eventHandler.errorHandler(_this)(firstError);
328
+ });
329
+ }
330
+ process.nextTick(eventHandler.closeHandler(_this));
331
+ }
323
332
  else {
324
333
  process.nextTick(eventHandler.connectHandler(_this));
325
334
  }
326
- stream.once("error", eventHandler.errorHandler(_this));
327
- stream.once("close", eventHandler.closeHandler(_this));
335
+ if (!stream.destroyed) {
336
+ stream.once("error", eventHandler.errorHandler(_this));
337
+ stream.once("close", eventHandler.closeHandler(_this));
338
+ }
328
339
  if (options.noDelay) {
329
340
  stream.setNoDelay(true);
330
341
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ioredis",
3
- "version": "4.23.0",
3
+ "version": "4.24.2",
4
4
  "description": "A robust, performance-focused and full-featured Redis client for Node.js.",
5
5
  "main": "built/index.js",
6
6
  "files": [
@@ -34,7 +34,7 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "cluster-key-slot": "^1.1.0",
37
- "debug": "^4.1.1",
37
+ "debug": "^4.3.1",
38
38
  "denque": "^1.1.0",
39
39
  "lodash.defaults": "^4.2.0",
40
40
  "lodash.flatten": "^4.4.0",
@@ -42,7 +42,7 @@
42
42
  "redis-commands": "1.7.0",
43
43
  "redis-errors": "^1.2.0",
44
44
  "redis-parser": "^3.0.0",
45
- "standard-as-callback": "^2.0.1"
45
+ "standard-as-callback": "^2.1.0"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@semantic-release/changelog": "^5.0.1",