From a326a8cbde4d7378696cb4f940a2aa349a59e569 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Sun, 22 Mar 2026 23:17:55 -0400 Subject: [PATCH] fix(lmxproxy): make MxAccess client name unique per instance Multiple instances registering with the same name may cause MxAccess to conflict on callback routing. ClientName is now configurable via appsettings.json, defaulting to a GUID-suffixed name if not set. Instances A and B use "LmxProxy-A" and "LmxProxy-B" respectively. Co-Authored-By: Claude Opus 4.6 (1M context) --- lmxproxy/instances_config.md | 3 ++- .../Configuration/LmxProxyConfiguration.cs | 3 +++ lmxproxy/src/ZB.MOM.WW.LmxProxy.Host/LmxProxyService.cs | 3 ++- .../MxAccess/MxAccessClient.Connection.cs | 5 +++-- .../src/ZB.MOM.WW.LmxProxy.Host/MxAccess/MxAccessClient.cs | 5 ++++- 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/lmxproxy/instances_config.md b/lmxproxy/instances_config.md index 1e4167e..82f82c7 100644 --- a/lmxproxy/instances_config.md +++ b/lmxproxy/instances_config.md @@ -8,12 +8,13 @@ Two instances of the LmxProxy v2 Host service are deployed on windev (10.100.0.4 |---|---|---| | **Service Name** | `ZB.MOM.WW.LmxProxy.Host.V2` | `ZB.MOM.WW.LmxProxy.Host.V2B` | | **Display Name** | SCADA Bridge LMX Proxy V2 | SCADA Bridge LMX Proxy V2B | +| **MxAccess Client Name** | `LmxProxy-A` | `LmxProxy-B` | | **Publish Directory** | `C:\publish-v2\` | `C:\publish-v2b\` | | **gRPC Port** | 50100 | 50101 | | **HTTP Status Port** | 8081 | 8082 | | **Log File Prefix** | `lmxproxy-v2-` | `lmxproxy-v2b-` | | **Log Directory** | `C:\publish-v2\logs\` | `C:\publish-v2b\logs\` | -| **Health Probe Tag** | `DevAppEngine.Scheduler.ScanTime` | `DevAppEngine.Scheduler.ScanTime` | +| **Health Probe Tag** | `DevPlatform.Scheduler.ScanTime` | `DevPlatform.Scheduler.ScanTime` | | **API Keys File** | `C:\publish-v2\apikeys.json` | `C:\publish-v2b\apikeys.json` | | **Auto-Start** | Yes | Yes | diff --git a/lmxproxy/src/ZB.MOM.WW.LmxProxy.Host/Configuration/LmxProxyConfiguration.cs b/lmxproxy/src/ZB.MOM.WW.LmxProxy.Host/Configuration/LmxProxyConfiguration.cs index 71b7f09..91d15c1 100644 --- a/lmxproxy/src/ZB.MOM.WW.LmxProxy.Host/Configuration/LmxProxyConfiguration.cs +++ b/lmxproxy/src/ZB.MOM.WW.LmxProxy.Host/Configuration/LmxProxyConfiguration.cs @@ -9,6 +9,9 @@ namespace ZB.MOM.WW.LmxProxy.Host.Configuration /// Path to API key configuration file. Default: apikeys.json. public string ApiKeyConfigFile { get; set; } = "apikeys.json"; + /// Unique client name for MxAccess Register(). Must be unique per instance. Default: auto-generated. + public string? ClientName { get; set; } + /// MxAccess connection settings. public ConnectionConfiguration Connection { get; set; } = new ConnectionConfiguration(); diff --git a/lmxproxy/src/ZB.MOM.WW.LmxProxy.Host/LmxProxyService.cs b/lmxproxy/src/ZB.MOM.WW.LmxProxy.Host/LmxProxyService.cs index 96d09b2..5137066 100644 --- a/lmxproxy/src/ZB.MOM.WW.LmxProxy.Host/LmxProxyService.cs +++ b/lmxproxy/src/ZB.MOM.WW.LmxProxy.Host/LmxProxyService.cs @@ -70,7 +70,8 @@ namespace ZB.MOM.WW.LmxProxy.Host probeTestTagAddress: _config.HealthCheck.TestTagAddress, probeTimeoutMs: _config.HealthCheck.ProbeTimeoutMs, maxConsecutiveTransportFailures: _config.HealthCheck.MaxConsecutiveTransportFailures, - degradedProbeIntervalMs: _config.HealthCheck.DegradedProbeIntervalMs); + degradedProbeIntervalMs: _config.HealthCheck.DegradedProbeIntervalMs, + clientName: _config.ClientName); // 5. Connect to MxAccess synchronously (with timeout) Log.Information("Connecting to MxAccess (timeout: {Timeout}s)...", diff --git a/lmxproxy/src/ZB.MOM.WW.LmxProxy.Host/MxAccess/MxAccessClient.Connection.cs b/lmxproxy/src/ZB.MOM.WW.LmxProxy.Host/MxAccess/MxAccessClient.Connection.cs index 144b97f..1ecdea6 100644 --- a/lmxproxy/src/ZB.MOM.WW.LmxProxy.Host/MxAccess/MxAccessClient.Connection.cs +++ b/lmxproxy/src/ZB.MOM.WW.LmxProxy.Host/MxAccess/MxAccessClient.Connection.cs @@ -107,8 +107,9 @@ namespace ZB.MOM.WW.LmxProxy.Host.MxAccess _lmxProxy.OnDataChange += OnDataChange; _lmxProxy.OnWriteComplete += OnWriteComplete; - // Register with MxAccess - _connectionHandle = _lmxProxy.Register("ZB.MOM.WW.LmxProxy.Host"); + // Register with MxAccess using unique client name + _connectionHandle = _lmxProxy.Register(_clientName); + Log.Information("Registered with MxAccess as '{ClientName}'", _clientName); if (_connectionHandle <= 0) { diff --git a/lmxproxy/src/ZB.MOM.WW.LmxProxy.Host/MxAccess/MxAccessClient.cs b/lmxproxy/src/ZB.MOM.WW.LmxProxy.Host/MxAccess/MxAccessClient.cs index ffc57db..a0d3c4c 100644 --- a/lmxproxy/src/ZB.MOM.WW.LmxProxy.Host/MxAccess/MxAccessClient.cs +++ b/lmxproxy/src/ZB.MOM.WW.LmxProxy.Host/MxAccess/MxAccessClient.cs @@ -26,6 +26,7 @@ namespace ZB.MOM.WW.LmxProxy.Host.MxAccess private readonly bool _autoReconnect; private readonly string? _nodeName; private readonly string? _galaxyName; + private readonly string _clientName; private readonly SemaphoreSlim _readSemaphore; private readonly SemaphoreSlim _writeSemaphore; @@ -81,7 +82,8 @@ namespace ZB.MOM.WW.LmxProxy.Host.MxAccess string? probeTestTagAddress = null, int probeTimeoutMs = 5000, int maxConsecutiveTransportFailures = 3, - int degradedProbeIntervalMs = 30000) + int degradedProbeIntervalMs = 30000, + string? clientName = null) { _maxConcurrentOperations = maxConcurrentOperations; _readTimeoutMs = readTimeoutSeconds * 1000; @@ -94,6 +96,7 @@ namespace ZB.MOM.WW.LmxProxy.Host.MxAccess _probeTimeoutMs = probeTimeoutMs; _maxConsecutiveTransportFailures = maxConsecutiveTransportFailures; _degradedProbeIntervalMs = degradedProbeIntervalMs; + _clientName = clientName ?? "LmxProxy-" + Guid.NewGuid().ToString("N").Substring(0, 8); _readSemaphore = new SemaphoreSlim(maxConcurrentOperations, maxConcurrentOperations); _writeSemaphore = new SemaphoreSlim(maxConcurrentOperations, maxConcurrentOperations);