test(redundancy): cover stale-probe-not-demoted branch + make _probeFreshnessWindow readonly (code-review)
This commit is contained in:
@@ -59,7 +59,7 @@ public sealed class OpcUaPublishActor : ReceiveActor
|
||||
private readonly Phase7Applier? _applier;
|
||||
private readonly IActorRef? _dbHealthProbe;
|
||||
private readonly TimeSpan _staleWindow;
|
||||
private TimeSpan _probeFreshnessWindow;
|
||||
private readonly TimeSpan _probeFreshnessWindow;
|
||||
private readonly Akka.Cluster.Cluster _cluster = Akka.Cluster.Cluster.Get(Context.System);
|
||||
private readonly ILoggingAdapter _log = Context.GetLogger();
|
||||
|
||||
|
||||
@@ -355,6 +355,50 @@ public sealed class OpcUaPublishActorTests : RuntimeActorTestBase
|
||||
duration: TimeSpan.FromMilliseconds(500));
|
||||
}
|
||||
|
||||
/// <summary>Verifies branch (3) of <c>OpcUaProbeOk()</c>: a peer's NEGATIVE verdict about this node
|
||||
/// that has aged past <c>_probeFreshnessWindow</c> is given the benefit of the doubt (the peer's
|
||||
/// verdict aged out → don't demote), so the node still publishes the HEALTHY level. With a 1ms
|
||||
/// freshness window and a 30ms wait before the recompute, the cached <c>Ok==false</c> verdict is
|
||||
/// reliably stale by the time the level is computed, so <c>OpcUaProbeOk()</c> returns true and a
|
||||
/// healthy primary-leader computes inputs (true, true, false) → 240, +10 leader → 250 — NOT the 0
|
||||
/// that a still-fresh negative verdict would produce (cf. <see cref="Probe_false_about_me_with_healthy_db_publishes_0"/>).</summary>
|
||||
[Fact]
|
||||
public void Stale_probe_verdict_is_not_demoted_publishes_healthy()
|
||||
{
|
||||
var publisher = new RecordingPublisher();
|
||||
var local = NodeId.Parse("primary-node");
|
||||
var probe = Sys.ActorOf(Akka.Actor.Props.Create(() =>
|
||||
new StubDbHealth(new DbHealthProbeActor.DbHealthStatus(true, DateTime.UtcNow, null))));
|
||||
var actor = Sys.ActorOf(OpcUaPublishActor.PropsForTests(
|
||||
serviceLevel: publisher, localNode: local, dbHealthProbe: probe,
|
||||
staleWindow: TimeSpan.FromSeconds(30),
|
||||
probeFreshnessWindow: TimeSpan.FromMilliseconds(1)));
|
||||
|
||||
// Seed a healthy DB sample and a NEGATIVE peer verdict about me. (No RedundancyStateChanged yet,
|
||||
// so no level is computed from these — the verdict just gets stamped with the receive time.)
|
||||
actor.Tell(new DbHealthProbeActor.DbHealthStatus(true, DateTime.UtcNow, null));
|
||||
actor.Tell(new PeerOpcUaProbeActor.OpcUaProbeResult(local, Ok: false));
|
||||
|
||||
// Let the verdict age WELL past the 1ms freshness window before any level is computed. The 30ms
|
||||
// wait is >> 1ms, so the verdict is reliably stale by the time OpcUaProbeOk() runs — no reliance
|
||||
// on sub-ms clock ties.
|
||||
ExpectNoMsg(TimeSpan.FromMilliseconds(30));
|
||||
|
||||
// Now trigger the recompute with a fresh healthy primary-leader snapshot. By now _probeAboutMe is
|
||||
// >1ms old, so branch (3) ignores the negative verdict and OpcUaProbeOk() returns true.
|
||||
actor.Tell(new RedundancyStateChanged(
|
||||
Nodes: new[]
|
||||
{
|
||||
new NodeRedundancyState(local, RedundancyRole.Primary,
|
||||
IsClusterLeader: true, IsRoleLeaderForDriver: true, DateTime.UtcNow),
|
||||
},
|
||||
CorrelationId.NewId()));
|
||||
|
||||
// Healthy (250), NOT 0 — proves an aged negative verdict does not demote.
|
||||
AwaitAssert(() => publisher.Levels.ShouldContain((byte)250),
|
||||
duration: TimeSpan.FromMilliseconds(500));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that with no peer probe result ever received, <c>OpcUaProbeOk()</c> defaults
|
||||
/// to <c>true</c> (benefit of the doubt / single-node). A healthy primary-leader thus computes
|
||||
/// inputs (true, true, false) → 240, +10 leader → 250.</summary>
|
||||
|
||||
Reference in New Issue
Block a user