fix(mesh-phase5): dial supervisor — re-dial on endpoint change, pin no-restart invariant, throttle skip warnings

Claude-Session: https://claude.ai/code/session_01GASWkNEi68FSCtvr6rLoEW
This commit is contained in:
Joseph Doherty
2026-07-23 16:43:09 -04:00
parent ffb75b7854
commit 84fa2e1e43
3 changed files with 91 additions and 13 deletions
@@ -70,6 +70,44 @@ public sealed class TelemetryDialSupervisorTests : TestKit
// node-3 gets a fresh dialer.
var invocations = dial.WaitForCount(3, Timeout);
invocations.ShouldContain(i => i.Target.NodeId == "node-3" && i.CorrelationId == "central-node-3-1");
// node-2 is present in BOTH refreshes with an unchanged endpoint: it must NOT be restarted, or
// its live stream would be dropped on every refresh. Exactly one dial invocation for it.
ExpectNoMsg(TimeSpan.FromMilliseconds(200));
dial.Invocations.Count(i => i.Target.NodeId == "node-2").ShouldBe(1);
}
[Fact]
public void Refresh_redials_a_node_whose_endpoint_changed_but_not_an_unchanged_one()
{
var dial = new FakeDialLoop();
var current = new List<TelemetryDialTarget>
{
new("node-1", "http://old-host:5300"),
new("node-2", "http://h2:5300"),
};
var actor = Spawn(dial, () => current.ToArray());
dial.WaitForCount(2, Timeout);
var node1Original = dial.Invocations.Single(i => i.Target.NodeId == "node-1");
// node-1 re-provisioned to a new host; node-2 unchanged.
current.Clear();
current.Add(new TelemetryDialTarget("node-1", "http://new-host:5300"));
current.Add(new TelemetryDialTarget("node-2", "http://h2:5300"));
actor.Tell(new TelemetryDialSupervisor.RefreshNodes());
// The old node-1 stream is torn down and a fresh dialer opens at the NEW endpoint (gen 2).
AwaitCondition(() => node1Original.Ct.IsCancellationRequested, Timeout);
var invocations = dial.WaitForCount(3, Timeout);
invocations.ShouldContain(i =>
i.Target.NodeId == "node-1"
&& i.Target.Endpoint == "http://new-host:5300"
&& i.CorrelationId == "central-node-1-2");
// node-2's endpoint did not change → still exactly one dial invocation (no churn).
ExpectNoMsg(TimeSpan.FromMilliseconds(200));
dial.Invocations.Count(i => i.Target.NodeId == "node-2").ShouldBe(1);
}
[Fact]