namespace ZB.MOM.WW.OtOpcUa.Cluster; public sealed class AkkaClusterOptions { public const string SectionName = "Cluster"; /// Gets or sets the Akka system name. public string SystemName { get; set; } = "otopcua"; /// Gets or sets the hostname to bind to (default 0.0.0.0). public string Hostname { get; set; } = "0.0.0.0"; /// Gets or sets the port to listen on (default 4053). public int Port { get; set; } = 4053; /// /// Hostname advertised in cluster gossip. Must be reachable by other nodes. /// In docker-compose this is the container DNS name; in bare metal it's the /// host's stable LAN address. /// public string PublicHostname { get; set; } = "127.0.0.1"; /// Gets or sets the seed nodes for cluster bootstrapping. public string[] SeedNodes { get; set; } = Array.Empty(); /// /// Cluster roles for this node. When empty the role list comes from /// OTOPCUA_ROLES via . Allowed values: /// admin, driver, dev. /// public string[] Roles { get; set; } = Array.Empty(); /// /// How the cluster decides to down a node it can no longer reach. One of auto-down /// (default) or keep-oldest; any other value fails the host at startup. /// /// /// /// auto-down — availability. The leader among the reachable members /// downs the unreachable peer after /// . A hard crash of either /// node — including the oldest — fails over to the survivor with no operator action. /// The trade is that a genuine network partition (both nodes alive, link cut) leaves /// both sides running active until an operator restarts one. /// /// /// keep-oldest — partition-safety. The SBR resolver sacrifices the younger /// side of a split, so a partition can never run dual-active. The cost is severe for a /// two-node pair: it cannot survive a crash of the oldest node at all. Akka.NET's /// KeepOldest.OldestDecision only lets down-if-alone rescue a side holding /// >= 2 members, so the 1-vs-1 survivor downs itself and shuts down — the /// redundancy pair turns a single-node crash into a total outage. Choose this only for /// clusters of three or more nodes, or where dual-active is genuinely worse than an /// outage. /// /// public string SplitBrainResolverStrategy { get; set; } = "auto-down"; /// /// Bootstrap self-form fallback window (decision 2026-07-22, scadaproj/akka_failover.md §6.1). /// Akka only lets the FIRST listed seed form a new cluster; a non-first seed cold-starting /// while its peer is down loops on InitJoin forever ("auto-down removes the crash outage, /// not this one" — docs/Redundancy.md). When this node has waited longer than this window /// without cluster membership it joins itself — but ONLY if its own address appears in /// ; a non-seed node (e.g. a site node whose only seed is central-1) /// stays waiting, because self-forming there creates a permanent island. Default 10s /// (same-datacenter pair; a live peer answers InitJoin in milliseconds). null or a /// non-positive value disables the fallback. Accepted trade: both pair nodes cold-starting /// inside the window while mutually unreachable form two clusters — the same dual-active /// class the auto-down strategy already accepts, same recovery (restart one side). /// public TimeSpan? SelfFormAfter { get; set; } = TimeSpan.FromSeconds(10); }