fix(auditlog): prune per-site reconciliation state and pull-channel caches for sites absent from the enumeration; bound the abandoned-id set (review 2026-10-21 06/MEM-2)

This commit is contained in:
Joseph Doherty
2026-10-22 06:40:54 -04:00
parent 9e7e19c3c1
commit 38167bca88
7 changed files with 299 additions and 12 deletions
@@ -214,6 +214,8 @@ public sealed class GrpcPullSiteCallsClient : IPullSiteCallsClient
string siteId, CancellationToken ct)
{
var sites = await _sites.EnumerateAsync(ct).ConfigureAwait(false);
// Review 2026-10-21 06/MEM-2: drop channels for sites/addresses no longer enumerated.
(_invoker as IGrpcChannelCachePruner)?.RetainOnly(sites);
foreach (var site in sites)
{
if (string.Equals(site.SiteId, siteId, StringComparison.Ordinal) &&
@@ -275,13 +277,13 @@ public sealed class GrpcPullSiteCallsClient : IPullSiteCallsClient
/// issues the unary <c>PullSiteCallsAsync</c> call. The cache is keyed by
/// endpoint string, so a changed site address (NodeA→NodeB failover flip / an
/// edited gRPC address) is reached as soon as the resolver hands the new endpoint
/// to <see cref="InvokeAsync"/>. The channel for a previous address lingers idle
/// until <see cref="Dispose"/> (idle channels hold no streams — a minor cache
/// footprint cost, not a correctness or liveness gap). Sibling of
/// to <see cref="InvokeAsync"/>. The channel for a previous address (or a deleted
/// site) is evicted and disposed by <see cref="RetainOnly"/>, which the client calls
/// with every site enumeration (review 2026-10-21 06/MEM-2). Sibling of
/// <see cref="GrpcPullAuditEventsInvoker"/>.
/// </summary>
public sealed class GrpcPullSiteCallsInvoker
: GrpcPullSiteCallsClient.IPullSiteCallsInvoker, IDisposable
: GrpcPullSiteCallsClient.IPullSiteCallsInvoker, IDisposable, IGrpcChannelCachePruner
{
private readonly ConcurrentDictionary<(string Site, string Endpoint), GrpcChannel> _channels = new();
private readonly CommunicationOptions _options;
@@ -358,6 +360,10 @@ public sealed class GrpcPullSiteCallsInvoker
},
}.WithMessageLimits().WithSiteCredentials(_pskProvider, siteId));
/// <inheritdoc />
public void RetainOnly(IReadOnlyList<SiteEntry> liveSites) =>
GrpcChannelCachePruning.RetainOnly(_channels, liveSites);
/// <summary>Disposes all cached channels.</summary>
public void Dispose()
{