fix(communication): failback probation is time-boxed and cleared on address change (review 2026-10-21 03/P-2 follow-up)

This commit is contained in:
Joseph Doherty
2026-10-22 08:28:11 -04:00
parent f4d328aecc
commit 4456a04e81
3 changed files with 60 additions and 2 deletions
@@ -86,6 +86,46 @@ public class SitePairChannelProviderFailbackTests
Assert.Equal(TimeSpan.FromSeconds(1), provider.GetFailbackBackoff(Site));
}
/// <summary>Verifies that probation expires: a quiet site whose NodeA dies long after the failback starts again at the initial backoff.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ProbationOlderThanTheWindow_IsTreatedAsConfirmed_FirstProbeAtInitialBackoff()
{
using var provider = Build((_, _) => Task.FromResult(true));
provider.FailbackProbationWindow = TimeSpan.FromMilliseconds(100);
provider.UpdateSite(Site, "http://localhost:15321", "http://localhost:15322");
await CallRejectedByA(provider);
Assert.True(await provider.TryFailbackAsync(Site, CancellationToken.None));
await CallRejectedByA(provider);
Assert.Equal(TimeSpan.FromSeconds(2), provider.GetFailbackBackoff(Site));
// Fail back on probation, then the site goes quiet (no real call) past the window.
Assert.True(await provider.TryFailbackAsync(Site, CancellationToken.None));
await Task.Delay(300);
await CallRejectedByA(provider);
Assert.Equal(TimeSpan.FromSeconds(1), provider.GetFailbackBackoff(Site));
}
/// <summary>Verifies that an address change clears probation.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task AddressChange_ClearsProbation()
{
using var provider = Build((_, _) => Task.FromResult(true));
provider.UpdateSite(Site, "http://localhost:15331", "http://localhost:15332");
await CallRejectedByA(provider);
Assert.True(await provider.TryFailbackAsync(Site, CancellationToken.None));
await CallRejectedByA(provider);
Assert.True(await provider.TryFailbackAsync(Site, CancellationToken.None));
provider.UpdateSite(Site, "http://localhost:15333", "http://localhost:15332");
await CallRejectedByA(provider);
Assert.Equal(TimeSpan.FromSeconds(1), provider.GetFailbackBackoff(Site));
}
/// <summary>Verifies that the production probe treats a not-ready (not-dispatched Unavailable) site as not ready.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]