ioredis 5.9.0 → 5.9.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
@@ -802,16 +802,22 @@ Set maxRetriesPerRequest to `null` to disable this behavior, and every command w
802
802
 
803
803
  ioredis can apply a client-side timeout to blocking commands (such as `blpop`, `brpop`, `bzpopmin`, `bzmpop`, `blmpop`, `xread`, `xreadgroup`, etc.). This protects against scenarios where the TCP connection becomes a zombie (e.g., due to a silent network failure like a Docker network disconnect) and Redis never replies.
804
804
 
805
- For commands with a finite timeout (e.g., `blpop("key", 5)`), ioredis automatically sets a client-side deadline based on the command's timeout plus a small grace period. If no reply arrives before the deadline, the command resolves with `null`—the same value Redis returns when a blocking command times out normally.
806
-
807
- For commands that intentionally block forever (e.g., `timeout = 0` or `BLOCK 0`), you can provide a safety net via the optional `blockingTimeout` option (milliseconds):
805
+ This feature is **opt-in**. It is **disabled by default** and is only enabled
806
+ when `blockingTimeout` is set to a positive number of milliseconds. If
807
+ `blockingTimeout` is omitted, `0`, or negative (for example `-1`), ioredis
808
+ does not arm any client-side timeouts for blocking commands and their
809
+ behavior matches Redis exactly.
808
810
 
809
811
  ```javascript
810
812
  const redis = new Redis({
811
- blockingTimeout: 30000, // Resolve with null after 30 seconds when timeout=0/BLOCK 0
813
+ blockingTimeout: 30000, // Enable blocking timeout protection
812
814
  });
813
815
  ```
814
816
 
817
+ When enabled:
818
+ - For commands with a finite timeout (e.g., `blpop("key", 5)`), ioredis sets a client-side deadline based on the command's timeout plus a small grace period (`blockingTimeoutGrace`, default 100ms). If no reply arrives before the deadline, the command resolves with `null`—the same value Redis returns when a blocking command times out normally.
819
+ - For commands that block forever (e.g., `timeout = 0` or `BLOCK 0`), the `blockingTimeout` value is used as a safety net.
820
+
815
821
  ### Reconnect on Error
816
822
 
817
823
  Besides auto-reconnect when the connection is closed, ioredis supports reconnecting on certain Redis errors using the `reconnectOnError` option. Here's an example that will reconnect when receiving `READONLY` error:
package/built/Redis.js CHANGED
@@ -325,7 +325,7 @@ class Redis extends Commander_1.default {
325
325
  * @ignore
326
326
  */
327
327
  sendCommand(command, stream) {
328
- var _a, _b, _c;
328
+ var _a, _b;
329
329
  if (this.status === "wait") {
330
330
  this.connect().catch(lodash_1.noop);
331
331
  }
@@ -379,11 +379,11 @@ class Redis extends Commander_1.default {
379
379
  stream: stream,
380
380
  select: this.condition.select,
381
381
  });
382
- // For blocking commands, set a timeout while queued to ensure they don't wait forever
383
- // if connection never becomes ready (e.g., docker network disconnect scenario)
384
- // Use blockingTimeout if configured, otherwise fall back to the command's own timeout
382
+ // For blocking commands in the offline queue, arm a client-side timeout
383
+ // only when blockingTimeout is configured. Without this option, queued
384
+ // blocking commands may wait indefinitely on a dead connection.
385
385
  if (Command_1.default.checkFlag("BLOCKING_COMMANDS", command.name)) {
386
- const offlineTimeout = (_b = this.getConfiguredBlockingTimeout()) !== null && _b !== void 0 ? _b : blockingTimeout;
386
+ const offlineTimeout = this.getConfiguredBlockingTimeout();
387
387
  if (offlineTimeout !== undefined) {
388
388
  command.setBlockingTimeout(offlineTimeout);
389
389
  }
@@ -392,7 +392,7 @@ class Redis extends Commander_1.default {
392
392
  else {
393
393
  // @ts-expect-error
394
394
  if (debug.enabled) {
395
- debug("write command[%s]: %d -> %s(%o)", this._getDescription(), (_c = this.condition) === null || _c === void 0 ? void 0 : _c.select, command.name, command.args);
395
+ debug("write command[%s]: %d -> %s(%o)", this._getDescription(), (_b = this.condition) === null || _b === void 0 ? void 0 : _b.select, command.name, command.args);
396
396
  }
397
397
  if (stream) {
398
398
  if ("isPipeline" in stream && stream.isPipeline) {
@@ -435,6 +435,11 @@ class Redis extends Commander_1.default {
435
435
  if (!Command_1.default.checkFlag("BLOCKING_COMMANDS", command.name)) {
436
436
  return undefined;
437
437
  }
438
+ // Feature is opt-in: only enabled when blockingTimeout is set to a positive number
439
+ const configuredTimeout = this.getConfiguredBlockingTimeout();
440
+ if (configuredTimeout === undefined) {
441
+ return undefined;
442
+ }
438
443
  const timeout = command.extractBlockingTimeout();
439
444
  if (typeof timeout === "number") {
440
445
  if (timeout > 0) {
@@ -442,11 +447,11 @@ class Redis extends Commander_1.default {
442
447
  return timeout + ((_a = this.options.blockingTimeoutGrace) !== null && _a !== void 0 ? _a : RedisOptions_1.DEFAULT_REDIS_OPTIONS.blockingTimeoutGrace);
443
448
  }
444
449
  // Command has timeout=0 (block forever), use blockingTimeout option as safety net
445
- return this.getConfiguredBlockingTimeout();
450
+ return configuredTimeout;
446
451
  }
447
452
  if (timeout === null) {
448
453
  // No BLOCK option found (e.g., XREAD without BLOCK), use blockingTimeout as safety net
449
- return this.getConfiguredBlockingTimeout();
454
+ return configuredTimeout;
450
455
  }
451
456
  return undefined;
452
457
  }
@@ -12,13 +12,15 @@ export interface CommonRedisOptions extends CommanderOptions {
12
12
  */
13
13
  commandTimeout?: number;
14
14
  /**
15
- * Timeout (ms) for blocking commands with timeout=0 / BLOCK 0.
16
- * When exceeded, the command resolves with null.
15
+ * Enables client-side timeout protection for blocking commands when set
16
+ * to a positive number. If `blockingTimeout` is undefined, `0`, or
17
+ * negative (e.g. `-1`), the protection is disabled and no client-side
18
+ * timers are installed for blocking commands.
17
19
  */
18
20
  blockingTimeout?: number;
19
21
  /**
20
- * Grace period (ms) added to blocking command timeouts to account for network latency.
21
- * @default 100
22
+ * Grace period (ms) added to blocking command timeouts. Only used when
23
+ * `blockingTimeout` is a positive number. Defaults to 100ms.
22
24
  */
23
25
  blockingTimeoutGrace?: number;
24
26
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ioredis",
3
- "version": "5.9.0",
3
+ "version": "5.9.1",
4
4
  "description": "A robust, performance-focused and full-featured Redis client for Node.js.",
5
5
  "main": "./built/index.js",
6
6
  "types": "./built/index.d.ts",