fix(communication): key gRPC client cache by (site, endpoint) so per-session failover cannot dispose shared channels
This commit is contained in:
+42
-10
@@ -81,19 +81,47 @@ public class SiteStreamGrpcClientFactoryTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetOrCreate_EndpointChanged_DisposesPriorClient()
|
||||
public void GetOrCreate_DifferentEndpointSameSite_KeepsBothClientsAlive()
|
||||
{
|
||||
// Communication-013 regression: a later edit to a site's gRPC address must
|
||||
// invalidate (and dispose) the stale cached client, so the corrected
|
||||
// endpoint takes effect without a central restart.
|
||||
// Arch review 02 (High): the cache is keyed by (site, endpoint). Both node
|
||||
// channels for a site coexist, so a per-session NodeA→NodeB failover flip
|
||||
// must NOT dispose the other endpoint's channel — another debug session may
|
||||
// be using it. (Supersedes Communication-013's dispose-on-endpoint-change.)
|
||||
using var factory = new TrackingEndpointFactory();
|
||||
|
||||
var first = (TrackingEndpointClient)factory.GetOrCreate("site-a", "http://localhost:5100");
|
||||
var second = (TrackingEndpointClient)factory.GetOrCreate("site-a", "http://localhost:5200");
|
||||
var a = (TrackingEndpointClient)factory.GetOrCreate("site-a", "http://localhost:5100");
|
||||
var b = (TrackingEndpointClient)factory.GetOrCreate("site-a", "http://localhost:5200");
|
||||
|
||||
Assert.NotSame(first, second);
|
||||
Assert.True(first.Disposed, "stale client for the old endpoint should be disposed");
|
||||
Assert.False(second.Disposed, "fresh client for the new endpoint should still be live");
|
||||
Assert.NotSame(a, b);
|
||||
Assert.False(a.Disposed, "the other endpoint's channel must stay alive");
|
||||
Assert.Same(a, factory.GetOrCreate("site-a", "http://localhost:5100")); // still cached
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGet_ReturnsCachedClientOrNull_WithoutCreating()
|
||||
{
|
||||
using var factory = new TrackingEndpointFactory();
|
||||
|
||||
Assert.Null(factory.TryGet("site-a", "http://localhost:5100"));
|
||||
var a = factory.GetOrCreate("site-a", "http://localhost:5100");
|
||||
Assert.Same(a, factory.TryGet("site-a", "http://localhost:5100"));
|
||||
Assert.Null(factory.TryGet("site-a", "http://localhost:5200")); // other endpoint not cached
|
||||
Assert.Equal(1, factory.CreatedCount); // TryGet never creates
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RemoveSiteAsync_DisposesAllEndpointsForTheSite_OnlyThatSite()
|
||||
{
|
||||
using var factory = new TrackingEndpointFactory();
|
||||
var a = (TrackingEndpointClient)factory.GetOrCreate("site-a", "http://localhost:5100");
|
||||
var b = (TrackingEndpointClient)factory.GetOrCreate("site-a", "http://localhost:5200");
|
||||
var other = (TrackingEndpointClient)factory.GetOrCreate("site-b", "http://localhost:5100");
|
||||
|
||||
await factory.RemoveSiteAsync("site-a");
|
||||
|
||||
Assert.True(a.Disposed);
|
||||
Assert.True(b.Disposed);
|
||||
Assert.False(other.Disposed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -126,7 +154,11 @@ public class SiteStreamGrpcClientFactoryTests
|
||||
private sealed class TrackingEndpointFactory : SiteStreamGrpcClientFactory
|
||||
{
|
||||
public TrackingEndpointFactory() : base(NullLoggerFactory.Instance) { }
|
||||
public int CreatedCount { get; private set; }
|
||||
protected override SiteStreamGrpcClient CreateClient(string grpcEndpoint)
|
||||
=> new TrackingEndpointClient(grpcEndpoint);
|
||||
{
|
||||
CreatedCount++;
|
||||
return new TrackingEndpointClient(grpcEndpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user