1+ import asyncio
12import math
2- from collections .abc import AsyncIterator , Awaitable , Callable , Coroutine
3+ from collections .abc import AsyncIterator , Awaitable , Callable
34from typing import TYPE_CHECKING , Any , Optional , TypeAlias
45
56from redis .exceptions import ResponseError
3435TopicName : TypeAlias = bytes
3536Offset : TypeAlias = bytes
3637
38+ ReadResponse = tuple [
39+ tuple [
40+ TopicName ,
41+ tuple [
42+ tuple [
43+ Offset ,
44+ dict [bytes , bytes ],
45+ ],
46+ ...,
47+ ],
48+ ],
49+ ...,
50+ ]
51+ ReadCallable = Callable [[str ], Awaitable [ReadResponse ]]
52+
3753
3854class _StreamHandlerMixin (LogicSubscriber ):
3955 def __init__ (
@@ -80,24 +96,7 @@ async def start(self) -> None:
8096
8197 stream = self .stream_sub
8298
83- read : Callable [
84- [str ],
85- Awaitable [
86- tuple [
87- tuple [
88- TopicName ,
89- tuple [
90- tuple [
91- Offset ,
92- dict [bytes , bytes ],
93- ],
94- ...,
95- ],
96- ],
97- ...,
98- ],
99- ],
100- ]
99+ read : ReadCallable
101100
102101 if stream .group and stream .consumer :
103102 group_create_id = "$" if self .last_id == ">" else self .last_id
@@ -112,81 +111,23 @@ async def start(self) -> None:
112111 if "already exists" not in str (e ):
113112 raise
114113
115- def read (
116- _ : str ,
117- ) -> Awaitable [
118- tuple [
119- tuple [
120- TopicName ,
121- tuple [
122- tuple [
123- Offset ,
124- dict [bytes , bytes ],
125- ],
126- ...,
127- ],
128- ],
129- ...,
130- ],
131- ]:
132- return client .xreadgroup (
133- groupname = stream .group ,
134- consumername = stream .consumer ,
135- streams = {stream .name : stream .last_id },
136- count = stream .max_records ,
137- block = stream .polling_interval ,
138- noack = stream .no_ack ,
139- )
140-
141- elif self .stream_sub .min_idle_time is None :
142-
143- def read (
144- last_id : str ,
145- ) -> Awaitable [
146- tuple [
147- tuple [
148- TopicName ,
149- tuple [
150- tuple [
151- Offset ,
152- dict [bytes , bytes ],
153- ],
154- ...,
155- ],
156- ],
157- ...,
158- ],
159- ]:
160- return client .xread (
161- {stream .name : last_id },
162- block = stream .polling_interval ,
163- count = stream .max_records ,
164- )
114+ if stream .min_idle_time is None :
115+
116+ def read (
117+ _ : str ,
118+ ) -> Awaitable [ReadResponse ]:
119+ return client .xreadgroup (
120+ groupname = stream .group ,
121+ consumername = stream .consumer ,
122+ streams = {stream .name : stream .last_id },
123+ count = stream .max_records ,
124+ block = stream .polling_interval ,
125+ noack = stream .no_ack ,
126+ )
165127
166- else :
128+ else :
167129
168- def read (
169- _ : str ,
170- ) -> Coroutine [
171- Any ,
172- Any ,
173- tuple [
174- tuple [
175- TopicName ,
176- tuple [
177- tuple [
178- Offset ,
179- dict [bytes , bytes ],
180- ],
181- ...,
182- ],
183- ],
184- ...,
185- ],
186- ]:
187- async def xautoclaim () -> tuple [
188- tuple [TopicName , tuple [tuple [Offset , dict [bytes , bytes ]], ...]], ...
189- ]:
130+ async def read (_ : str ) -> ReadResponse :
190131 stream_message = await client .xautoclaim (
191132 name = self .stream_sub .name ,
192133 groupname = self .stream_sub .group ,
@@ -197,13 +138,26 @@ async def xautoclaim() -> tuple[
197138 )
198139 stream_name = self .stream_sub .name .encode ()
199140 (next_id , messages , _ ) = stream_message
141+
200142 # Update start_id for next call
201143 self .autoclaim_start_id = next_id
202- if not messages :
144+
145+ if next_id == b"0-0" and not messages :
146+ await asyncio .sleep (stream .polling_interval / 1000 ) # ms to s
203147 return ()
148+
204149 return ((stream_name , messages ),)
205150
206- return xautoclaim ()
151+ else :
152+
153+ def read (
154+ last_id : str ,
155+ ) -> Awaitable [ReadResponse ]:
156+ return client .xread (
157+ {stream .name : last_id },
158+ block = stream .polling_interval ,
159+ count = stream .max_records ,
160+ )
207161
208162 await super ().start (read )
209163
0 commit comments