Describe the bug
When the Redis stream consume loop raises a terminal error — one that re-running the read cannot fix — _consume re-raises it, the consume task dies, and TaskCallbackSupervisor (restart_on_failure=True by default) restarts it. The restarted task hits the same error on its first read, so it spins with no backoff. The subscriber's own message says "Stopping subscriber", but nothing actually stops.
Two known triggers, both raising from _consume:
How to reproduce
NOGROUP: start a normal consumer-group subscriber, then delete the stream from another connection while it runs.
import asyncio
from redis.asyncio import Redis
from faststream import FastStream
from faststream.redis import RedisBroker, StreamSub
broker = RedisBroker()
app = FastStream(broker)
@broker.subscriber(stream=StreamSub("s", group="g", consumer="c"))
async def handle(body: str) -> None: ...
@app.after_startup
async def drop_stream_soon() -> None:
async def _drop():
await asyncio.sleep(1)
r = Redis()
await r.delete("s")
await r.aclose()
asyncio.create_task(_drop())
Expected behavior
A terminal error should stop the subscriber for good, not be restarted. The read should not be retried when re-running it cannot succeed.
Observed behavior
Against Redis 7.4.10, the traceback is logged once right after the stream is deleted:
ERROR - Task-2 raised an exception, retrying...
Traceback (most recent call last):
File ".../faststream/redis/subscriber/usecases/stream_subscriber.py", line 108, in _consume
await self._get_msgs(*args)
File ".../faststream/redis/subscriber/usecases/stream_subscriber.py", line 446, in _get_msgs
for stream_name, msgs in await read(self.last_id):
File ".../faststream/redis/subscriber/usecases/stream_subscriber.py", line 373, in _xreadgroup
response = await self._client.xreadgroup(
...
redis.exceptions.ResponseError: NOGROUP No such key 's' or consumer group 'g' in XREADGROUP with GROUP option
After that only INFO-level restart lines remain, so at WARNING+ log levels the app looks idle while it hammers the server:
INFO - callback for Task-9 is being executed...
INFO - callback for Task-10 is being executed...
INFO - callback for Task-11 is being executed...
INFO - callback for Task-12 is being executed...
Server-side MONITOR — PING + XREADGROUP repeating every ~0.5 ms with no backoff:
1788852995.863508 "XREADGROUP" "GROUP" "g" "c" "BLOCK" "100" "STREAMS" "s" ">"
1788852995.864748 "XREADGROUP" "GROUP" "g" "c" "BLOCK" "100" "STREAMS" "s" ">"
1788852995.865755 "XREADGROUP" "GROUP" "g" "c" "BLOCK" "100" "STREAMS" "s" ">"
In a ~5s window after deletion: 8,290 supervisor restarts and 8,288 XREADGROUP reaching the server (~1,650/s). The StreamClaimUnsupportedError path behaves identically (~13k restarts / 6.3s in the same setup).
Screenshots
Environment
Running FastStream 0.7.5 with CPython 3.13.14 on Darwin
Additional context
Raised as a follow-up from the #3049 review (thanks @borisalekseev for flagging the claim case). Happy to take this and open a PR.
Describe the bug
When the Redis stream consume loop raises a terminal error — one that re-running the read cannot fix — _consume re-raises it, the consume task dies, and TaskCallbackSupervisor (restart_on_failure=True by default) restarts it. The restarted task hits the same error on its first read, so it spins with no backoff. The subscriber's own message says "Stopping subscriber", but nothing actually stops.
Two known triggers, both raising from _consume:
How to reproduce
NOGROUP: start a normal consumer-group subscriber, then delete the stream from another connection while it runs.
Expected behavior
A terminal error should stop the subscriber for good, not be restarted. The read should not be retried when re-running it cannot succeed.
Observed behavior
Against Redis 7.4.10, the traceback is logged once right after the stream is deleted:
After that only INFO-level restart lines remain, so at WARNING+ log levels the app looks idle while it hammers the server:
Server-side MONITOR — PING + XREADGROUP repeating every ~0.5 ms with no backoff:
In a ~5s window after deletion: 8,290 supervisor restarts and 8,288 XREADGROUP reaching the server (~1,650/s). The StreamClaimUnsupportedError path behaves identically (~13k restarts / 6.3s in the same setup).
Screenshots
Environment
Running FastStream 0.7.5 with CPython 3.13.14 on Darwin
Additional context
Raised as a follow-up from the #3049 review (thanks @borisalekseev for flagging the claim case). Happy to take this and open a PR.