MQTT Buffering and QoS
buffering means holding a message so it can be delivered later, after a broker restart, a network drop, or a client being offline two independent paths exist publisher to broker, and broker to subscriber both depend on the same two mechanisms working together qos and persistent sessions key point up front qos ≥1 and a persistent session are both required, together qos governs the handshake while two clients are connected it does not, by itself, make anything survive a disconnect a persistent session is what makes the broker (and client) hold messages across a disconnect neither alone is enough what each setting is qos (quality of service) qos is the delivery guarantee on a message there are three levels qos guarantee behaviour buffered 0 at most once fire and forget sent once, never held or retried lost if the link is down no 1 at least once kept and resent until the other side acknowledges it may arrive more than once yes 2 exactly once like qos 1, with extra steps to avoid duplicates higher overhead, used for critical data where duplicates are unacceptable yes buffering only exists at qos ≥1, because only then is a message held until it is confirmed the collector subscribes at the level set by the qos collector setting, which defaults to 1 see mqtt collector docid\ ggnqijhlxifoimbt yp2c for the full qos table and the collector settings persistent session a session is the state the broker remembers about a client its subscriptions and its queued messages a persistent session (clean session = false) means the broker keeps that state while the client is offline, instead of throwing it away on disconnect this is what makes buffering work for the collector without it, the broker forgets the collector the moment it disconnects and queues nothing on the factry mqtt collector this is the persistentsession setting, which defaults to true on mqtt 5, clean session is split into clean start and session expiry interval , and session expiry interval defaults to 0 , which ends the session as soon as the client disconnects on an mqtt 5 broker, clean start = 0 alone gives you no queueing the session expiry interval must also be set to cover the worst case outage the session is tied to the client id the broker uses the client id to recognise the returning collector and hand back its queue the client id of the factry mqtt collector is automatically derived (including collector uuid) and is fixed keep alive keep alive is how often the collector signals the broker that it is still there on the factry mqtt collector this is the keepalivesecs setting, which defaults to 30 if the broker hears nothing for 1 5x the keep alive interval, it decides the collector is gone and starts queueing for it a shorter keep alive means the broker notices a real drop sooner and starts holding messages sooner publisher to broker buffering path publisher up, broker or network down the publisher should hold the data produced during the outage and send it on reconnect requirements on the publisher publish at qos ≥1, so each message is held until the broker acknowledges it clean session = false and a fixed client id, so an interrupted session resumes instead of being discarded caveat whether new samples produced while offline are actually buffered depends on the publisher client, not on the mqtt flags some clients persist a queue to disk, some hold it in memory, some drop it, and some only queue while they think they are connected the flags set up the session; the client library decides if there is a real buffer see troubleshooting broker to subscriber buffering path broker up, subscriber (collector) down the broker holds the collector's messages and delivers them in order on reconnect requirements on the collector persistent session on ( persistentsession true, which sets clean session = false) on an mqtt 5 broker, also set a session expiry interval long enough to cover the outage fixed, unique client id subscription qos ≥1 ( qos 1 normally) broker requirements (names vary by broker) for queued messages to survive a broker restart, the broker must queue qos ≥1 messages for offline persistent sessions, allow the per client queue to be sized for your worst case outage (message count, and byte cap if your broker separates them), persist sessions and queues to disk, so they are not lost on restart example (mosquitto) persistent true, max queued messages , max queued bytes hivemq, emqx and others expose the same three capabilities under different names check your broker's docs for the equivalents on reconnect the collector receives the queued messages in the order the broker received them what the collector depends on but does not control publish qos what the collector receives is min(publish qos, subscription qos), so a source publishing at qos 0 gets no broker side queueing whatever the collector is set to check the publisher's qos before assuming a collector problem sparkplug b is the common case here, see sparkplug b and qos below publisher side buffering (source up, broker or network down) happens on the publishing device, not the collector it belongs in the broker or publisher documentation troubleshooting qos is only as good as the publisher's implementation the flags describe intent whether the publisher actually buffers during an outage depends on the client check its buffering behaviour directly (outgoing queue, memory or persisted, what it does on a detected disconnect) rather than assuming qos ≥1 means store and forward node red / mqtt js publisher the node red mqtt node (mqtt js) only queues outgoing messages while it believes it is connected once it detects the disconnect (after 1 5x keep alive, or immediately when the broker sends a disconnect on a clean stop) it stops buffering, so the first few points get queued and the rest are lost workaround used set keep alive to 0 so the client always considers itself connected and keeps buffering in memory caveats the buffer is in memory restarting the publisher loses it with keep alive 0 the client will not detect a dead link via keep alive, it relies on tcp rough sizing, payload bytes only a 30 metric sparkplug message in mqtt js is about 1 25 kb, so one message per 5 s is about 22 mb per day mqtt js holds each queued message as a javascript object in its outgoing store, with the topic string, packet metadata and buffer allocation on top of the payload, so real memory use is a multiple of that measure actual process memory growth for your payload and provision generous headroom rather than sizing on payload alone sparkplug b and qos sparkplug b publishes nbirth, dbirth, ndata and ddata at qos 0 the exceptions are the primary host state message and the ndeath death certificate, which the spec requires at qos 1 (ndeath is registered as the mqtt will, with will qos 1 and retain false) it does not use retain on data messages either instead it recovers state with a per message sequence number and birth/death certificates, and a host can request a rebirth to resync the consequence is that a spec compliant sparkplug b source gives no broker side buffering data produced during an outage is lost unless the device has its own store and forward some sparkplug capable clients still expose a qos setting and let you publish at qos ≥1 that is outside the spec but does enable mqtt buffering if you rely on it, confirm the device honours it end to end