sbtinstruments / aiomqtt

The idiomatic asyncio MQTT client, wrapped around paho-mqtt
https://sbtinstruments.github.io/aiomqtt
BSD 3-Clause "New" or "Revised" License
392 stars 71 forks source link

Reconnect Bug #288

Closed nicholas-a-guerra closed 3 months ago

nicholas-a-guerra commented 3 months ago

After testing the newest version 2.0.1, there seems to be some possible issue with guidance on how to reconnect. Currently, if the client class object is reused in the context manager then on every new connect it will fail. From the documents, this is guidance on the reconnection technique:

import asyncio
import aiomqtt

async def main():
    client = aiomqtt.Client("test.mosquitto.org")
    interval = 5  # Seconds
    while True:
        try:
            async with client:
                await client.subscribe("humidity/#")
                async for message in client.messages:
                    print(message.payload)
        except aiomqtt.MqttError:
            print(f"Connection lost; Reconnecting in {interval} seconds ...")
            await asyncio.sleep(interval)

asyncio.run(main())

To test, I would connect to local host and start with mosquitto on, which works perfect. Then I would stop the mosquitto service. This would result in a correct MqttError exception and reconnection attempt every 5 seconds. Then I would start the mosquitto service again. Instead of reconnecting as intended though, it would either first silently drop out of the async with client: context and attempt another reconnect or it would also raise more MqttError exceptions repeatedly.

Instead, if I made a slight change to not reuse the old client object and create a new one every time. Then it seems to work as expected. Below is how it worked as expected:

import asyncio
import aiomqtt

async def main():
    interval = 5  # Seconds
    while True:
        try:
            async with aiomqtt.Client("test.mosquitto.org"):
                await client.subscribe("humidity/#")
                async for message in client.messages:
                    print(message.payload)
        except aiomqtt.MqttError:
            print(f"Connection lost; Reconnecting in {interval} seconds ...")
            await asyncio.sleep(interval)

asyncio.run(main())
empicano commented 3 months ago

Hi Nick, thanks for this detailed issue! 👍 Issue #268 is related to this, the code in the documentation should work (but doesn't). I'll update the example so that others won't encounter the same problem!