namespace ZB.MOM.WW.OtOpcUa.ControlPlane.Redundancy; /// /// A read-only view of the cluster's driver membership, as the manual-failover control sees it. /// /// /// The current driver Primary — the oldest Up driver member, the same node /// names — or when /// there is no Up driver member at all. /// /// /// Every Up driver member, oldest first. A manual failover needs at least two: with one /// there is nothing to fail over to. /// public sealed record ManualFailoverSnapshot(string? PrimaryAddress, IReadOnlyList DriverAddresses) { /// Whether a failover can be performed — i.e. a peer exists to take over. public bool CanFailOver => DriverAddresses.Count >= 2; } /// /// Operator-initiated, graceful swap of the driver Primary. /// /// /// /// The mechanism is a cluster Leave of the current Primary, never a Down. The /// leaving node hands its singletons over through the normal cluster-leave phases, /// CoordinatedShutdown runs, ActorSystemTerminationWatchdog exits the process, /// the service supervisor restarts it, and it rejoins as the youngest member — so the /// survivor becomes the oldest Up driver member and therefore Primary. ServiceLevel 250 /// moves with it and OPC UA clients re-select. /// /// /// Mesh-scope caveat. Until the per-cluster mesh work lands /// (docs/plans/2026-07-21-per-cluster-mesh-design.md) the Primary is elected once per /// Akka mesh, not per application Cluster row. On a fleet running several application /// clusters in one mesh this acts on the whole mesh's Primary. The UI says so. /// /// public interface IManualFailoverService { /// Reads the current driver membership from live cluster state. ManualFailoverSnapshot GetSnapshot(); /// /// Gracefully removes the current driver Primary from the cluster so its peer takes over. /// /// The authenticated identity performing the failover; recorded in the audit event. /// /// The address of the node asked to leave, or when the failover was /// refused because no driver peer exists (in which case nothing was changed and nothing audited). /// Task FailOverDriverPrimaryAsync(string actor); }