using Akka.Cluster; using ZB.MOM.WW.Health.Akka; namespace ZB.MOM.WW.ScadaBridge.Communication.ClusterState; /// /// THE single definition of "active node" (review 01 [High]; review 02 round 2 [Critical] N1): /// a node is active when it is the OLDEST Up member (optionally within a role scope) — i.e. /// the member the ClusterSingletonManager places singletons on. Cluster LEADERSHIP (lowest /// address) is an Akka-internal concept that diverges from singleton placement permanently /// once the original first node restarts and rejoins; every product-level active/standby /// decision must use this evaluator, never cluster.State.Leader. /// /// Lives in Communication (not Host) so SiteCommunicationActor can default to it — /// Host cannot be referenced from there. The Host's /// ClusterActivityEvaluator.SelfIsOldest delegates here, so the S&F delivery gate /// (IClusterNodeProvider.SelfIsPrimary) and the heartbeat IsActive stamp share one /// implementation. /// /// It also backed SiteReplicationActor's resync authority checks until LocalDb /// Phase 2 deleted that actor. Those checks existed because the bespoke resync applied a /// destructive delete-all-then-insert-all, so running it in the wrong direction wiped a /// live store-and-forward buffer. LocalDb's snapshot resync merges per row under /// last-writer-wins and never deletes, so there is no destructive apply left to gate — the /// evaluator survives for the delivery gate and the heartbeat, which still genuinely need /// a single active node. /// /// /// public static class ActiveNodeEvaluator { /// True when self is Up and no other Up member (in the role scope) is older. /// The Akka cluster to evaluate. /// Optional role scope; when set, only members with this role are considered. /// true when self is Up and the oldest Up member in the role scope. /// /// Delegates to the shared /// (ZB.MOM.WW.Health.Akka 0.3.0), which is this evaluator promoted into the shared library /// after OtOpcUa independently needed the same rule and wrote its own third copy. The rule now has /// one implementation family-wide; this method survives as Communication's entry point into it, so /// the delivery gate and heartbeat keep their existing call sites and layering. /// public static bool SelfIsOldestUp(Cluster cluster, string? role = null) => ClusterActiveNode.SelfIsActive(cluster, role); }