fix(reconcile): batch startup reconcile into fixed round trips, bound central work by QueryTimeout, retry failed site passes with back-off (review 2026-10-21 03/P-1)

This commit is contained in:
Joseph Doherty
2026-10-22 06:25:57 -04:00
parent d6a9a79c27
commit 7c83a4aa94
9 changed files with 638 additions and 130 deletions
@@ -40,13 +40,17 @@ public class CentralCommunicationActorReconcileTests : TestKit
{
new(2, "inst-B", "rev2", "dep-B", true),
});
deploymentRepo.GetDeployedSnapshotByInstanceIdAsync(2, Arg.Any<CancellationToken>())
.Returns(new DeployedConfigSnapshot("dep-B", "rev2", "{\"cfg\":\"B\"}"));
deploymentRepo.StagePendingIfAbsentAsync(
Arg.Any<int>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>(),
Arg.Any<string>(), Arg.Any<DateTimeOffset>(), Arg.Any<DateTimeOffset>(),
Arg.Any<CancellationToken>())
.Returns(true);
deploymentRepo.GetDeployedSnapshotsByInstanceIdsAsync(
Arg.Any<IReadOnlyCollection<int>>(), Arg.Any<CancellationToken>())
.Returns(new Dictionary<int, DeployedConfigSnapshot>
{
[2] = new("dep-B", "rev2", "{\"cfg\":\"B\"}") { InstanceId = 2 },
});
// Every candidate is staged fresh: the batch hands each one back as its live row.
deploymentRepo.StagePendingBatchIfAbsentAsync(
Arg.Any<IReadOnlyList<PendingDeployment>>(), Arg.Any<DateTimeOffset>(), Arg.Any<CancellationToken>())
.Returns(ci => (IReadOnlyDictionary<int, PendingDeployment>)ci.ArgAt<IReadOnlyList<PendingDeployment>>(0)
.ToDictionary(p => p.InstanceId));
var options = Options.Create(new CommunicationOptions
{