Files
lmxopcua/archreview/plans/FOLLOWUP-11-crit1-sbr-premise.md
T

4.1 KiB

Follow-up #11 — Correct Critical 1's premise: SBR was already active on master (not NoDowning)

Status: OPEN · Surfaced by: the #9 hard-kill failover test's negative control on fix/archreview-crit1-split-brain-resolver a25c9ed0 · Task: TaskCreate #11 · Severity: Low (docs/rationale accuracy; the code change is harmless) · Effort: S. See STATUS.md.

The finding

Critical 1 (03/S1) claimed: the akka.conf split-brain-resolver block was inert, the cluster ran Akka's default NoDowning, and setting the typed ClusterOptions.SplitBrainResolver = KeepOldestOption { DownIfAlone = true } is what activated the resolver so hard-crashed nodes fail over.

That premise is inaccurate. The cluster was never running NoDowning; hard-crash failover already worked on master before Critical 1.

Evidence (empirical + source)

  1. The #9 failover test's negative control. With the typed SplitBrainResolver option removed, the hard-kill failover test still passes — node B downs the alone crashed oldest node and takes over. Only forcing explicit NoDowning (akka.cluster.downing-provider-class = "") makes the test time out (node stuck Up-but-Unreachable). So removing Critical 1's change does not disable failover; only an explicit NoDowning does.

  2. Akka.Cluster.Hosting default (v1.5.51). WithClustering(ClusterOptions) applies SplitBrainResolverOption.Default when SplitBrainResolver is null — i.e. it enables the SBR downing provider by default. That provider then reads the akka.cluster.split-brain-resolver HOCON. (Confirmed by IL inspection of Akka.Cluster.Hosting.dll: the null branch loads …Default and calls .Apply(builder, …).)

  3. The akka.conf keep-oldest block pre-existed Critical 1. git show master:…/Resources/akka.conf already contains split-brain-resolver { active-strategy = "keep-oldest"; stable-after = 15s; keep-oldest { down-if-alone = on } }. Critical 1's diff to akka.conf added only comments (plus the typed option in ServiceCollectionExtensions.BuildClusterOptions).

Net: the effective resolver on master was already keep-oldest + down-if-alone (active via Akka.Cluster.Hosting's default provider reading the pre-existing akka.conf block). Critical 1's typed option produces the same effective behavior — it is reinforcing/explicit, not the activator.

What is (and isn't) wrong

  • Wrong: the claim that SBR was inert / the cluster ran NoDowning / hard-crash failover was broken before Critical 1. This appears in the BuildClusterOptions XML comment, the akka.conf comment Critical 1 added, and the Redundancy.md / Architecture.md wording Critical 1 touched.
  • Fine to keep: the typed KeepOldestOption { DownIfAlone = true } itself. Making the strategy explicit in code (independent of the framework's default) is defensible belt-and-suspenders and matches the akka.conf block. Do not revert the code.

Proposed correction

  1. Rewrite the BuildClusterOptions XML comment to state accurately: Akka.Cluster.Hosting enables an SBR downing provider by default (applies SplitBrainResolverOption.Default when the option is null), which reads the akka.conf keep-oldest block; this typed option makes the strategy explicit in code rather than relying on the framework default — it is not the sole activator.
  2. Correct the akka.conf comment (drop "inert" / "NoDowning" framing).
  3. Correct docs/Redundancy.md + docs/v2/Architecture.md wording Critical 1 added — do not describe Critical 1 as "fixing a NoDowning bug."
  4. Keep the #9 HardKillFailoverTests as the standing guard — it verifies the failover OUTCOME regardless of activation path, and its comments already reflect the corrected understanding.

Decision needed

Confirm the correction (docs + comments only, no code revert), or flag if there is production context where the akka.conf block or the Hosting default would not apply (none found — production uses the same WithClustering path and ships the same akka.conf). Bundle with the docs branch (low-risk).