fix(prefect-redis): make socket timeouts reachable and publisher/reconnect safe - #23015
Open
Adisa-Shobi wants to merge 11 commits into
Open
fix(prefect-redis): make socket timeouts reachable and publisher/reconnect safe#23015Adisa-Shobi wants to merge 11 commits into
Adisa-Shobi wants to merge 11 commits into
Conversation
clear_cached_clients() was async but awaited nothing, evicting cached clients without closing their connection pools. A connection stuck in-use from a hung operation (e.g. blocked XREADGROUP during an outage) leaked forever, eventually exhausting the pool's connection cap even after Redis recovered. Closes PrefectHQ#22478
Adisa-Shobi
requested review from
chrisguidry,
desertaxle and
zzstoatzz
as code owners
September 2, 2026 15:30
…t precedence Isolates the batch under flush before the deduplication await so a publish landing mid-flush survives instead of being cleared. Also accounts for a socket_timeout set via the Redis URL query, which redis-py gives precedence over keyword arguments, when deriving a consumer's blocking-read timeout.
…writes are recoverable
…m each other's markers
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
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.
closes #22478
clear_cached_clients()evicted cached Redis clients from the dict without closing them, so a connection stuck in-use from a hung operation (e.g. a blockedXREADGROUPduring an outage) leaked forever, eventually exhausting the pool's connection cap even after Redis recovered. This also fixes the two things that keep that recovery path from actually firing and from losing data once it does.Details
clear_cached_clients()now awaitsclient.aclose()for each evicted client instead of just dropping the cache entry, and accepts an optionalclient=argument soConsumer.runretires only the client that failed instead of force-closing every current-loop client (including healthy ones for other endpoints).socket_timeout/socket_connect_timeoutdefault to60.0/10.0instead ofNone. WithNone, a dead socket never raises, soConsumer.run'sexcept RedisErrorreconnect branch — the thing this PR fixes — is never reached in the first place.Publisher._publish_periodically()now survives a flush failure instead of dying silently, and_publish_current_batch()re-queues the unsent tail of a batch instead of dropping it. Without this, a finite timeout turns a hang into silent data loss on the next sustained outage.Known and deliberate, left out of scope:
RedisWorkerCleanupQueueSettings(cleanup_queue.py) has the samesocket_timeout=Nonedefault and the same latent hang, minus the amplifier — it polls rather than blocking. Separate settings class, separate client path that never touches_client_cache.get_async_redis_client()passes no arguments, so today all cached clients share one cache key and per-client targeting is a no-op in practice. It matters once a caller passes an expliciturl=/host=.Validation:
PYTHONPATH=src/integrations/prefect-redis .venv/bin/pytest src/integrations/prefect-redis/tests/test_client.py src/integrations/prefect-redis/tests/test_messaging.pyPYTHONPATH=src/integrations/prefect-redis .venv/bin/ruff check src/integrations/prefect-redis/prefect_redis/ src/integrations/prefect-redis/tests/PYTHONPATH=src/integrations/prefect-redis .venv/bin/mypy src/integrations/prefect-redis/prefect_redis/client.py src/integrations/prefect-redis/prefect_redis/messaging.pylocalhost:6379.Checklist
#22478"mint.json.