Phase 0 of docs/plans/2026-07-22-overview-dashboard-impl-plan.md: give the
canonical health JSON a structured channel so the family overview dashboard can
read each Akka cluster's current leader.
- ZbHealthWriter: optional `"data": {...}` per entry, sourced from
HealthReportEntry.Data, emitted only when non-empty. Per-property JsonIgnore
(NOT a global DefaultIgnoreCondition) so `"description": null` still renders —
payloads from data-less checks stay byte-identical to 0.1.0.
- AkkaClusterHealthCheck: BuildClusterData publishes this node's own view —
leader (omitted while unknown), selfAddress, selfRoles (sorted), memberCount,
unreachableCount — on every result path. The startup-safety paths (no
ActorSystem / cluster inaccessible) stay description-only.
- Tests: writer data emit/omit (raw-JSON assert on the omit case), and a real
single-node self-joined cluster via Akka.TestKit.Xunit2 for the data values.
70 tests green (25/39/6).
- Version 0.1.0 -> 0.2.0; 3 packages published to the Gitea feed and
restore-verified from a scratch consumer, which serves data.leader live.
4.6 KiB
ZB.MOM.WW.Health
Health-check libraries for the ZB.MOM.WW SCADA family (OtOpcUa, MxAccessGateway, ScadaBridge). These are libraries, not a service — each package is linked directly into the consuming application at build time. There is no central health process or network hop; probes run in-process alongside the application.
The library normalizes the three-tier health endpoint convention (/health/ready, /health/active, /healthz) and provides reusable probe implementations so the three sister projects share a common surface without duplicating probe logic.
Packages
| Package | Description | Key Dependencies |
|---|---|---|
ZB.MOM.WW.Health |
Core tiers, MapZbHealth extension, canonical JSON writer (ZbHealthWriter), IActiveNodeGate seam, GrpcDependencyHealthCheck reachability probe, and tier-tag constants (ZbHealthTags). No Akka or EF dependency. |
Microsoft.AspNetCore.App (framework ref), Grpc.Net.Client |
ZB.MOM.WW.Health.Akka |
AkkaClusterHealthCheck with a configurable AkkaClusterStatusPolicy (presets: Default three-way / OtOpcUaCompat two-way), ActiveNodeHealthCheck with an optional role filter, and AkkaActiveNodeGate that backs IActiveNodeGate from the cluster member state. |
ZB.MOM.WW.Health, Akka.Cluster |
ZB.MOM.WW.Health.EntityFrameworkCore |
DatabaseHealthCheck<TContext> with CanConnectAsync by default and an optional ProbeQuery delegate for custom connectivity validation. |
ZB.MOM.WW.Health, Microsoft.EntityFrameworkCore |
Consumer Matrix
| Consumer | ZB.MOM.WW.Health (core) |
ZB.MOM.WW.Health.Akka |
ZB.MOM.WW.Health.EntityFrameworkCore |
|---|---|---|---|
| OtOpcUa | yes (+ GrpcDependencyHealthCheck for the MxAccessGateway channel) |
yes | yes |
| MxAccessGateway | yes (+ GrpcDependencyHealthCheck for the x86 worker IPC) |
— | — |
| ScadaBridge | yes | yes | yes |
MxAccessGateway consumes the core package only — it has no Akka cluster and no EF DbContext. OtOpcUa and ScadaBridge consume all three packages.
Versioning
All three packages are versioned lockstep from Directory.Build.props. The current release is 0.2.0. A single version bump in Directory.Build.props bumps all three packages simultaneously — consumers should reference the same version for all ZB.MOM.WW.Health packages.
0.2.0 — per-entry data (additive, non-breaking)
ZbHealthWriteremits an optional"data": { … }object per entry, sourced from the check'sHealthCheckResult.Data, only when non-empty — a check that publishes no data produces a byte-identical body to 0.1.0, so every existing consumer is unaffected. Data keys are written verbatim (the camelCase policy applies to the envelope, not to dictionary keys).AkkaClusterHealthCheckpopulates that dictionary with this node's cluster view:leader(omitted while unknown),selfAddress,selfRoles,memberCount,unreachableCount. The startup-safety paths (noActorSystem, cluster not yet accessible) stay description-only.- Consumers gain the leader field on a package bump alone — no application code change, provided the
app registers the shared
AkkaClusterHealthCheck.
Building and testing
# from ZB.MOM.WW.Health/
dotnet build ZB.MOM.WW.Health.slnx
dotnet test ZB.MOM.WW.Health.slnx
All three test assemblies run with dotnet test and require no external dependencies — no database and no
external cluster (the cluster-data tests form a single-node in-process Akka cluster on a loopback port
via Akka.TestKit.Xunit2):
| Assembly | Tests |
|---|---|
ZB.MOM.WW.Health.Tests |
25 |
ZB.MOM.WW.Health.Akka.Tests |
39 |
ZB.MOM.WW.Health.EntityFrameworkCore.Tests |
6 |
| Total | 70 |
Packing
dotnet pack ZB.MOM.WW.Health.slnx -c Release -o ./artifacts
Produces three .nupkg files in artifacts/:
ZB.MOM.WW.Health.0.2.0.nupkg
ZB.MOM.WW.Health.Akka.0.2.0.nupkg
ZB.MOM.WW.Health.EntityFrameworkCore.0.2.0.nupkg
GeneratePackageOnBuild is off — pack explicitly as above.
Status
Built at 0.2.0 and published to the Gitea NuGet feed. Adopted by all four apps (OtOpcUa, MxAccessGateway, ScadaBridge, HistorianGateway). Adoption is tracked in the component backlog:
~/Desktop/scadaproj/components/health/GAPS.md
Design documentation lives alongside that backlog:
~/Desktop/scadaproj/components/health/spec/SPEC.md— normalized three-tier target~/Desktop/scadaproj/components/health/shared-contract/ZB.MOM.WW.Health.md— proposed API~/Desktop/scadaproj/components/health/current-state/— per-project current state (code-verified)