using Akka.Cluster; namespace ZB.MOM.WW.ScadaBridge.Host.Health; /// /// THE single definition of "active node" (review 01 [High]): 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. /// public static class ClusterActivityEvaluator { /// True when self is Up and no other Up member (in the role scope) is older. /// Delegates to the shared — /// one implementation for the delivery gate, the resync authority checks, and the /// heartbeat IsActive stamp (review 02 round 2, N1). /// 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 SelfIsOldest(Cluster cluster, string? role = null) => Communication.ClusterState.ActiveNodeEvaluator.SelfIsOldestUp(cluster, role); /// The oldest Up member in the role scope, or null while none is Up. Used for Primary/Standby labelling. /// The Akka cluster to evaluate. /// Optional role scope; when set, only members with this role are considered. /// The oldest Up member in the role scope, or null when none is Up. public static Member? OldestUpMember(Cluster cluster, string? role = null) { Member? oldest = null; foreach (var m in cluster.State.Members.Where(m => m.Status == MemberStatus.Up)) { if (role != null && !m.HasRole(role)) continue; if (oldest == null || m.IsOlderThan(oldest)) oldest = m; } return oldest; } }