Repository navigation
feat: [redis] add optional redis PEL reading support - #2568
JonathanSerafini wants to merge 6 commits into
Conversation
|
@Lancetnik I was wondering if you might have an opinion on this implementation for reading of pending redis messages |
|
@JonathanSerafini sorry, I am nor sure, that I got your second idea correctly. Can you explain more detail, please? Probably, with pseudocode. But, I like your approach |
|
@Lancetnik Sure ... subscriber = broker.subscriber(stream=queue)
@subscriber
async def handler(msg): ...
async def pending_monitor(subscriber: StreamSubscriber, interval: float, idle: int) -> None:
while True:
# optionally get first xpending message
# optionally filter xpending by idle
# or optionally walk the list of xpending_range and delete those where delivery_attempts too high
if has_pending:
subscriber.read_pending = True
else:
subscriber.read_pending = False
await asyncio.sleep(interval)
subscriber.add_task(pending_monitor, subscriber, 15, 30 * 1000)above is pseudo-code ... it doesn't quite work with the declarative approach out of the box would be cleaner if it was either builtin support or subscribers had some sort of support for registering tasks as part of the decorator. |
Not "tasks" exactly, but we made smth close here - https://faststream.ag2.ai/latest/howto/nats/in-progress/ |
e320405 to
d47d4ff
Compare
8177181 to
2513f7d
Compare
By the looks of it, it does seem like a background task would be warranted given that it's more "subscriber lifecycle" than it is "message lifecycle" as it is for NATs. |
|
@JonathanSerafini hi! Sorry for waiting so long for this feature |
|
@Lancetnik It's up to you ... once this was merged i was going to have background tasks running to set the |
|
@JonathanSerafini do you see any difference between "background task" and "second subscriber in parallel"? For me they looks the same from event loop view - just an endless task, fetching data from stream time-to-time |
|
@Lancetnik ultimately it's a design decision that depends on how to envision users will typically use the subscriber. if you look at a Kafka stream, for instance, you have one subscriber that processes messages sequentially. Should a message fail to be ACKed, then the subscriber would retry the message before moving on to the next message. All of this occurs in the one subscriber you've defined. in Redis, this is slightly different since the pending message won't ever be retried by default since it's going to the PEL. So should we: For the background tasks this is what i was thinking of : async def monitor_pending(self) -> None:
"""Monitor the current worker for pending messages."""
interval = self.stream_sub.pending_interval or 60
while True:
if not self._client:
await asyncio.sleep(interval)
continue
response = await self.client.xpending_range(
name=self.stream_sub.name,
groupname=self.stream_sub.group,
consumername=self.stream_sub.consumer,
min="-",
max="+",
count=1,
idle=self.stream_sub.pending_min_idle,
)
if response:
self.resume_from("0-0")
await asyncio.sleep(interval)
async def monitor_claim(self) -> None:
"""Monitor the the stream for idle messages and reclaim them."""
interval = self.stream_sub.claim_interval or 60
while True:
if not self._client:
await asyncio.sleep(interval)
continue
await self.client.xautoclaim(
name=self.stream_sub.name,
groupname=self.stream_sub.group,
consumername=self.stream_sub.consumer,
min_idle_time=self.stream_sub.claim_min_idle,
count=self.stream_sub.claim_max_count,
justid=True
)
await asyncio.sleep(interval)
async def monitor_consumers(self) -> None:
"""Monitor the the stream for idle consumers and delete them.""" |
|
@JonathanSerafini fortunately #2607 closes both cases
@broker.subscriber("regular")
async def handler(): ...
@broker.subscriber("PEL")
async def retry_handler(): ...
# decorate the same handler to create 2 subscribers with same processing logic
@broker.subscriber("regular")
@broker.subscriber("PEL")
async def handler(): ... |
|
alrighty that works, i'll just combine those in my wrapper and call it a day. i am curious to see how xautoclaim works for messages from the same consumer, will need to test it. next pr will just fix the fact the redis subscriber implementation blocks the usage of |
Description
This change adds an initial implementation for an optional reading mode for the redis stream
subscriber whereby it will attempt to read messages from the PEL first and then continue on
reading new messages on the stream.
At present this mode is an optional and disable so as to not alter the existing behaviour.
On it's own, this change doesn't "do" much. However, by utilizing the
resume_frommethodit now becomes possible to periodically have the consumer switch from reading new messages
to reading from the PEL.
In a subsequent PR, i could add the resumable option to the subscriber settings and potentially
also add a background task to monitor the pending list to set resume_from to consume pending.
Type of change
Please delete options that are not relevant.
Checklist
just lintshows no errors)just test-coveragejust static-analysis