From 4db93cae2b5ea454d9bb33f053d52eb949f254c3 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Tue, 24 Mar 2026 15:43:29 -0400 Subject: [PATCH] fix(lmxproxy): fix orphaned tag subscriptions when client subscribes per-tag When a client calls Subscribe multiple times with the same session ID (one tag per RPC), each call overwrites the ClientSubscription entry. UnsubscribeClient only cleaned up tags from the last entry, leaving earlier tags orphaned in _tagSubscriptions. Now scans all tag subscriptions for client references during cleanup. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../Subscriptions/SubscriptionManager.cs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lmxproxy/src/ZB.MOM.WW.LmxProxy.Host/Subscriptions/SubscriptionManager.cs b/lmxproxy/src/ZB.MOM.WW.LmxProxy.Host/Subscriptions/SubscriptionManager.cs index bbcfb40..4a9194f 100644 --- a/lmxproxy/src/ZB.MOM.WW.LmxProxy.Host/Subscriptions/SubscriptionManager.cs +++ b/lmxproxy/src/ZB.MOM.WW.LmxProxy.Host/Subscriptions/SubscriptionManager.cs @@ -57,8 +57,8 @@ namespace ZB.MOM.WW.LmxProxy.Host.Subscriptions }); var addressSet = new HashSet(addresses, StringComparer.OrdinalIgnoreCase); - var clientSub = new ClientSubscription(clientId, channel, addressSet); + var clientSub = new ClientSubscription(clientId, channel, addressSet); _clientSubscriptions[clientId] = clientSub; var newTags = new List(); @@ -170,17 +170,18 @@ namespace ZB.MOM.WW.LmxProxy.Host.Subscriptions _rwLock.EnterWriteLock(); try { - foreach (var address in clientSub.Addresses) + // Scan all tag subscriptions — not just clientSub.Addresses — because + // a client may have called Subscribe multiple times (one tag per RPC), + // each overwriting the ClientSubscription. The last one's Addresses + // only has the final batch, but earlier tags still reference this client. + foreach (var kvp in _tagSubscriptions) { - if (_tagSubscriptions.TryGetValue(address, out var tagSub)) + if (kvp.Value.ClientIds.Remove(clientId)) { - tagSub.ClientIds.Remove(clientId); - - // Last client unsubscribed — remove the tag subscription - if (tagSub.ClientIds.Count == 0) + if (kvp.Value.ClientIds.Count == 0) { - _tagSubscriptions.TryRemove(address, out _); - tagsToDispose.Add(address); + _tagSubscriptions.TryRemove(kvp.Key, out _); + tagsToDispose.Add(kvp.Key); } } }