|
33 | 33 | NREVRANGE_CMD = "TS.NREVRANGE" |
34 | 34 | QUERYINDEX_CMD = "TS.QUERYINDEX" |
35 | 35 | RANGE_CMD = "TS.RANGE" |
| 36 | +READ_CMD = "TS.READ" |
36 | 37 | REVRANGE_CMD = "TS.REVRANGE" |
37 | 38 |
|
38 | 39 |
|
@@ -996,6 +997,93 @@ def revrange( |
996 | 997 | ) |
997 | 998 | return self.execute_command(REVRANGE_CMD, *params, keys=[key]) |
998 | 999 |
|
| 1000 | + @overload |
| 1001 | + def read( |
| 1002 | + self: SyncClientProtocol, |
| 1003 | + key: KeyT, |
| 1004 | + timestamp: int | str, |
| 1005 | + block_milliseconds: int | None = None, |
| 1006 | + block_min_count: int | None = None, |
| 1007 | + max_count: int | None = None, |
| 1008 | + ) -> TimeSeriesRangeResponse: ... |
| 1009 | + |
| 1010 | + @overload |
| 1011 | + def read( |
| 1012 | + self: AsyncClientProtocol, |
| 1013 | + key: KeyT, |
| 1014 | + timestamp: int | str, |
| 1015 | + block_milliseconds: int | None = None, |
| 1016 | + block_min_count: int | None = None, |
| 1017 | + max_count: int | None = None, |
| 1018 | + ) -> Awaitable[TimeSeriesRangeResponse]: ... |
| 1019 | + |
| 1020 | + def read( |
| 1021 | + self, |
| 1022 | + key: KeyT, |
| 1023 | + timestamp: int | str, |
| 1024 | + block_milliseconds: int | None = None, |
| 1025 | + block_min_count: int | None = None, |
| 1026 | + max_count: int | None = None, |
| 1027 | + ) -> TimeSeriesRangeResponse | Awaitable[TimeSeriesRangeResponse]: |
| 1028 | + """ |
| 1029 | + Read a batch of samples with timestamps at or after `timestamp`, in |
| 1030 | + ascending timestamp order. |
| 1031 | +
|
| 1032 | + Without blocking, returns immediately with whatever qualifies (possibly an |
| 1033 | + empty list). With the `block_milliseconds` group, waits until at least |
| 1034 | + `block_min_count` qualifying samples exist or until the timeout elapses. |
| 1035 | + This allows consuming historical and newly-appended samples continuously, |
| 1036 | + in batches, without polling `TS.RANGE`. |
| 1037 | +
|
| 1038 | + For more information see https://redis.io/commands/ts.read/ |
| 1039 | +
|
| 1040 | + Args: |
| 1041 | + key: |
| 1042 | + Key name for the time series (a regular series or a compaction |
| 1043 | + destination). |
| 1044 | + timestamp: |
| 1045 | + Inclusive cursor. Samples with `timestamp >= timestamp` qualify. |
| 1046 | + A non-negative integer (Unix milliseconds, `0` reads from the |
| 1047 | + beginning) or one of the sentinels `-` (earliest), `+` (latest |
| 1048 | + existing sample, inclusive) or `$` (only samples added after the |
| 1049 | + command is received). Sentinels are sent to the server as-is and |
| 1050 | + resolved server-side; `$` is only meaningful together with |
| 1051 | + `block_milliseconds`, since nothing can qualify at execution time |
| 1052 | + without blocking. |
| 1053 | + block_milliseconds: |
| 1054 | + Opt into blocking. Maximum time to wait, in whole milliseconds; |
| 1055 | + a non-negative integer where `0` means wait indefinitely. When |
| 1056 | + `None` (default) the command does not block. |
| 1057 | + block_min_count: |
| 1058 | + The unblock threshold: the call returns once this many samples |
| 1059 | + qualify. A positive integer, defaulting to `1` when blocking is |
| 1060 | + requested. Only used when `block_milliseconds` is set; the value is |
| 1061 | + always emitted on the wire inside the BLOCK group. |
| 1062 | + max_count: |
| 1063 | + Reply cap, a positive integer. When more samples qualify than |
| 1064 | + `max_count`, the oldest `max_count` are returned so callers can page |
| 1065 | + forward. `None` (default) means unlimited. |
| 1066 | +
|
| 1067 | + Returns: |
| 1068 | + A list of `[timestamp, value]` samples in ascending timestamp order. |
| 1069 | + An empty list is a successful reply (returned when nothing qualifies, |
| 1070 | + or when a blocking call times out with nothing available). |
| 1071 | +
|
| 1072 | + .. warning:: |
| 1073 | + A blocking call keeps the connection parked for up to |
| 1074 | + `block_milliseconds`. The client's `socket_timeout` still applies: with |
| 1075 | + the default (5 seconds) a longer block raises `TimeoutError` before the |
| 1076 | + server replies. When using `block_milliseconds`, configure the client |
| 1077 | + with a `socket_timeout` larger than the block window (or `None`), as with |
| 1078 | + other blocking commands. This command must not be retried automatically |
| 1079 | + after an empty or partial reply. |
| 1080 | + """ |
| 1081 | + params: list[EncodableT] = [key, timestamp] |
| 1082 | + self._append_block(params, block_milliseconds, block_min_count) |
| 1083 | + self._append_max_count(params, max_count) |
| 1084 | + |
| 1085 | + return self.execute_command(READ_CMD, *params, keys=[key]) |
| 1086 | + |
999 | 1087 | def __n_range_params( |
1000 | 1088 | self, |
1001 | 1089 | keys: List[KeyT], |
@@ -1785,6 +1873,35 @@ def _append_count(params: list[EncodableT], count: int | None): |
1785 | 1873 | if count is not None: |
1786 | 1874 | params.extend(["COUNT", count]) |
1787 | 1875 |
|
| 1876 | + @staticmethod |
| 1877 | + def _append_block( |
| 1878 | + params: list[EncodableT], |
| 1879 | + block_milliseconds: int | None, |
| 1880 | + block_min_count: int | None, |
| 1881 | + ): |
| 1882 | + """Append the BLOCK group to params. |
| 1883 | +
|
| 1884 | + The BLOCK group is all-or-nothing: when blocking is requested |
| 1885 | + (`block_milliseconds` is set), both `milliseconds` and `min_count` are |
| 1886 | + always emitted, with `min_count` defaulting to 1. There is no standalone |
| 1887 | + MIN_COUNT keyword in this command. |
| 1888 | + """ |
| 1889 | + if block_milliseconds is None: |
| 1890 | + if block_min_count is not None: |
| 1891 | + raise DataError( |
| 1892 | + "block_min_count requires block_milliseconds to be set; the " |
| 1893 | + "BLOCK group is all-or-nothing." |
| 1894 | + ) |
| 1895 | + return |
| 1896 | + min_count = 1 if block_min_count is None else block_min_count |
| 1897 | + params.extend(["BLOCK", block_milliseconds, min_count]) |
| 1898 | + |
| 1899 | + @staticmethod |
| 1900 | + def _append_max_count(params: list[EncodableT], max_count: int | None): |
| 1901 | + """Append MAX_COUNT property to params.""" |
| 1902 | + if max_count is not None: |
| 1903 | + params.extend(["MAX_COUNT", max_count]) |
| 1904 | + |
1788 | 1905 | @staticmethod |
1789 | 1906 | def _append_timestamp(params: list[EncodableT], timestamp: int | None): |
1790 | 1907 | """Append TIMESTAMP property to params.""" |
|
0 commit comments