feat: add TS.READ command support to the timeseries module - #4170
Merged
Merged
Conversation
elena-kolevska
approved these changes
Jul 8, 2026
5 tasks
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.
Change summary
This pull request adds support for the
TS.READcommand to the RedisTimeSeries client.TS.READreads a batch of samples with timestamps at or after a given cursor in ascending order, and optionally blocks until a minimum number of samples qualify — letting callers consume historical and newly-appended samples continuously, in batches, without pollingTS.RANGE.The core change is a new
read()method onTimeSeriesCommandsinredis/commands/timeseries/commands.py, exposed with sync/async@overloadsignatures. It acceptskey, an inclusivetimestampcursor (a non-negative integer in Unix milliseconds or one of the server-side sentinels-,+,$), and the optionalblock_milliseconds,block_min_count, andmax_countparameters. Two static helpers,_append_blockand_append_max_count, build the wire arguments;_append_blockenforces the command's all-or-nothing BLOCK group by raisingDataErrorwhenblock_min_countis supplied withoutblock_milliseconds, and defaultsmin_countto1when blocking is requested.Result parsing is wired up in
redis/commands/timeseries/__init__.py:READ_CMD("TS.READ") is registered toparse_range_unifiedfor both the RESP2 and RESP3 unified callback maps, so the command returns the same[[timestamp, value], ...]sample shape regardless of protocol. An empty list is a successful reply — returned when nothing qualifies or when a blocking call times out with nothing available.The change is additive and backward-compatible: it introduces a new public method with no changes to existing signatures, return types, or protocol handling. The docstring notes an important operational caveat — a blocking call parks the connection for up to
block_milliseconds, so the client'ssocket_timeoutmust be configured larger than the block window (orNone), and the command must not be retried automatically after an empty or partial reply.Test coverage
New tests are added in both
tests/test_timeseries.pyandtests/test_asyncio/test_timeseries.py, keeping sync/async parity. They cover the core read with an inclusive cursor,max_countbounded paging, the+and-sentinels, empty replies (cursor past the newest sample and a missing key), blocking behavior (immediate return whenmin_countis met, flush of available samples on timeout, and empty reply when nothing is available), and the validation path whereblock_min_countwithoutblock_millisecondsraisesDataError.Note
Low Risk
Additive public API only; blocking semantics mirror other blocking Redis commands and are documented for timeout/retry pitfalls.
Overview
Adds
TS.READto the RedisTimeSeries client via a newread()onTimeSeriesCommands, with sync/async overloads returning ascending[[timestamp, value], ...]batches from an inclusive cursor (ms or-/+/$).Wire args are built with new
_append_block(all-or-nothingBLOCK ms min_count, default min 1;DataErrorifblock_min_countis set withoutblock_milliseconds) and_append_max_count.READ_CMDis registered in the timeseries module init toparse_range_unifiedso replies match unified range shape across protocols.Sync and async tests (Redis ≥ 8.9.0) cover paging, sentinels, empty/missing keys, blocking behavior, and the BLOCK validation path. The docstring calls out
socket_timeoutvs long blocks and avoiding automatic retries after empty/partial blocking replies.Reviewed by Cursor Bugbot for commit 811d836. Bugbot is set up for automated code reviews on this repo. Configure here.