Skip to content

Bug: terminal errors in stream consumer restart in a hot loop instead of stopping #3106

Description

@kon28289

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions