refactor(health): adopt the shared active-node check (Health 0.3.0)
OldestNodeActiveHealthCheck existed because the shared ActiveNodeHealthCheck
selected by cluster leadership (review 01 [High]): leadership is address-ordered
and diverges from singleton placement after a restart, and during a partition
both sides compute themselves leader so Traefik served both. Health 0.3.0 makes
the shared check age-based — it is now this host's own rule, promoted — so the
private copy is deleted and central registers the shared type.
ActiveNodeEvaluator, the "THE single definition of active node" this repo already
maintained, now delegates to the shared ClusterActiveNode rather than
re-implementing it. That keeps the delivery gate, the heartbeat IsActive stamp,
the inbound-API gate and the /health/active tier on one rule, and it means the
rule is shared with OtOpcUa, which had independently written a third copy.
Communication takes a ZB.MOM.WW.Health.Akka reference for it; the layering
trade-off is recorded at the PackageReference.
SitePairActiveNodeHealthCheck is deliberately KEPT. It is not a duplicate of the
rule — it is a thin adapter over the site's own IClusterNodeProvider, which
scopes to site-{SiteId} and is itself now backed by ClusterActiveNode. Registering
the shared check directly would have to re-derive the site role and would lose the
property that the tier and singleton placement come from the same provider.
Central stays unscoped: every central member competes for one active slot.
No behaviour change on any node — same rule, one implementation instead of two.
Verified: Host.Tests 439/439, Communication ActiveNode 2/2.
This commit is contained in:
@@ -344,9 +344,10 @@ try
|
||||
// Ready tier (/health/ready): database + akka-cluster + required-singletons.
|
||||
// Active tier (/health/active): active-node = oldest Up member (singleton host) AND
|
||||
// database reachable (review 01 [High]) — a DB-dead active node drops from Traefik
|
||||
// rotation, and the active-node check is the host-local OldestNodeActiveHealthCheck,
|
||||
// NOT the shared leader-based ActiveNodeHealthCheck (leadership diverges from singleton
|
||||
// placement after a restart and both sides claim leadership during a partition).
|
||||
// rotation. The active-node check is the shared ActiveNodeHealthCheck, which selects by age
|
||||
// as of Health 0.3.0; through 0.2.1 it selected by leadership, which diverges from singleton
|
||||
// placement after a restart and names both sides during a partition, so this host kept a
|
||||
// private copy until the shared one was fixed.
|
||||
// The Akka checks resolve ActorSystem from DI via the singleton bridge registered below;
|
||||
// the DatabaseHealthCheck<TContext> resolves a scoped ScadaBridgeDbContext (no factory).
|
||||
builder.Services.AddHealthChecks()
|
||||
@@ -371,9 +372,12 @@ try
|
||||
"required-singletons",
|
||||
failureStatus: null,
|
||||
tags: new[] { ZbHealthTags.Ready })
|
||||
// Host-local oldest-member check (review 01 [High]) — see OldestNodeActiveHealthCheck.
|
||||
// Resolves ActorSystem from DI, like the akka-cluster check above.
|
||||
.AddTypeActivatedCheck<OldestNodeActiveHealthCheck>(
|
||||
// Oldest-Up-member check (review 01 [High]), unscoped: every central member competes for
|
||||
// the one active slot. Since ZB.MOM.WW.Health 0.3.0 this IS the shared check — it selects
|
||||
// by age, so the host-local OldestNodeActiveHealthCheck that existed only to avoid the old
|
||||
// leader-based selection is gone. Resolves ActorSystem from DI, like the akka-cluster
|
||||
// check above.
|
||||
.AddTypeActivatedCheck<ActiveNodeHealthCheck>(
|
||||
"active-node",
|
||||
failureStatus: null,
|
||||
tags: new[] { ZbHealthTags.Active });
|
||||
@@ -399,9 +403,9 @@ try
|
||||
// consults IActiveNodeGate and defaults to "allow" when none is registered,
|
||||
// which leaves the design's "central cluster only (active node)" guarantee
|
||||
// unenforced in deployed binaries). The gate is backed by the same oldest-member
|
||||
// evaluator (ClusterActivityEvaluator) as OldestNodeActiveHealthCheck above, so the
|
||||
// inbound API and the /health/active endpoint Traefik routes against agree on
|
||||
// which node is active.
|
||||
// evaluator (ClusterActivityEvaluator, which delegates to the shared ClusterActiveNode) as
|
||||
// the active-node check above, so the inbound API and the /health/active endpoint Traefik
|
||||
// routes against agree on which node is active.
|
||||
builder.Services.AddSingleton<ZB.MOM.WW.ScadaBridge.InboundAPI.IActiveNodeGate, ActiveNodeGate>();
|
||||
|
||||
// Admin-triggered manual failover of the central pair (Health page control,
|
||||
|
||||
Reference in New Issue
Block a user