From 1320f3fa089501fc39dcfa2cde871c2ed40c5c7a Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Sat, 15 Aug 2026 12:07:05 -0400 Subject: [PATCH] perf(runtime): raise opcua pinned dispatcher throughput 1 -> 100 --- src/Core/ZB.MOM.WW.OtOpcUa.Cluster/Resources/akka.conf | 9 ++++++++- .../ServiceCollectionExtensions.cs | 10 ++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/Resources/akka.conf b/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/Resources/akka.conf index 56648f0b..93f1a0aa 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/Resources/akka.conf +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Cluster/Resources/akka.conf @@ -109,8 +109,15 @@ akka { # Pinned dispatcher used by OpcUaPublishActor (Task 44) so the OPC UA SDK sees # only one thread per actor instance — its session/subscription locks expect # strict single-threaded access. +# +# throughput is NOT what provides that guarantee: the PinnedDispatcher does, by +# giving the actor its own dedicated thread. throughput only sets how many queued +# messages drain per scheduling run, so 100 removes a mailbox re-schedule +# round-trip per message on the hottest actor in the process with zero change to +# concurrency. Must stay in sync with the inline HOCON copy in +# ServiceCollectionExtensions.WithOtOpcUaRuntimeActors. opcua-synchronized-dispatcher { type = "PinnedDispatcher" executor = "thread-pool-executor" - throughput = 1 + throughput = 100 } diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/ServiceCollectionExtensions.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/ServiceCollectionExtensions.cs index 3ce81372..98daf31a 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/ServiceCollectionExtensions.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/ServiceCollectionExtensions.cs @@ -302,12 +302,18 @@ public static class ServiceCollectionExtensions // bootstrap their own HOCON (e.g. ServiceCollectionExtensionsTests) wouldn't pick it up // — OpcUaPublishActor.Props pins itself to opcua-synchronized-dispatcher and Akka throws // ConfigurationException if it doesn't exist. Prepend a fallback so the runtime extension - // is self-contained. + // is self-contained. Keep this block identical to the akka.conf one. + // + // The OPC UA SDK's single-threaded session/subscription requirement is guaranteed by the + // PinnedDispatcher alone (a dedicated thread per actor instance), not by throughput. + // throughput only sets how many queued messages drain per scheduling run, so 100 removes a + // mailbox re-schedule round-trip per message on the hottest actor in the process with zero + // change to concurrency. builder.AddHocon(@" opcua-synchronized-dispatcher { type = ""PinnedDispatcher"" executor = ""thread-pool-executor"" - throughput = 1 + throughput = 100 } ", HoconAddMode.Prepend);