feat: implement full MQTT Go parity across 5 phases — binary protocol, auth/TLS, cross-protocol bridging, monitoring, and JetStream persistence

Phase 1: Binary MQTT 3.1.1 wire protocol with PipeReader-based parsing,
full packet type dispatch, and MQTT 3.1.1 compliance checks.

Phase 2: Auth pipeline routing MQTT CONNECT through AuthService,
TLS transport with SslStream wrapping, pinned cert validation.

Phase 3: IMessageRouter refactor (NatsClient → INatsClient),
MqttNatsClientAdapter for cross-protocol bridging, MqttTopicMapper
with full Go-parity topic/subject translation.

Phase 4: /connz mqtt_client field population, /varz actual MQTT port.

Phase 5: JetStream persistence — MqttStreamInitializer creates 5
internal streams, MqttConsumerManager for QoS 1/2 consumers,
subject-keyed session/retained lookups replacing linear scans.

All 503 MQTT tests and 1589 Core tests pass.
This commit is contained in:
Joseph Doherty
2026-03-13 10:09:40 -04:00
parent 0be321fa53
commit 845441b32c
34 changed files with 3194 additions and 126 deletions

View File

@@ -1,6 +1,7 @@
// QoS 1 outgoing message tracker for MQTT.
// QoS 1 outgoing message tracker for MQTT with JetStream ack integration.
// Go reference: golang/nats-server/server/mqtt.go
// QoS 1 outbound tracking — mqttProcessPub (~line 1200)
// trackPublish — maps packet IDs to stream sequences for ack tracking.
using System.Collections.Concurrent;
@@ -8,8 +9,8 @@ namespace NATS.Server.Mqtt;
/// <summary>
/// Tracks outgoing QoS 1 messages pending PUBACK from the client.
/// Messages are stored with their packet ID and can be redelivered on reconnect.
/// Go reference: server/mqtt.go — mqttProcessPub (QoS 1 outbound tracking).
/// Maps packet IDs to JetStream stream sequences for ack-based cleanup.
/// Go reference: server/mqtt.go — mqttProcessPub, trackPublish.
/// </summary>
public sealed class MqttQoS1Tracker
{
@@ -24,7 +25,7 @@ public sealed class MqttQoS1Tracker
/// Registers an outgoing QoS 1 message and assigns a packet ID.
/// Returns the assigned packet ID.
/// </summary>
public ushort Register(string topic, byte[] payload)
public ushort Register(string topic, byte[] payload, ulong streamSequence = 0)
{
var id = GetNextPacketId();
_pending[id] = new QoS1PendingMessage
@@ -34,17 +35,18 @@ public sealed class MqttQoS1Tracker
Payload = payload,
SentAtUtc = DateTime.UtcNow,
DeliveryCount = 1,
StreamSequence = streamSequence,
};
return id;
}
/// <summary>
/// Acknowledges receipt of a PUBACK for the given packet ID.
/// Returns true if the message was found and removed.
/// Returns the pending message if found, or null.
/// </summary>
public bool Acknowledge(ushort packetId)
public QoS1PendingMessage? Acknowledge(ushort packetId)
{
return _pending.TryRemove(packetId, out _);
return _pending.TryRemove(packetId, out var msg) ? msg : null;
}
/// <summary>
@@ -93,4 +95,11 @@ public sealed class QoS1PendingMessage
public byte[] Payload { get; init; } = [];
public DateTime SentAtUtc { get; set; }
public int DeliveryCount { get; set; } = 1;
/// <summary>
/// JetStream stream sequence for this message. 0 if not backed by JetStream.
/// Used to ack the message in the stream on PUBACK.
/// Go reference: server/mqtt.go trackPublish — maps packet ID → stream sequence.
/// </summary>
public ulong StreamSequence { get; init; }
}