Fix async RedisCluster lifecycle after aclose#4069
Open
seplease wants to merge 4 commits into
Open
Conversation
|
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. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Reviewed by Cursor Bugbot for commit b922022. Configure here.
Collaborator
|
Hi @seplease, thank you for your contribution! I will have a look at it. |
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.

Summary
Fix async
RedisClusterreopening itself afteraclose().Previously, calling
await client.aclose()did not fully terminate the cluster client lifecycle. A later command could trigger lazy reinitialization and recreate cluster connections.This change separates "not initialized yet" from "explicitly closed" and prevents closed async cluster clients from being reused.
Fixes #3846.
Background
RedisClusteruses lazy initialization internally.The problem was that
_initializerepresented two different states at once:aclose()disconnected node connections, but also reset_initialize=True.As a result, a later command path treated the client as uninitialized and called
initialize()again, reopening cluster connections after close.That behavior made
aclose()act more like a soft reset than a terminal lifecycle operation.There was also a race window between
initialize()andaclose()where connections created during initialization could survive the close path.Changes
Introduced an explicit
_closedstate for the async cluster client lifecycle.Main changes:
_closedlifecycle tracking_ensure_open()checks before initialization and command executionaclose()to mark the client closed and prevent reuseaclose()wait for in-flight initialization before disconnecting nodesInternal cleanup paths are now split into:
aclose()_close_nodes()_disconnect_nodes()Cluster recovery paths (
MOVED,ClusterDownError,SlotNotCoveredError, and pipeline retry) now use_close_nodes()so topology refresh behavior remains unchanged.Tests
Added regression coverage for:
aclose()aclose()before initializationinitialize()/aclose()executionAlso added an integration-style regression test against a real Docker Redis Cluster.
Ran:
.venv/bin/python -m pytest tests/test_asyncio/test_cluster.py -q \ --redis-url=redis://localhost:16379/0 \ --redis-ssl-url=rediss://localhost:27379/0 \ -m 'not onlynoncluster and not redismod and not fixed_client'Result:
Also validated with:
Notes
This PR only changes the async cluster client lifecycle behavior.
The sync
RedisClusterimplementation is unchanged.Note
Medium Risk
Changes core async cluster client lifecycle and error-recovery disconnect paths; behavior shifts for code that called aclose() then reused the same instance or relied on implicit reopening.
Overview
Fixes async
RedisClustersoaclose()is terminal: a new_closedflag and_ensure_open()blockinitialize(),execute_command(), and pipeline paths after close, instead of treating a closed client as merely “uninitialized” and lazily reconnecting.Lifecycle split:
aclose()sets_closedand disconnects under the init lock (including waiting on in-flightinitialize());_close_nodes()/_disconnect_nodes()tear down node pools without marking the client closed—used for context-manager exit,MOVED/ cluster-down recovery, and pipeline retries so topology refresh still works.Tests & fixtures: Broad regression tests (reuse after close, concurrent init/close, pipelines, real cluster ping) and cluster teardown
flushdbwhen the fixture client is already closed.Reviewed by Cursor Bugbot for commit 00924b4. Bugbot is set up for automated code reviews on this repo. Configure here.