fix(communication): resolve Communication-012..015 — endpoint-aware gRPC client cache, address-change recreation, correlation-id validation, node-flip tests

This commit is contained in:
Joseph Doherty
2026-05-17 03:18:17 -04:00
parent a78c3bcb6f
commit a768135237
7 changed files with 289 additions and 14 deletions
@@ -20,6 +20,14 @@ public class SiteStreamGrpcClient : IAsyncDisposable, IDisposable
private readonly ILogger? _logger;
private readonly ConcurrentDictionary<string, CancellationTokenSource> _subscriptions = new();
/// <summary>
/// The gRPC endpoint (site node address) this client is bound to. The
/// <see cref="SiteStreamGrpcClientFactory"/> compares this against the requested
/// endpoint so a NodeA→NodeB failover flip (or a site address edit) is honoured
/// rather than served stale from cache.
/// </summary>
public virtual string Endpoint { get; } = string.Empty;
/// <summary>
/// The HTTP/2 keepalive ping delay actually applied to this client's channel.
/// Exposed for tests verifying that <see cref="CommunicationOptions"/> is honoured.
@@ -44,6 +52,7 @@ public class SiteStreamGrpcClient : IAsyncDisposable, IDisposable
/// </summary>
public SiteStreamGrpcClient(string endpoint, ILogger logger, CommunicationOptions options)
{
Endpoint = endpoint;
KeepAlivePingDelay = options.GrpcKeepAlivePingDelay;
KeepAlivePingTimeout = options.GrpcKeepAlivePingTimeout;
_channel = GrpcChannel.ForAddress(endpoint, new GrpcChannelOptions
@@ -67,6 +76,16 @@ public class SiteStreamGrpcClient : IAsyncDisposable, IDisposable
{
}
/// <summary>
/// Protected constructor for unit testing — records the endpoint without
/// opening a real gRPC channel, so endpoint-aware factory behaviour can be
/// exercised by test doubles.
/// </summary>
protected SiteStreamGrpcClient(string endpoint)
{
Endpoint = endpoint;
}
/// <summary>
/// Creates a test-only instance that has no gRPC channel. Used to test
/// Unsubscribe and Dispose behavior without needing a real endpoint.