Skip to content

[#3127] Fix for Redis Sentinel client doesn't reconnect after connection failure#3184

Open
mayankkapoor wants to merge 2 commits intoredis:masterfrom
mayankkapoor:fix/3127-client-doesnt-reconnect
Open

[#3127] Fix for Redis Sentinel client doesn't reconnect after connection failure#3184
mayankkapoor wants to merge 2 commits intoredis:masterfrom
mayankkapoor:fix/3127-client-doesnt-reconnect

Conversation

@mayankkapoor
Copy link

@mayankkapoor mayankkapoor commented Feb 24, 2026

Description

When a Redis Sentinel goes down and comes back up, the node-redis client fails to reconnect, throwing "None of the sentinels are available". It seems this is a regression introduced in v5.9.0 (commit 73413e0 — "fix: resolve doubly linked list push issue (#3085)").

Root Cause: #handleSentinelFailure() (added in v5.9.0) permanently removes the sentinel node from #sentinelRootNodes via splice() when its connection drops. When #reset() then calls observe(), the sentinel list is empty (or missing that node), so reconnection is impossible.

Secondary issue: The sentinel list update in transform() (line 1350) compares only array lengths (analyzed.sentinelList.length != this.#sentinelRootNodes.length), so even if reconnection succeeds via another sentinel, a removed node may never be restored if the list sizes happen to match.

Fix:

  1. Stop removing sentinels from #sentinelRootNodes in #handleSentinelFailure — just call #reset() so reconnection is retried against all known sentinels.
  2. Fix the sentinel list update check: the old code only compared array lengths, so it missed cases where the list contents changed but the length stayed the same. Replaced with a proper element-wise comparison (areSentinelListsEqual).

Fixes #3127

Checklist

  • Does npm test pass with this change (including linting)?
  • Is the new or changed code fully tested?
  • Is a documentation update included (if this change modifies existing APIs, or introduces new ones)? N/A no API changes, this is a bug fix for internal reconnection logic.

…ent fails to reconnect, throwing `"None of the sentinels are available"`. This is a regression introduced in v5.9.0 (commit `73413e086c` — "fix: resolve doubly linked list push issue (redis#3085)").

**Root Cause:** `#handleSentinelFailure()` (added in v5.9.0) permanently removes the sentinel node from `#sentinelRootNodes` via `splice()` when its connection drops. When `#reset()` then calls `observe()`, the sentinel list is empty (or missing that node), so reconnection is impossible.

**Secondary issue:** The sentinel list update in `transform()` (line 1350) compares only array lengths (`analyzed.sentinelList.length != this.#sentinelRootNodes.length`), so even if reconnection succeeds via another sentinel, a removed node may never be restored if the list sizes happen to match.

- [ ] **Task 1:** Fix `#handleSentinelFailure` — stop permanently removing sentinel nodes
  - **File:** `packages/client/lib/sentinel/index.ts` (lines 934–942)
  - Remove the `splice()` call. A transient TCP disconnect (which happens during sentinel restart) should not permanently delete the node from the root list. Just call `#reset()` to trigger reconnection, matching v5.8.3 behavior.
  - Before (broken):
    ```typescript
    #handleSentinelFailure(node: RedisNode) {
        const found = this.#sentinelRootNodes.findIndex(
            (rootNode) => rootNode.host === node.host && rootNode.port === node.port
        );
        if (found !== -1) {
            this.#sentinelRootNodes.splice(found, 1);
        }
        this.#reset();
    }
    ```
  - After (fixed):
    ```typescript
    #handleSentinelFailure(node: RedisNode) {
        this.#reset();
    }
    ```

- [ ] **Task 2:** Fix sentinel list update condition in `transform()`
  - **File:** `packages/client/lib/sentinel/index.ts` (line 1350)
  - Replace the length-only comparison with a proper content comparison so that nodes removed or added are always detected.
  - Before:
    ```typescript
    if (analyzed.sentinelList.length != this.#sentinelRootNodes.length) {
    ```
  - After — compare actual content:
    ```typescript
    if (!areSentinelListsEqual(analyzed.sentinelList, this.#sentinelRootNodes)) {
    ```
  - Add a helper (either inline or as a small private method) that compares host+port of each node, not just array length.

- [ ] **Task 3:** Run existing tests to confirm no regressions
  - Run `npm test` in the `packages/client` directory
  - Look for and run any sentinel-specific test files

- [ ] **Task 4:** Verify the fix scenario
  - Confirm `#sentinelRootNodes` retains the original sentinel entry after disconnect
  - Confirm `observe()` retries that sentinel and succeeds once it's back online
  - Confirm no `"None of the sentinels are available"` error after sentinel restart
@jit-ci
Copy link

jit-ci bot commented Feb 24, 2026

Hi, I’m Jit, a friendly security platform designed to help developers build secure applications from day zero with an MVS (Minimal viable security) mindset.

In case there are security findings, they will be communicated to you as a comment inside the PR.

Hope you’ll enjoy using Jit.

Questions? Comments? Want to learn more? Get in touch with us.

@mayankkapoor mayankkapoor changed the title Fix for Redis Sentinel client doesn't reconnect after connection failure #3127 [#3127] Fix for Redis Sentinel client doesn't reconnect after connection failure Feb 24, 2026
@mayankkapoor mayankkapoor marked this pull request as draft February 24, 2026 12:37
Exports areSentinelListsEqual and adds tests to prevent the regression
where sentinel nodes were removed from the root list on failure, and
where sentinel list changes were only detected by length, not content.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@mayankkapoor mayankkapoor marked this pull request as ready for review February 24, 2026 13:50
@nkaradzhov
Copy link
Collaborator

@mayankkapoor thanks for the PR, I will have a look

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Redis Sentinel client doesn't reconnect after connection failure

2 participants