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. public static bool SelfIsOldest(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)); } /// The oldest Up member in the role scope, or null while none is Up. Used for Primary/Standby labelling. 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; } }