Skip to content
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

Reconnection example throws "Does not support reentrant" #244

Closed
westbywest opened this issue Aug 18, 2023 · 9 comments · Fixed by #245
Closed

Reconnection example throws "Does not support reentrant" #244

westbywest opened this issue Aug 18, 2023 · 9 comments · Fixed by #245

Comments

@westbywest
Copy link

westbywest commented Aug 18, 2023

Adapting the reconnection example to throw exceptions verbosely:

import asyncio
import aiomqtt

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

asyncio.run(main())

Subsequent reconnection attempts actually will always fail on "Does not support reentrant," even if the target host becomes available.

MQTT error [Errno -2] Name or service not known; Reconnecting in 5 seconds ...
MQTT error Does not support reentrant; Reconnecting in 5 seconds ...
MQTT error Does not support reentrant; Reconnecting in 5 seconds ...

For reconnection to succeed, I have to move client = ... statement to within the while loop. Is this intended usage, and the example happens to be incorrect?

Observed under python 3.10.7, asyncio 3.4.3, aiomqtt 1.1.0.

@vvanglro
Copy link
Contributor

@westbywest Hi, I can't reproduce it. I use the same code. After restarting the mqtt service. It can still be reconnected normally.

@empicano
Copy link
Collaborator

Hi Ben,

Thanks for the minimal example, I can reproduce it on my side 👍 Moving the client statement inside the while loop is a good option in the meantime, but we do want it to work like in the example (without re-initializing the client instance), so it seems there's something broken, good catch!

For reference, we want the client to be reusable, but not reentrant, the corresponding PR is #216 (by @vvanglro, thanks again! 🚀).

This is an interesting one! I tested this a bit more thouroughly with a local Docker mosquitto broker that I shut down. With some logging on the exact exception types and on the relevant lock, this gives:

lock acquired
lock released
<class 'aiomqtt.error.MqttCodeError'>
ERROR: [code:7] The connection was lost.; Reconnecting in 5 seconds ...

lock acquired
<class 'aiomqtt.error.MqttError'>
ERROR: [Errno 61] Connection refused; Reconnecting in 5 seconds ...

<class 'aiomqtt.error.MqttReentrantError'>
ERROR: Does not support reentrant; Reconnecting in 5 seconds ...

What we see here is that the lock is released when an MqttCodeError is raised, but not when an MqttError is raised. Due to the lock not being correctly released, the code runs into this condition inside __aenter__ and raises the "Does not support reentrant" error that you see.

If I got it right the reason for the lock not being released is that the code already fails inside __aenter__ after aquiring the lock and inside the call to connect(). Due to this failure __aexit__ (where the lock is released) is not called.

@vvanglro, are you able to reproduce the error with this extra information?

@empicano
Copy link
Collaborator

I just pushed a script to spin up a local Mosquitto broker via Docker to make it easier to reproduce these steps. After calling the script you should be able to connect to the broker via aiomqtt.Client("localhost", port=1883) 🙂

@vvanglro
Copy link
Contributor

The emqx image I use. Maybe we should catch the exception of connect method and release the lock.

@empicano
Copy link
Collaborator

Sounds good, do you want to make a PR with a fix and a test? 💯

@vvanglro
Copy link
Contributor

Sounds good, do you want to make a PR with a fix and a test? 💯

next monday i'll take the time to do it.

vvanglro added a commit to vvanglro/aiomqtt that referenced this issue Aug 21, 2023
empicano pushed a commit to vvanglro/aiomqtt that referenced this issue Aug 23, 2023
@frederikaalund
Copy link
Member

Great job to both of you (@vvanglro and @empicano) with this quick fix on this issue. 😄 👍

@dunielpls
Copy link

dunielpls commented Sep 3, 2023

Hello, and thank you for the great library. Can you consider tagging a release with the fix?

I am running into this exact issue (the Lock not being released appropriately) and it would be really nice to avoid running the main code long term.

@empicano
Copy link
Collaborator

empicano commented Sep 3, 2023

Done 🙂 Happy to hear that you like aiomqtt!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants