Repository navigation
fix(client): replace pool members whose reconnect strategy gave up - #3525
Merged
nkaradzhov merged 1 commit intoOct 21, 2026
Merged
Conversation
A pool member whose reconnect strategy gives up ('terminated') is closed for
good, but the pool kept handing it out: every task on it rejected with
ClientClosedError, tasks queued behind it were routed to it, and close()
rejected, until the pool was recreated.
Mark such members on 'terminated' and destroy them instead of reusing them.
Tasks queued behind one get a replacement client; if the replacement cannot
connect and no other client is in use, they are rejected with the connect
error, as #create already does while closing.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
When a pool member's
reconnectStrategygives up, the member emits'terminated'and is closed for good, but the pool keeps handing it out. Every task on it rejects withClientClosedError, tasks queued behind it are routed to it,pool.connect()is a no-op andpool.close()rejects, so only a new pool recovers. WithsocketTimeoutand the default strategy this hits idle members after any quiet period longer than the timeout.Redis 8, built dist of each side:
The pool now marks a member on
'terminated'(a WeakSet next to#abandoned) and destroys it instead of reusing it. Tasks queued behind it get a replacement, and if that can't connect while nothing else is in use, they get the connect error through the rule#createalready applies while closing. I used'terminated'rather than!isOpenso a member you destroyed yourself still makesclose()reject (#3437's test).execute()dispatches the task again after dropping a dead idle member, since a reassignedletloses its narrowing in thepublish()callback on TS 5.3.Not changed: a task queued at
maximumbehind a failed scale-up connect still waits foracquireTimeout. That happens on master without a dead member too, so I'd keep it for a separate PR.Added 5 cases to pool.spec.ts against a real server with
CLIENT KILL, they fail on master with "The client is closed". pool.spec.ts and the other specs that import pool.ts pass on Linux, tsc and eslint are clean.Checklist
npm testpass with this change (including linting)?Note
Medium Risk
Changes core pool lifecycle (acquire, return, close, and scale-up) when connections are permanently terminated; behavior is covered by new integration tests but affects all pooled Redis usage after reconnect exhaustion.
Overview
Fixes Redis client pool behavior when a member’s reconnect strategy gives up: the pool no longer reuses that connection, so work stops failing with
ClientClosedErrorandclose()can complete normally.The pool tracks permanently dead members in a
#terminatedWeakSet (on the client’s'terminated'event).execute()drops terminated idle clients and retries the task;#returnClient()destroys them after use and starts a replacement when tasks are waiting;#create(true)rejects queued work if replacement connect fails with no other clients (same idea as during close).close()destroys terminated idle members instead of callingclose()on them.Five integration tests use
CLIENT KILLwithreconnectStrategy: falseto cover idle replacement, queued tasks, failed replacement, and close paths.Reviewed by Cursor Bugbot for commit b524c64. Bugbot is set up for automated code reviews on this repo. Configure here.