fix(health): route site heartbeats into the aggregator
CentralCommunicationActor.HandleHeartbeat was forwarding each incoming HeartbeatMessage to Context.Parent, which resolves to the /user guardian — a non-actor. Every site heartbeat went straight to dead letters (~1026 per central node per 30 minutes at the default ~2s interval across three sites). The aggregator now exposes MarkHeartbeat(siteId, receivedAt) which bumps LastReportReceivedAt on already-known sites (and clears IsOnline if it had flipped) without touching LatestReport. Heartbeats from unregistered sites are dropped — first registration still happens on the first full report. CentralCommunicationActor calls this in place of the no-op Tell. The result: heartbeats now serve their stated health-monitoring purpose (per CLAUDE.md) by keeping a site marked online between the 30s full reports if a single report is briefly delayed, and the dead letter noise disappears entirely.
This commit is contained in:
@@ -11,6 +11,7 @@ using ScadaLink.Commons.Messages.Deployment;
|
||||
using ScadaLink.Commons.Messages.DebugView;
|
||||
using ScadaLink.Commons.Messages.Health;
|
||||
using ScadaLink.Communication.Actors;
|
||||
using ScadaLink.HealthMonitoring;
|
||||
using Akka.TestKit;
|
||||
|
||||
namespace ScadaLink.Communication.Tests;
|
||||
@@ -140,25 +141,27 @@ public class CentralCommunicationActorTests : TestKit
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Heartbeat_ForwardedToParent()
|
||||
public void Heartbeat_BumpsAggregatorTimestamp()
|
||||
{
|
||||
var mockRepo = Substitute.For<ISiteRepository>();
|
||||
mockRepo.GetAllSitesAsync(Arg.Any<CancellationToken>())
|
||||
.Returns(new List<Site>());
|
||||
|
||||
var aggregator = Substitute.For<ICentralHealthAggregator>();
|
||||
|
||||
var services = new ServiceCollection();
|
||||
services.AddScoped(_ => mockRepo);
|
||||
services.AddSingleton(aggregator);
|
||||
var sp = services.BuildServiceProvider();
|
||||
|
||||
var siteClientFactory = Substitute.For<ISiteClientFactory>();
|
||||
var parentProbe = CreateTestProbe();
|
||||
var centralActor = parentProbe.ChildActorOf(
|
||||
var centralActor = Sys.ActorOf(
|
||||
Props.Create(() => new CentralCommunicationActor(sp, siteClientFactory)));
|
||||
|
||||
var heartbeat = new HeartbeatMessage("site1", "host1", true, DateTimeOffset.UtcNow);
|
||||
centralActor.Tell(heartbeat);
|
||||
var timestamp = DateTimeOffset.UtcNow;
|
||||
centralActor.Tell(new HeartbeatMessage("site1", "host1", true, timestamp));
|
||||
|
||||
parentProbe.ExpectMsg<HeartbeatMessage>(msg => msg.SiteId == "site1");
|
||||
AwaitAssert(() => aggregator.Received(1).MarkHeartbeat("site1", timestamp));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
Reference in New Issue
Block a user