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

Connect is blocking #23

Open
Python1320 opened this issue Jun 24, 2020 · 10 comments
Open

Connect is blocking #23

Python1320 opened this issue Jun 24, 2020 · 10 comments
Assignees

Comments

@Python1320
Copy link

Our broker was recently DDoSed, which caused a disconnect on our luamqtt end, and a reconnect attempt where the connecting routine took over a minute after which our service was automatically killed for being frozen by our watchdog.
The stacktrace showed us that in fact the ioloop is not used for connecting in mqtt:

local ok, err = args.connector.connect(conn)
(settimeout is only applied later)

Is this an oversight or by design? Would a rearchitecturing be out of the question so that connect would be asynchronous also?
(Connect will always block if there is DNS resolving to be done so this may also not be the perfect solution for everyone).
Another option is we could establish a connection ourselves and just provide the connected socket to luamqtt.

@xHasKx xHasKx self-assigned this Jun 24, 2020
@xHasKx
Copy link
Owner

xHasKx commented Jun 24, 2020

@Python1320 , good point. I'll think about how to make connector.connect call non-blocking...

@xHasKx
Copy link
Owner

xHasKx commented Jul 4, 2020

@Python1320 , I found a way how to use settimeout() on almost all stages of the connection lifetime (except DNS resolving).
But this will be a breaking change and actually it will use socket.select() technique, which may suffer if you plan to handle hundreds or thousands of simultaneous connections.
If you plan to handle such highload - you should consider using another mqtt framework with ioloop builtin into its runtime, like gmqtt for Pyhton or mqtt.js for nodejs.

On the other hand, luamqtt library is built to be small and simple, that's why it does still not have ioloop on all connection stages.

Do you still plan to use it in your project?

@rafale77
Copy link

I have been struggling with a similar problem, I can't seem to be able to keep a client based on this library connected and listening to the broker for messages without blocking the lua socket. It makes it unusable even in my very simple use case of one publishing client, one listening client with one broker. Am I missing something?

@xHasKx
Copy link
Owner

xHasKx commented Jul 23, 2020

@rafale77 , currently only TCP Connection opening process is blocking and all next stages is async thanks to ioloop, allowing other MQTT clients to work in the same lua script.
Did you used mqtt.run_ioloop(client)?

@rafale77
Copy link

Yes I did and I observed that it opens a listener and waits for a message. While waiting, it does block my main program's socket activities.

@xHasKx
Copy link
Owner

xHasKx commented Jul 28, 2020

Yes I did and I observed that it opens a listener and waits for a message. While waiting, it does block my main program's socket activities.

That's because mqtt.run_ioloop(client) is running an infinity loop to call your mqtt messages handlers when new MQTT messages arrived over TCP connection.
Which runtime is running your code?

If you want to run some parallel calculations or network communications in the same script with luamqtt - you have several options:

  • Start a separate OS thread or process to run your logic. In this case you have to exchange data between threads/processes somehow, you should handle it yourself.
  • Run your logic in the ioloop started by mqtt.run_ioloop(client). There will be some requirements for your code (it should not block for long). See an example in this test: https://github.com/xHasKx/luamqtt/blob/master/tests/spec/ioloop.lua
  • Run your own ioloop and adopt the luamqtt to be used in it. If you'll get an example - maybe I can help with that.
  • Don't use ioloop at all and try to use luamqtt in sync mode like in this example: https://github.com/xHasKx/luamqtt/blob/master/examples/sync.lua

@Python1320
Copy link
Author

I am slowly trying out https://github.com/flukso/lua-mosquitto but we are still using luamqtt, but with a 2 second timeout on connect and exponential backoff.
A broken MQTT connection is better in my case than a fully frozen process :)

Keeping things simple and flexible is indeed difficult.

In my case I need many things to cooperate at the same time and MQTT freezing the other parts is not acceptable.
Imagine a light switch: I want to be able to toggle the light on and off by polling the button status, but I also want to maintain a MQTT connection to tell about the switch status changes. Creating a thread for mqtt sounds like an overkill in this case, but indeed, MQTT must not block toggling the light switch on network troubles.

@xHasKx
Copy link
Owner

xHasKx commented Aug 8, 2020

Did you try OpenResty - https://openresty.org/ ?
It's Lua engine has async sockets and supported in luamqtt with appropriate connector

@xHasKx
Copy link
Owner

xHasKx commented Aug 8, 2020

Anyway, there is a way to provide a new method in luamqtt to start connecting async like described in #23 (comment)
I'll try to implement it on the next week

@Tieske
Copy link
Contributor

Tieske commented Nov 1, 2021

@Python1320 The main "connect" problem is here; https://github.com/xHasKx/luamqtt/blob/master/mqtt/luasocket.lua#L12

It uses socket.connect shortcut, which is blocking without a timeout. Can be circumvented easily by using code like this;

local sock = socket.tcp()
sock:settimeout(timeout)
local ok, err = sock:connect(host, port)   --> calling `connect` on the socket, NOT the library!

The call will still be blocking, but at least it will timeout at some point and not just hang. If you use an IP instead of a name, you can take out the DNS resolution call as well.

@xHasKx As for using a select based implementation, that is pretty hard. Essentially you would rewrite Copas inside this library. So imo the better option is to keep this simple, and for non-blocking sockets refer people to Copas.

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

No branches or pull requests

4 participants