docs(archreview #11): correct Critical 1 premise — SBR was already active on master, not NoDowning

Akka.Cluster.Hosting's WithClustering enables an SBR downing provider by default
(applies SplitBrainResolverOption.Default when ClusterOptions.SplitBrainResolver is
null), which reads the pre-existing akka.conf keep-oldest block. So the cluster was
NOT running NoDowning before Critical 1 and hard-crash failover already worked — the
typed KeepOldestOption is reinforcing/explicit-in-code, not the sole activator.

Corrects the inaccurate 'HOCON inert / NoDowning / never fails over' framing in:
- ServiceCollectionExtensions.BuildClusterOptions XML comment
- akka.conf split-brain-resolver comment
- docs/Redundancy.md Split-brain section
- SplitBrainResolverActivationTests summary + assertion message (+ method rename)

No code revert (the typed option is correct belt-and-suspenders). Cluster.Tests 29/29.
Surfaced by the #9 hard-kill failover negative control.
This commit is contained in:
Joseph Doherty
2026-07-08 22:34:20 -04:00
parent a25c9ed097
commit eaf78aad90
4 changed files with 49 additions and 37 deletions
@@ -6,12 +6,15 @@ using Xunit;
namespace ZB.MOM.WW.OtOpcUa.Cluster.Tests;
/// <summary>
/// Guards the split-brain-resolver ACTIVATION (arch-review 03/S1). The <c>split-brain-resolver</c>
/// HOCON block is inert on its own; the resolver only runs when the typed
/// <see cref="ClusterOptions.SplitBrainResolver"/> is set. These tests assert
/// <see cref="ServiceCollectionExtensions.BuildClusterOptions"/> registers it, so a future refactor
/// cannot silently drop back to Akka's default NoDowning (the exact "built-but-never-wired" failure
/// mode this finding described).
/// Guards that the split-brain-resolver strategy stays set <b>explicitly in code</b> (arch-review 03/S1,
/// premise corrected — see #11). Akka.Cluster.Hosting already enables an SBR downing provider by default
/// (it applies <c>SplitBrainResolverOption.Default</c> when <see cref="ClusterOptions.SplitBrainResolver"/>
/// is null, which reads the akka.conf <c>keep-oldest</c> block), so the cluster is not NoDowning even
/// without the typed option. These tests assert
/// <see cref="ServiceCollectionExtensions.BuildClusterOptions"/> sets the typed
/// <c>KeepOldestOption { DownIfAlone = true }</c> so the strategy is explicit in code rather than relying on
/// the framework default — a future refactor cannot silently drop that explicitness. (Failover behaviour is
/// separately verified by <c>HardKillFailoverTests</c>, which passes regardless of activation path.)
/// </summary>
public sealed class SplitBrainResolverActivationTests
{
@@ -21,14 +24,14 @@ public sealed class SplitBrainResolverActivationTests
Roles = new[] { "admin", "driver" },
};
/// <summary>Verifies the cluster options carry a split-brain resolver (provider gets activated).</summary>
/// <summary>Verifies the cluster options carry an explicit split-brain resolver strategy.</summary>
[Fact]
public void BuildClusterOptions_activates_a_split_brain_resolver()
public void BuildClusterOptions_sets_an_explicit_split_brain_resolver()
{
var options = ServiceCollectionExtensions.BuildClusterOptions(SampleOptions());
options.SplitBrainResolver.ShouldNotBeNull(
"SplitBrainResolver must be set — without it the cluster runs default NoDowning and never fails over on a hard crash.");
"SplitBrainResolver must be set explicitly in code — keeps the strategy independent of Akka.Cluster.Hosting's default provider rather than relying on it.");
}
/// <summary>Verifies the resolver is keep-oldest with down-if-alone (correct for a 2-node pair).</summary>