From f7a8d72a6de214a0058d6ceff2d1127a5eb69880 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Fri, 13 Mar 2026 14:44:49 -0400 Subject: [PATCH] perf: optimize MQTT NatsToMqtt fast path and pre-warm topic cache Add string.Create fast path in NatsToMqtt for subjects without _DOT_ escape sequences (common case), avoiding StringBuilder allocation. Pre-warm the topic bytes cache when MQTT subscriptions are added to eliminate cache miss on first message delivery. --- src/NATS.Server/Mqtt/MqttNatsClientAdapter.cs | 3 +++ src/NATS.Server/Mqtt/MqttTopicMapper.cs | 13 ++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) 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);