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 diagram as text
- MQTT server → EventLoop::poll decodes the PUBLISH
- EventLoop::poll decodes the PUBLISH → Connection task finds the subscription
- Connection task finds the subscription → MqttMessage payload shared, no copy
- MqttMessage payload shared, no copy → Dispatch one loop, or lanes
- Dispatch one loop, or lanes → Decode read from the payload
- Decode read from the payload → Handler async fn, #[subscriber]
- Handler async fn, #[subscriber] → Reply encoded into a buffer
- Reply encoded into a buffer → MqttPublisher hands the buffer over
- MqttPublisher hands the buffer over → Request channel of the AsyncClient
- MqttPublisher hands the buffer over → Acknowledge PUBACK or PUBREC: then
- Acknowledge PUBACK or PUBREC → Request channel of the AsyncClient
- Request channel of the AsyncClient → EventLoop writes PUBLISH, then PUBACK
- 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.