-
Notifications
You must be signed in to change notification settings - Fork 195
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reconnect does not work with version 8.2.4 but works with version 8.2.3 (8.2.2, 8.2.1, 8.2.0 as well) #505
Comments
Hi @smfx, thanks for your kind words.
To be honest, I modified your code a bit to not install import asyncio
import aio_pika
from aio_pika import ExchangeType
from aio_pika.abc import AbstractIncomingMessage
async def _consume(message: AbstractIncomingMessage) -> None:
async with message.process(requeue=True, ignore_processed=True):
print(f"[x] {message.routing_key}"
f"[x] Decoded {message.body.decode()}")
async def _check_tasks() -> None:
while True:
tasks = asyncio.all_tasks()
print(f"Number of tasks: {len(tasks)}")
for task in tasks:
print(f"Task: {task.get_name()} {task.get_coro()}")
print("================================")
await asyncio.sleep(10)
async def _receive() -> None:
connection = await aio_pika.connect_robust()
channel = await connection.channel()
await channel.set_qos(prefetch_count=20)
exchange = await channel.declare_exchange("broker_publish",
ExchangeType.TOPIC)
queue = await channel.declare_queue("broker_events", durable=False,
exclusive=False, auto_delete=False)
await queue.bind(exchange, routing_key="#")
await queue.consume(_consume)
async def main() -> None:
await asyncio.gather(_receive(), _check_tasks())
if __name__ == "__main__":
asyncio.run(main()) |
Maybe there is a problem only on Windows, I have nowhere to check it now, maybe it will appear later. If you manage to find out the details feel free to ping here. |
Thank you for your assistant! But it seems the problem persists in your output too. You had the following tasks before reconnect:
Pay attention too Channel._reader. After
So, if you publish message (after reconnect) to the exchange it wont be processed (with aio-pika 8.2.0 it would be processed). Actually, I can see the queue wont be restored after docker restart. Maybe, it is intentional behavior, but my application just stops receiving messages after docker restart with no errors :) I will try to find MacOS for testing :) |
@smfx to reproduce, have you tried killing the RabbitMQ process in the Docker container? It's likely that RabbitMQ is respecting |
I already found the reason, the fact is that the garbage collector removes the channel object and sometimes the connection. in the above example, after the function ends, neither the channel nor the connection remains referenced. |
Hi! I looked a little bit more:
Code of the test with testcontainersimport asyncio
from typing import Dict
from aio_pika.abc import AbstractIncomingMessage
from aio_pika import ExchangeType, connect_robust, Message
from testcontainers.rabbitmq import RabbitMqContainer
import pytest
import json
incoming_message_list = []
class TestConsumer:
"""Silly test consumer."""
def __init__(
self,
rmq_connection_str: str,
exchange_name: str,
exchange_type: ExchangeType,
queue_name: str,
):
self._rmq_connection_str = rmq_connection_str
self._exchange_name = exchange_name
self._exchange_type = exchange_type
self._queue_name = queue_name
self._connection = None
self._queue = None
self._queue_consumer_tag = None
async def start_consuming(self) -> None:
"""Connecting to queue and waiting for messages."""
self._connection = await connect_robust(
self._rmq_connection_str,
)
channel = await self._connection.channel()
await channel.set_qos(1)
exchange = await channel.declare_exchange(
self._exchange_name,
self._exchange_type,
)
self._queue = await channel.declare_queue(
self._queue_name,
durable=False,
exclusive=False,
auto_delete=False,
)
await self._queue.bind(exchange, routing_key="#")
self._queue_consumer_tag = await self._queue.consume(self._consume)
async def _consume(
self, message: AbstractIncomingMessage
) -> None:
"""Message handler."""
async with message.process():
incoming_message_list.append(message.body.decode())
async def publish_message(
message: Dict,
connection_string: str,
exchange_name: str,
exchange_type: ExchangeType = ExchangeType.TOPIC,
) -> None:
"""Sends message to exchange."""
_connection = await connect_robust(connection_string,)
channel = await _connection.channel()
exchange = await channel.declare_exchange(
exchange_name,
exchange_type,
)
await exchange.publish(
Message(json.dumps(message).encode()),
routing_key="test"
)
@pytest.mark.asyncio
async def test_receive_message_after_reconnect():
"""Check that messages are still being delivered after reconnecting."""
rmq_container = RabbitMqContainer()
# Пробрасываем порты, чтобы после рестарта они не поменялись
rmq_container.ports = {5672: 5672}
with rmq_container as rmq:
# Arrange
connection_string = f"amqp://guest:guest@" \
f"{rmq.get_container_host_ip()}:" \
f"{rmq.get_exposed_port(rmq.RABBITMQ_NODE_PORT)}/"
exchange_name = "test_exchange"
queue_name = "test_queue"
consumer = TestConsumer(
rmq_connection_str=connection_string,
exchange_name=exchange_name,
exchange_type=ExchangeType.TOPIC,
queue_name=queue_name,
)
await consumer.start_consuming()
# Waiting consumer connection
await asyncio.sleep(5)
rmq.get_wrapped_container().restart()
# Waiting rmq restart
await asyncio.sleep(15)
# Act
connection_string = f"amqp://guest:guest@" \
f"{rmq.get_container_host_ip()}:" \
f"{rmq.get_exposed_port(rmq.RABBITMQ_NODE_PORT)}/"
await publish_message(
{"test": "message111"},
connection_string=connection_string,
exchange_name=exchange_name,
)
await asyncio.sleep(0.1)
# Assert
assert len(incoming_message_list) == 1
# Cleanup
incoming_message_list.clear() |
@dvf Hello! No, I did not try this. For me it is easier to restart container :) But I tried to connect to the RMQ on the remote server (K8S cluster) and imitate network error (switch off wifi) and the problem remains. |
@smfx see this #505 (comment) |
So the workaround for this is to keep a robust connection and robust channel in a variable in my application code? Shouldn't there be any other way instead of using weak references in the robust connection? |
We are seeing the same thing in version 9.0.7 If we don't hold a reference to the RobustChannel, we always fail to resume consuming after connection issues to RabbitMQ, |
Why do we need weak references for channels in RobustConnection? |
@MartinWallgren this already reworked in #533 |
Hello!
First of all, thank you for your great work! I probably found a bug with aio-pika 8.2.4 version. I'm using windows, docker image with rabbitmq, python 3.9.
Code example for reproducing:
Steps:
Run the script (with aio-pika 8.2.4). You will see 7 running tasks (I think on Linux it would be 6).
Publish message to queue. You will see this message in terminal.
Restart docker container with RabbitMQ.
Thera are only 6 running tasks after reconnect (Channel._reader is missing)
Try to publish another message. No result in terminal. In RMQ GUI no consumers related to this queue.
Repeat steps 1-5 with aio-pika 8.2.0. Number of tasks will remain after reconnect and the script will receive message.
I can provide more information if needed. Thank you very much!
The text was updated successfully, but these errors were encountered: