fix(comm): single-gRPC-endpoint sites can go live (flip degenerates to same-node reconnect) (plan R2-02 T15)
This commit is contained in:
@@ -294,7 +294,9 @@ public sealed class SiteAlarmLiveCacheService : ISiteAlarmLiveCache
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(site.GrpcNodeAAddress) || string.IsNullOrWhiteSpace(site.GrpcNodeBAddress))
|
var nodeA = site.GrpcNodeAAddress;
|
||||||
|
var nodeB = site.GrpcNodeBAddress;
|
||||||
|
if (string.IsNullOrWhiteSpace(nodeA) && string.IsNullOrWhiteSpace(nodeB))
|
||||||
{
|
{
|
||||||
_logger.LogWarning(
|
_logger.LogWarning(
|
||||||
"Live alarm cache: site {SiteId} ({SiteIdentifier}) has no gRPC node addresses; " +
|
"Live alarm cache: site {SiteId} ({SiteIdentifier}) has no gRPC node addresses; " +
|
||||||
@@ -302,7 +304,13 @@ public sealed class SiteAlarmLiveCacheService : ISiteAlarmLiveCache
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (site.SiteIdentifier, site.GrpcNodeAAddress!, site.GrpcNodeBAddress!);
|
// Single-endpoint site (review 02 round 2, N9): reuse the sole configured
|
||||||
|
// endpoint for both slots — the aggregator's NodeA↔NodeB failover flip
|
||||||
|
// degenerates to a reconnect against the same node, which is exactly the
|
||||||
|
// right behavior for a one-node site.
|
||||||
|
var grpcA = !string.IsNullOrWhiteSpace(nodeA) ? nodeA! : nodeB!;
|
||||||
|
var grpcB = !string.IsNullOrWhiteSpace(nodeB) ? nodeB! : nodeA!;
|
||||||
|
return (site.SiteIdentifier, grpcA, grpcB);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Snapshot fan-out (the seed / reconcile) ─────────────────────────────────
|
// ── Snapshot fan-out (the seed / reconcile) ─────────────────────────────────
|
||||||
|
|||||||
@@ -258,4 +258,48 @@ public class SiteAlarmLiveCacheServiceTests : TestKit
|
|||||||
Assert.False(service.IsLive(999));
|
Assert.False(service.IsLive(999));
|
||||||
Assert.Empty(service.GetCurrentAlarms(999));
|
Assert.Empty(service.GetCurrentAlarms(999));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── R2 T15: single-endpoint sites can go live (N9) ──
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SiteWithOnlyNodeAEndpoint_StartsAnAggregator_AgainstThatEndpoint()
|
||||||
|
{
|
||||||
|
var site = new Site("Three", "site-3")
|
||||||
|
{
|
||||||
|
Id = 3,
|
||||||
|
GrpcNodeAAddress = "http://only-node:8083",
|
||||||
|
GrpcNodeBAddress = null, // single-endpoint site — pre-fix: never goes live
|
||||||
|
};
|
||||||
|
|
||||||
|
var siteRepo = Substitute.For<ISiteRepository>();
|
||||||
|
siteRepo.GetSiteByIdAsync(3, Arg.Any<CancellationToken>()).Returns(site);
|
||||||
|
|
||||||
|
var instanceRepo = Substitute.For<ITemplateEngineRepository>();
|
||||||
|
instanceRepo.GetInstancesBySiteIdAsync(3, Arg.Any<CancellationToken>())
|
||||||
|
.Returns(new List<Instance>());
|
||||||
|
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
services.AddScoped(_ => siteRepo);
|
||||||
|
services.AddScoped(_ => instanceRepo);
|
||||||
|
var provider = services.BuildServiceProvider();
|
||||||
|
|
||||||
|
var options = Options.Create(new CommunicationOptions
|
||||||
|
{
|
||||||
|
LiveAlarmCacheLinger = TimeSpan.FromSeconds(30),
|
||||||
|
LiveAlarmCacheReconcileInterval = TimeSpan.FromMinutes(10),
|
||||||
|
});
|
||||||
|
var comm = new CommunicationService(Options.Create(new CommunicationOptions()),
|
||||||
|
NullLogger<CommunicationService>.Instance);
|
||||||
|
var factory = new CountingFactory();
|
||||||
|
|
||||||
|
var service = new SiteAlarmLiveCacheService(
|
||||||
|
provider, comm, factory, options, NullLogger<SiteAlarmLiveCacheService>.Instance);
|
||||||
|
service.SetActorSystem(Sys);
|
||||||
|
|
||||||
|
using var sub = service.Subscribe(3, () => { });
|
||||||
|
|
||||||
|
// Pre-fix: ResolveSiteAsync required BOTH endpoints and bailed → never live, no client.
|
||||||
|
AwaitCondition(() => service.IsLive(3), TimeSpan.FromSeconds(5));
|
||||||
|
Assert.True(factory.GetOrCreateCount >= 1); // a client was created for the single endpoint
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user