using Akka.Cluster; 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 BOTH SiteCommunicationActor and /// SiteReplicationActor can default to it — Host cannot be referenced from either. /// The Host's ClusterActivityEvaluator.SelfIsOldest delegates here, so the S&F /// delivery gate (IClusterNodeProvider.SelfIsPrimary), the resync authority checks, /// and the heartbeat IsActive stamp all share one implementation. /// /// 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. public static bool SelfIsOldestUp(Cluster cluster, string? role = null) { var self = cluster.SelfMember; if (self.Status != MemberStatus.Up) return false; if (role != null && !self.HasRole(role)) return false; return cluster.State.Members .Where(m => m.Status == MemberStatus.Up) .Where(m => role == null || m.HasRole(role)) .All(m => m.UniqueAddress.Equals(self.UniqueAddress) || self.IsOlderThan(m)); } }