You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Нет возможности выявить некорректное состояние канала #722
Если RabbitMQ кластеризованный и происходит перезапуск ноды, на которой существует очередь и к ней подключен клиент, то клиент никак не сможет определить, что нужно пересоздать канал и собственно не сможет получать сообщения. TCP соединение живёт, keep alive не настроить, канал тоже живёт по факту. Функции по закрытию канала не срабатывают, состояние канала не актуализируется. Есть ли способ как-то определить перезапуск ноды и невалидное состояние канала?
The logs show an error like: "WARNING: 2026-10-07 06:27:31 aiormq.channel: Consumer 'ctag1.f7fae0eedb4149f18875ed493572036d' cancelled by the broker on <Channel: "1" 0x7fab77db9e50 at>".
However, close_callbacks are not triggered. Querying RabbitMQ during a node restart doesn't help either; for instance, when running:
@mosquito since you're thinking about options: I reproduced this on a local three-node cluster and tried a couple of fixes. Same root cause as #334 and #390.
When it happens. The queue is a classic queue whose home node is not the node the client is connected to. When that node goes down (rolling restart, Kubernetes node maintenance), the broker sends basic.cancel. The connection and the channel stay open, so RobustConnection / RobustChannel have nothing to recover, and the consumer is gone until the process restarts. Quorum queues are not affected: the leader moves and the consumer stays.
RabbitMQ 4.0, client on node 1, classic queue declared via node 2, docker stop node 2, then start it again. aio-pika 10.1.0 / aiormq 7.2.1 (9.6.2 / 6.9.4 behaves the same, just without the warning):
classic: before consumers=1
aiormq.channel: Consumer 'ctag1.c9ea17bd1fbc4992880c83e8b35934ad' cancelled by the broker on <Channel: "1" at 0x10951e410>
classic: node 2 down consumers=0 connection_closed=False channel_closed=False
classic: node 2 back consumers=0 delivered=[]
Calling restore() straight from the cancel callback makes it worse. While node 2 is down, the re-declare gets 404 NOT_FOUND ... process is stopped by supervisor and the broker closes the channel. RobustChannel._on_close tries restore() once, it fails with the same 404, and nothing tries again. After node 2 is back the channel is still closed: publish raises ChannelInvalidStateError: ... closed and queue.restore() raises Channel was not opened, while the connection looks healthy. @lutermaks, if your callback re-declares right away, you will probably hit this once the node stays down for more than a moment.
What worked. On cancel, retry with a pause: declare the queue on a short-lived separate channel, close it, and only after that succeeds call restore() on the real channel. While the node is down only the throwaway channels fail. With 20 classic queues on the stopped node, all 20 consumers came back, messages were delivered, and the main channel stayed open.
Proposal. I'd be glad to send a PR along these lines if the direction works for you: RobustChannel subscribes to the underlay channel's on_consumer_cancel_callbacks (re-registered on every reopen), and one background loop per connection probes and re-consumes only the cancelled tags, stopping when the channel or the connection closes, in line with #717. One loop rather than one per queue, because a node failure cancels every classic queue on it at once, and per-queue loops would be the channel open/close churn from mosquito/aiormq#263 (I couldn't trigger it with 20 queues, but it is cheap to avoid). The hook is aiormq's on_consumer_cancel_callbacks (mosquito/aiormq#238), which the current aiormq>=7.1.3 floor already includes.
One question before I start: if an operator deleted the queue (no node failure), should it come back? A regular declare in the probe would recreate it; a passive one would wait until someone declares it again. For the node-down case a regular declare is harmless: in my test (durable classic queues, RabbitMQ 4.0.9, node stopped with docker stop) it got 404 NOT_FOUND and did not create the queue on another node. I haven't tried a hard kill or a partition.
For the test suite: deleting the queue from a second channel produces the same basic.cancel, so recovery can be covered on a single broker. The cluster case stays a manual repro.
cluster.sh, repro.py, naive_restore.py and its output
before consumers=1
aiormq.channel: Consumer 'ctag1.1d56c9d693c048fd8d72c7d084ab4c12' cancelled by the broker on <Channel: "1" at 0x10a86e410>
basic.cancel received -> scheduling queue.restore()
aio_pika.tools: Callback <OneShotCallback: cb=<bound method RobustChannel._on_close of <aio_pika.robust_channel.RobustChannel object at 0x10a8d52b0>>> error: ChannelNotFoundEntity: NOT_FOUND - queue 'basic-cancel-naive-restore' in vhost '/' process is stopped by supervisor
node 2 down consumers=0 connection_closed=False channel_closed=True
immediate restore: ["ChannelNotFoundEntity: NOT_FOUND - queue 'basic-cancel-naive-restore' in vhost '/' process is stopped by supervisor"]
publish while down: ChannelInvalidStateError: <aio_pika.robust_channel.RobustChannel object at 0x10a8d52b0> closed
node 2 back consumers=0 connection_closed=False channel_closed=True
publish after back: ChannelInvalidStateError: <aio_pika.robust_channel.RobustChannel object at 0x10a8d52b0> closed
explicit queue.restore(): ChannelInvalidStateError: Channel was not opened
delivered=[]
Yes, I'm facing the exact same issue. In the end, I subscribed to on_consumer_cancel_callbacks and implemented a retry loop to re-register the consumer while the node is restarting. If exceptions like ChannelNotFoundEntity or ChannelInvalidStateError occur, I add a delay and try to register again
@mosquito thanks for #723! Re-ran my cluster repro on aio-pika 10.1.1 / aiormq 7.2.2 (RabbitMQ 4.0, classic queue homed on node 2, client on node 1, docker stop / docker start node 2):
classic: before consumers=1
aiormq.channel: Consumer 'ctag1.25097247554047719f59ba15465d6b42' cancelled by the broker on <Channel: "1" at 0x107037330>
classic: node 2 down consumers=0 connection_closed=False channel_closed=False
classic: node 2 back consumers=1 delivered=[b'after']
With 20 classic queues on the stopped node and all consumers on one channel, all 20 came back about 3 s after the node was up, each got its message, and the channel stayed open. Quorum queues behave as before.
Activity
mosquito commentedon Oct 6, 2026
Please double check the latest aiormq and aio-pika, this behavior should be resolved at all.
lutermaks commentedon Oct 7, 2026
The logs show an error like: "WARNING: 2026-10-07 06:27:31 aiormq.channel: Consumer 'ctag1.f7fae0eedb4149f18875ed493572036d' cancelled by the broker on <Channel: "1" 0x7fab77db9e50 at>".
However, close_callbacks are not triggered. Querying RabbitMQ during a node restart doesn't help either; for instance, when running:
declare_ok = await self._channel.declare_queue(
name=queue_name,
passive=True,
timeout=2
)
the old tag still remains in _consumers.
Versions: aio_pika = 10.1.0, aiormq = 7.2.1
lutermaks commentedon Oct 7, 2026
Using on_consumer_cancel_callbacks at the aiormq level fixed the issue
mosquito commentedon Oct 7, 2026
Good to know, I will think about options how can it might been fixed
lutermaks commentedon Oct 7, 2026
OK, thank you
SaInekK commentedon Oct 7, 2026
@mosquito since you're thinking about options: I reproduced this on a local three-node cluster and tried a couple of fixes. Same root cause as #334 and #390.
When it happens. The queue is a classic queue whose home node is not the node the client is connected to. When that node goes down (rolling restart, Kubernetes node maintenance), the broker sends
basic.cancel. The connection and the channel stay open, soRobustConnection/RobustChannelhave nothing to recover, and the consumer is gone until the process restarts. Quorum queues are not affected: the leader moves and the consumer stays.RabbitMQ 4.0, client on node 1, classic queue declared via node 2,
docker stopnode 2, then start it again. aio-pika 10.1.0 / aiormq 7.2.1 (9.6.2 / 6.9.4 behaves the same, just without the warning):Calling
restore()straight from the cancel callback makes it worse. While node 2 is down, the re-declare gets404 NOT_FOUND ... process is stopped by supervisorand the broker closes the channel.RobustChannel._on_closetriesrestore()once, it fails with the same 404, and nothing tries again. After node 2 is back the channel is still closed: publish raisesChannelInvalidStateError: ... closedandqueue.restore()raisesChannel was not opened, while the connection looks healthy. @lutermaks, if your callback re-declares right away, you will probably hit this once the node stays down for more than a moment.What worked. On cancel, retry with a pause: declare the queue on a short-lived separate channel, close it, and only after that succeeds call
restore()on the real channel. While the node is down only the throwaway channels fail. With 20 classic queues on the stopped node, all 20 consumers came back, messages were delivered, and the main channel stayed open.Proposal. I'd be glad to send a PR along these lines if the direction works for you:
RobustChannelsubscribes to the underlay channel'son_consumer_cancel_callbacks(re-registered on every reopen), and one background loop per connection probes and re-consumes only the cancelled tags, stopping when the channel or the connection closes, in line with #717. One loop rather than one per queue, because a node failure cancels every classic queue on it at once, and per-queue loops would be the channel open/close churn from mosquito/aiormq#263 (I couldn't trigger it with 20 queues, but it is cheap to avoid). The hook is aiormq'son_consumer_cancel_callbacks(mosquito/aiormq#238), which the currentaiormq>=7.1.3floor already includes.One question before I start: if an operator deleted the queue (no node failure), should it come back? A regular declare in the probe would recreate it; a passive one would wait until someone declares it again. For the node-down case a regular declare is harmless: in my test (durable classic queues, RabbitMQ 4.0.9, node stopped with
docker stop) it got404 NOT_FOUNDand did not create the queue on another node. I haven't tried a hard kill or a partition.For the test suite: deleting the queue from a second channel produces the same
basic.cancel, so recovery can be covered on a single broker. The cluster case stays a manual repro.cluster.sh, repro.py, naive_restore.py and its output
lutermaks commentedon Oct 8, 2026
Yes, I'm facing the exact same issue. In the end, I subscribed to on_consumer_cancel_callbacks and implemented a retry loop to re-register the consumer while the node is restarting. If exceptions like ChannelNotFoundEntity or ChannelInvalidStateError occur, I add a delay and try to register again
Restore robust consumers after broker cancellation
Restore robust consumers after broker cancellation
SaInekK commentedon Oct 12, 2026
@mosquito thanks for #723! Re-ran my cluster repro on aio-pika 10.1.1 / aiormq 7.2.2 (RabbitMQ 4.0, classic queue homed on node 2, client on node 1,
docker stop/docker startnode 2):With 20 classic queues on the stopped node and all consumers on one channel, all 20 came back about 3 s after the node was up, each got its message, and the channel stayed open. Quorum queues behave as before.