perf(comm): coalesce live-alarm delta publishes (250ms window, 0 = legacy) (plan R2-02 T10)
This commit is contained in:
@@ -86,4 +86,22 @@ public class CommunicationOptionsValidatorTests
|
||||
Assert.True(result.Failed);
|
||||
Assert.Contains("LiveAlarmCacheMaxSubscribersPerSite", result.FailureMessage);
|
||||
}
|
||||
|
||||
// ── R2 T10: live-delta publish-coalescing window (N6) ────────────────────────
|
||||
|
||||
[Fact]
|
||||
public void ZeroLiveAlarmCachePublishCoalesce_IsValid()
|
||||
{
|
||||
// Zero = publish per delta (legacy behavior).
|
||||
var result = Validate(new CommunicationOptions { LiveAlarmCachePublishCoalesce = TimeSpan.Zero });
|
||||
Assert.True(result.Succeeded, result.FailureMessage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NegativeLiveAlarmCachePublishCoalesce_IsRejected()
|
||||
{
|
||||
var result = Validate(new CommunicationOptions { LiveAlarmCachePublishCoalesce = TimeSpan.FromMilliseconds(-1) });
|
||||
Assert.True(result.Failed);
|
||||
Assert.Contains("LiveAlarmCachePublishCoalesce", result.FailureMessage);
|
||||
}
|
||||
}
|
||||
|
||||
+42
-2
@@ -137,7 +137,7 @@ public class SiteAlarmAggregatorActorTests : TestKit
|
||||
}
|
||||
|
||||
private (IActorRef Actor, SeedStub Seed, PublishSink Sink, MockSiteAlarmStreamClientFactory Factory) CreateActor(
|
||||
TimeSpan? reconcileInterval = null)
|
||||
TimeSpan? reconcileInterval = null, TimeSpan? publishCoalesce = null)
|
||||
{
|
||||
var seed = new SeedStub();
|
||||
var sink = new PublishSink();
|
||||
@@ -145,7 +145,8 @@ public class SiteAlarmAggregatorActorTests : TestKit
|
||||
|
||||
var props = Props.Create(() => new SiteAlarmAggregatorActor(
|
||||
SiteId, "corr-1", seed.Seed, sink.Publish, factory, GrpcNodeA, GrpcNodeB,
|
||||
reconcileInterval ?? TimeSpan.FromMinutes(10)));
|
||||
reconcileInterval ?? TimeSpan.FromMinutes(10),
|
||||
publishCoalesce ?? TimeSpan.Zero));
|
||||
|
||||
var actor = Sys.ActorOf(props);
|
||||
return (actor, seed, sink, factory);
|
||||
@@ -396,6 +397,45 @@ public class SiteAlarmAggregatorActorTests : TestKit
|
||||
AwaitCondition(() => TotalSubs() > before, TimeSpan.FromSeconds(3));
|
||||
}
|
||||
|
||||
// ── R2 T10: live-delta publish coalescing (N6) ──
|
||||
|
||||
[Fact]
|
||||
public void DeltaBurst_IsCoalesced_IntoFewPublishes_ContainingEveryRow()
|
||||
{
|
||||
var (_, seed, sink, factory) = CreateActor(publishCoalesce: TimeSpan.FromMilliseconds(150));
|
||||
AwaitCondition(() => seed.CallCount == 1, TimeSpan.FromSeconds(3));
|
||||
seed.CompleteNext(); // empty initial seed
|
||||
AwaitAssert(() => Assert.Equal(1, sink.Count)); // seed publish, immediate
|
||||
|
||||
AwaitCondition(() => factory.ClientFor(GrpcNodeA).Subs.Count == 1, TimeSpan.FromSeconds(3));
|
||||
var sub = factory.ClientFor(GrpcNodeA).Subs.Single();
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
for (var i = 0; i < 50; i++)
|
||||
sub.OnAlarm(Alarm($"A{i}", "", 900, now)); // 50 distinct keys, one burst
|
||||
|
||||
AwaitAssert(() =>
|
||||
{
|
||||
Assert.Equal(50, sink.Latest!.Count); // nothing lost
|
||||
Assert.InRange(sink.Count, 2, 4); // pre-fix: 51 publishes (1 seed + 1 per delta)
|
||||
}, TimeSpan.FromSeconds(3));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ZeroCoalesce_PreservesLegacyPerDeltaPublish()
|
||||
{
|
||||
var (_, seed, sink, factory) = CreateActor(publishCoalesce: TimeSpan.Zero);
|
||||
AwaitCondition(() => seed.CallCount == 1, TimeSpan.FromSeconds(3));
|
||||
seed.CompleteNext();
|
||||
AwaitAssert(() => Assert.Equal(1, sink.Count));
|
||||
|
||||
AwaitCondition(() => factory.ClientFor(GrpcNodeA).Subs.Count == 1, TimeSpan.FromSeconds(3));
|
||||
var sub = factory.ClientFor(GrpcNodeA).Subs.Single();
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
sub.OnAlarm(Alarm("A1", "", 900, now));
|
||||
sub.OnAlarm(Alarm("A2", "", 900, now));
|
||||
AwaitAssert(() => Assert.Equal(3, sink.Count)); // 1 seed + 1 per delta
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Stop_Message_TearsDown_Grpc_And_Stops_Actor()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user