using Akka.Actor; using Microsoft.Extensions.Diagnostics.HealthChecks; namespace ZB.MOM.WW.ScadaBridge.Host.Health; /// /// /health/active check: Healthy only on the oldest Up member — the node that /// hosts the cluster singletons. Replaces the shared package's leader-based /// ActiveNodeHealthCheck (review 01 [High]): leadership is address-ordered and /// diverges from singleton placement after a restart, and during a partition /// BOTH nodes compute themselves leader, so Traefik served both. Oldest-member /// semantics keep exactly one active node through a partition (the non-oldest /// side still sees the old oldest member until SBR resolves membership). /// public sealed class OldestNodeActiveHealthCheck : IHealthCheck { private readonly ActorSystem _system; /// Initializes a new bound to the running actor system. /// The live cluster actor system. public OldestNodeActiveHealthCheck(ActorSystem system) => _system = system; /// Reports Healthy only when this node is the oldest Up cluster member (the singleton host); otherwise reports Unhealthy. /// The health check context (unused; the result depends only on cluster membership). /// Cancellation token. /// A task that resolves to the health check result. public Task CheckHealthAsync(HealthCheckContext context, CancellationToken ct = default) { var cluster = Akka.Cluster.Cluster.Get(_system); return Task.FromResult(ClusterActivityEvaluator.SelfIsOldest(cluster) ? HealthCheckResult.Healthy("oldest Up member (singleton host)") : HealthCheckResult.Unhealthy("not the oldest Up member (standby)")); } }