diff --git a/src/NATS.Server/Mqtt/MqttNatsClientAdapter.cs b/src/NATS.Server/Mqtt/MqttNatsClientAdapter.cs index a651857..b8ebddb 100644 --- a/src/NATS.Server/Mqtt/MqttNatsClientAdapter.cs +++ b/src/NATS.Server/Mqtt/MqttNatsClientAdapter.cs @@ -83,6 +83,9 @@ public sealed class MqttNatsClientAdapter : INatsClient /// public Subscription AddSubscription(string natsSubject, string sid, string? queue = null) { + // Pre-warm topic bytes cache for this subject to avoid cache miss on first message. + MqttTopicMapper.NatsToMqttBytes(natsSubject); + var sub = new Subscription { Client = this, diff --git a/src/NATS.Server/Mqtt/MqttTopicMapper.cs b/src/NATS.Server/Mqtt/MqttTopicMapper.cs index 32797c2..d44c23c 100644 --- a/src/NATS.Server/Mqtt/MqttTopicMapper.cs +++ b/src/NATS.Server/Mqtt/MqttTopicMapper.cs @@ -107,7 +107,18 @@ public static class MqttTopicMapper if (natsSubject.Length == 0) return string.Empty; - // First, replace _DOT_ escape sequences back to dots + // Fast path: no _DOT_ escape sequences — just char replacement via string.Create + // (avoids StringBuilder allocation for the common case). + if (!natsSubject.Contains(DotEscape)) + { + return string.Create(natsSubject.Length, natsSubject, static (span, src) => + { + for (var i = 0; i < src.Length; i++) + span[i] = src[i] switch { '.' => '/', '*' => '+', '>' => '#', _ => src[i] }; + }); + } + + // Slow path: has _DOT_ escape sequences — use StringBuilder var working = natsSubject.Replace(DotEscape, "\x00"); var sb = new StringBuilder(working.Length);