Skip to main content

Path

The way of one MQTT message through a RustStream service: the event loop, the connection task, the delivery, the handler, the reply and the acknowledgement.

The path of one message that is answered: down the left column from the server to the handler, up the right column back onto the wireEventLoop::polldecodes the PUBLISHConnection taskfinds the subscriptionMqttMessagepayload shared, no copyDispatchone loop, or lanesDecoderead from the payloadHandlerasync fn, #[subscriber]MQTT serverEventLoop writesPUBLISH, then PUBACKRequest channelof the AsyncClientAcknowledgePUBACK or PUBRECMqttPublisherhands the buffer overReplyencoded into a buffer
The diagram as text
  1. MQTT server → EventLoop::poll decodes the PUBLISH
  2. EventLoop::poll decodes the PUBLISH → Connection task finds the subscription
  3. Connection task finds the subscription → MqttMessage payload shared, no copy
  4. MqttMessage payload shared, no copy → Dispatch one loop, or lanes
  5. Dispatch one loop, or lanes → Decode read from the payload
  6. Decode read from the payload → Handler async fn, #[subscriber]
  7. Handler async fn, #[subscriber] → Reply encoded into a buffer
  8. Reply encoded into a buffer → MqttPublisher hands the buffer over
  9. MqttPublisher hands the buffer over → Request channel of the AsyncClient
  10. MqttPublisher hands the buffer over → Acknowledge PUBACK or PUBREC: then
  11. Acknowledge PUBACK or PUBREC → Request channel of the AsyncClient
  12. Request channel of the AsyncClient → EventLoop writes PUBLISH, then PUBACK
  13. EventLoop writes PUBLISH, then PUBACK → MQTT server

The connection task polls the rumqttc event loop, which reads the PUBLISH from the socket and decodes it. The task reads the topic, finds the subscriptions the packet belongs to, as Demultiplexing draws, and sends each its delivery over a channel of its own. A topic that is not UTF-8 is dropped with a warning.

The MqttMessage shares the packet's payload, so the body is never copied. It owns a copy of the topic and the headers built from the packet's properties. A QoS 1 or QoS 2 delivery also keeps the packet's identity, which its acknowledgement needs. The context a handler reads holds the topic as well.

A subscription's channel has no bound of its own. For QoS 1 and QoS 2 the bound is the server's: it sends no more than the Receive Maximum unacknowledged deliveries. A packet that matches no open subscription waits, up to 1024 of them, for a subscription that opens later.

The handler's reply is encoded into a buffer, and the publisher hands that buffer to rumqttc as it is, without a copy. The headers become the packet's properties. The publish returns once the request is on the client's request channel, not once the server has acknowledged it.

The delivery settles after the reply. Its acknowledgement goes through the same request channel, so it leaves after the reply and before a later shutdown's DISCONNECT. What each outcome sends is on Settlement.

The benchmarks count this path: the instructions and allocations of one delivery and one reply, the client's work on the service's thread included.