From 4c78dcd35800dd9634690147c0d7c2440cd87356 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Mon, 15 Jun 2026 13:33:34 -0400 Subject: [PATCH] feat(redundancy): wire dbHealth into OpcUaPublishActor + spawn PeerProbeSupervisor per node --- .../ServiceCollectionExtensions.cs | 15 ++++++++++++++- .../ServiceCollectionExtensionsTests.cs | 3 +++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/ServiceCollectionExtensions.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/ServiceCollectionExtensions.cs index dea78446..c97131ac 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/ServiceCollectionExtensions.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/ServiceCollectionExtensions.cs @@ -32,6 +32,7 @@ public static class ServiceCollectionExtensions public const string HistorianAdapterActorName = "historian-adapter"; public const string DependencyMuxActorName = "dependency-mux"; public const string OpcUaPublishActorName = "opcua-publish"; + public const string PeerProbeSupervisorName = "peer-probe-supervisor"; /// /// Registers shared runtime services. Currently binds @@ -213,10 +214,19 @@ public static class ServiceCollectionExtensions serviceLevel: serviceLevel, localNode: roleInfo.LocalNode, dbFactory: dbFactory, - applier: applier), + applier: applier, + dbHealthProbe: dbHealth), OpcUaPublishActorName); registry.Register(publishActor); + // Per-node peer-probe supervisor — keeps one OPC UA TCP probe per OTHER non-Detached + // driver node, so every node is continuously probed by all its peers. OpcUaPublishActor + // consumes the resulting probe verdicts to learn this node's own reachability. + var peerProbes = system.ActorOf( + PeerProbeSupervisor.Props(roleInfo.LocalNode), + PeerProbeSupervisorName); + registry.Register(peerProbes); + var driverHost = system.ActorOf( DriverHostActor.Props(dbFactory, roleInfo.LocalNode, coordinator: null, driverFactory: driverFactory, localRoles: roleInfo.LocalRoles, @@ -246,3 +256,6 @@ public sealed class DbHealthProbeActorKey { } public sealed class HistorianAdapterActorKey { } public sealed class DependencyMuxActorKey { } public sealed class OpcUaPublishActorKey { } + +/// Marker key for the per-node PeerProbeSupervisor. +public sealed class PeerProbeSupervisorKey { } diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/ServiceCollectionExtensionsTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/ServiceCollectionExtensionsTests.cs index bdcd7798..716be899 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/ServiceCollectionExtensionsTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/ServiceCollectionExtensionsTests.cs @@ -50,17 +50,20 @@ public sealed class ServiceCollectionExtensionsTests var historian = host.Services.GetRequiredService>(); var mux = host.Services.GetRequiredService>(); var publish = host.Services.GetRequiredService>(); + var peerProbes = host.Services.GetRequiredService>(); driverHost.ActorRef.ShouldNotBeNull(); dbHealth.ActorRef.ShouldNotBeNull(); historian.ActorRef.ShouldNotBeNull(); mux.ActorRef.ShouldNotBeNull(); publish.ActorRef.ShouldNotBeNull(); + peerProbes.ActorRef.ShouldNotBeNull(); driverHost.ActorRef.Path.Name.ShouldBe(ServiceCollectionExtensions.DriverHostActorName); dbHealth.ActorRef.Path.Name.ShouldBe(ServiceCollectionExtensions.DbHealthProbeActorName); historian.ActorRef.Path.Name.ShouldBe(ServiceCollectionExtensions.HistorianAdapterActorName); mux.ActorRef.Path.Name.ShouldBe(ServiceCollectionExtensions.DependencyMuxActorName); publish.ActorRef.Path.Name.ShouldBe(ServiceCollectionExtensions.OpcUaPublishActorName); + peerProbes.ActorRef.Path.Name.ShouldBe(ServiceCollectionExtensions.PeerProbeSupervisorName); } finally {