Everybody who works with MQTT has come across ‘clean session’ (MQTT3) or ‘clean start’ (MQTT5). But there are some caveats to understanding it.
The idea about a session in MQTT is that your program state is unaffected when the underlying connection (TCP) drops momentarily (and the length of a ‘moment’ is relative). This is the ‘reliable’ aspect in ‘reliable messaging protocol’. If the connection comes back, you still have the same subscriptions, and any messages that match your subscription are delivered.
What it’s not meant for (in most cases), is that when your program restarts, it can pick up a previous session. The server session will not reflect the new client’s running context, nor can the client ask for which subscriptions there are, for instance. It will only cause confusion.
The problem is making a distinction between a new connection, and a reconnection. In most cases, you want the session of your client ID to be wiped when your client starts fresh. That old session represented another running client context, and does not apply to a freshly started client.
For MQTT3 clients, the parameter ‘clean session’ determines whether an existing session is wiped on connect, but also whether a session should be wiped on disconnect. This presents problems. MQTT5 does it better, where ‘clean start’ only applies to the new connection.
That is not to say you are forced to use old/expired sessions with MQTT3. You need to either generate a random client ID on every new program start, and always reconnect with that ID, or you connect twice when you want to use deterministic client IDs: once with ‘clean session’ to wipe the session and then again without ‘clean session’. The last option is obviously not ideal.
It doesn’t always go correctly in the field. Let’s see what mosquitto_sub
(version 2.0.12) does in case of reconnects. First let’s do a normal connect.
./mosquitto_sub -v -V mqttv5 -t '#' -h 192.168.178.127 -k 5
The server says:
[2022-11-26 16:18:38] [NOTICE] Client '[ClientID='Ojo4KHvhxmQnY9fum9nkMfD', username='', fd=29, keepalive=5s, transport='TCP/MQTT/Non-SSL', address='192.168.178.178', prot=5.0, clean=1]' logged in successfully
Note the clean=1
. Now, let’s disconnect the Ethernet cable and plug it back in after the keep-alive expired:
[2022-11-26 16:19:40] [NOTICE] Removing client '[ClientID='Ojo4KHvhxmQnY9fum9nkMfD', username='', fd=29, keepalive=5s, transport='TCP/MQTT/Non-SSL', address='192.168.178.178', prot=5.0, clean=1]'. Reason(s): Keep-alive expired: authenticated=1, keep-alive=5s, last activity=12 seconds ago.
[2022-11-26 16:19:47] [NOTICE] Client '[ClientID='Ojo4KHvhxmQnY9fum9nkMfD', username='', fd=29, keepalive=5s, transport='TCP/MQTT/Non-SSL', address='192.168.178.178', prot=5.0, clean=1]' logged in successfully
As you can see, the reconnection is done with clean=1
. This means to avoid using expired sessions, even with MQTT5, you have to use the same tactic as described for MQTT3 ‘clean session’ above.
On the other hand, the Paho Python MQTT client actually has a ‘clean start’ mode called MQTT_CLEAN_START_FIRST_ONLY
, which is the default. So with that, you get proper session behavior.