Skip to content

Fix PubSub timeout propagation to prevent indefinite hangs on socket read operations#3982

Merged
petyaslavova merged 7 commits intomasterfrom
ps_fix_read_response_timeout_for_pubsub
Mar 10, 2026
Merged

Fix PubSub timeout propagation to prevent indefinite hangs on socket read operations#3982
petyaslavova merged 7 commits intomasterfrom
ps_fix_read_response_timeout_for_pubsub

Conversation

@petyaslavova
Copy link
Copy Markdown
Collaborator

@petyaslavova petyaslavova commented Feb 27, 2026

🛠 Fix: Prevent Indefinite Hang in PubSub.get_message() with Timeouts

🚨 Problem

This PR fixes a critical hang issue in PubSub.get_message() when using timeouts.

Previously:

  • can_read() correctly respected the provided timeout.
  • However, the subsequent read_response() call did not receive the timeout.
  • As a result, it fell back to the default socket timeout (often infinite).

❗ Consequence

This created a scenario where:

  1. can_read() reported that data was available.
  2. read_response() attempted to read from the socket.
  3. The socket read blocked indefinitely (e.g., due to partial data or connection state changes).

This resulted in unexpected and indefinite hangs.


✅ Solution

This PR propagates an optional timeout parameter through the entire call chain:
get_message()
→ read_response()
→ parser layer
→ connection layer
→ socket-level recv()

Coverage Includes

  • All parser implementations

    • Hiredis
    • RESP2
    • RESP3
  • All connection types

    • Standard connections
    • Sentinel-managed connections
    • Cache-proxy connections
  • Both execution models

    • Synchronous
    • Asynchronous

The timeout is now consistently respected down to the lowest-level socket recv() call.


🧪 Test Coverage

This PR adds comprehensive test coverage (887 lines) validating timeout behavior across:

  • Cluster sharded Pub/Sub
  • Pattern subscriptions
  • Standard Pub/Sub flows
  • Edge cases:
    • timeout=0
    • timeout=None

All relevant sync and async paths are covered.


🎯 Result

PubSub.get_message() now behaves correctly and predictably when timeouts are used, eliminating the risk of indefinite blocking under partial-read or connection edge cases.


Note

Medium Risk
Touches core response parsing and socket read paths (RESP2/RESP3/Hiredis and Connection.read_response), so mistakes could cause new timeouts or regressions in message parsing under load. Added/updated tests reduce risk, but behavior changes affect all PubSub and push-response reads.

Overview
Fixes Pub/Sub timeouts so get_message(timeout=...) can’t hang indefinitely by threading an explicit timeout value through PubSub.parse_response()Connection.read_response() → parser implementations → SocketBuffer socket reads.

Adds a timeout parameter (defaulting to SENTINEL to preserve existing socket timeout behavior) across RESP2/RESP3/Hiredis parsers, SocketBuffer.read/readline, SentinelManagedConnection, and cache proxy connections, and propagates the timeout in cluster sharded Pub/Sub message polling.

Expands test coverage with new sync/async PubSub and cluster sharded PubSub timeout-propagation tests, plus unit tests verifying timeout passthrough for proxy and sentinel connections.

Written by Cursor Bugbot for commit 32c19a0. This will update automatically on new commits. Configure here.

…opagated down to the socket read. This improves pubsub behaviour - now the timeout is applied when waiting for messages.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a critical bug in Redis PubSub where get_message(timeout=X) could hang indefinitely. The issue was that while can_read() respected the timeout, the subsequent read_response() call did not receive it, causing indefinite blocking on socket reads when partial data or connection issues occurred.

Changes:

  • Added timeout parameter propagation through the entire sync read chain: get_message()parse_response()read_response() → parser methods → socket buffer operations
  • Moved SENTINEL constant from redis/connection.py to redis/_parsers/socket.py for broader accessibility
  • Added comprehensive test coverage (887 lines) validating timeout behavior across all PubSub variants and edge cases

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated no comments.

Show a summary per file
File Description
redis/_parsers/socket.py Added timeout parameter to read() and readline() methods; made SENTINEL constant available for import
redis/_parsers/resp2.py Added timeout parameter to sync parser's read_response() and _read_response() methods
redis/_parsers/resp3.py Added timeout parameter to sync parser's read_response() and _read_response() methods
redis/_parsers/hiredis.py Added timeout parameter to read_response() method and propagated to read_from_socket()
redis/connection.py Added timeout parameter to abstract and concrete read_response() methods; moved SENTINEL to _parsers.socket
redis/sentinel.py Added timeout parameter to SentinelManagedConnection.read_response()
redis/client.py Updated parse_response() to properly propagate timeout to read_response(); added comprehensive docstring
redis/asyncio/client.py Added comprehensive docstring to parse_response() (async already had timeout support)
redis/cluster.py Updated _sharded_message_generator() to accept and propagate timeout parameter
tests/test_sentinel_managed_connection.py Added 107 lines of tests for timeout parameter propagation in SentinelManagedConnection
tests/test_pubsub.py Added 280 lines of sync PubSub timeout tests covering standard and cluster scenarios
tests/test_connection.py Added 115 lines of tests for CacheProxyConnection timeout parameter propagation
tests/test_asyncio/test_pubsub.py Added 192 lines of async PubSub timeout tests

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@jit-ci
Copy link
Copy Markdown

jit-ci Bot commented Feb 27, 2026

🛡️ Jit Security Scan Results

CRITICAL HIGH MEDIUM

✅ No security findings were detected in this PR


Security scan by Jit

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Comment thread redis/_parsers/hiredis.py
Comment thread redis/_parsers/resp3.py
Comment thread redis/_parsers/hiredis.py
@petyaslavova petyaslavova merged commit 53e6d8a into master Mar 10, 2026
120 of 122 checks passed
@petyaslavova petyaslavova deleted the ps_fix_read_response_timeout_for_pubsub branch March 10, 2026 13:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breakingchange API or Breaking Change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants