6a4fa220de
Rewrites the active-node sections of the spec and shared contract, which described the leader-based design that both consumers rejected. - SPEC §2.3 replaced: the rule, the ordered RolePreference, why a node holding none of the preferred roles is Unhealthy rather than Healthy, and the data payload. §4 (IActiveNodeGate) and the tier table follow. - SPEC §6 marked historical: it still tells adopters to use ActiveNodeHealthCheck(role: "admin") and AkkaActiveNodeGate, which is the advice that produced the defect. Kept for provenance, annotated so nobody follows it again. - GAPS: gaps L1/L2 recorded as closed the wrong way, reopened, then closed at 0.3.0 — including the part worth remembering, that both apps adopted the shared check as specified, hit it live, and reverted to private copies, so the surface reached zero consumers by being avoided rather than unused. - Index rows updated with the consumer-visible changes and both app branches.
78 lines
4.0 KiB
Markdown
78 lines
4.0 KiB
Markdown
# 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.
|
|
|
|
**Built at 0.3.0 and published to the Gitea NuGet feed. Adopted by all four apps** — adoption is tracked in `~/Desktop/scadaproj/components/health/GAPS.md`.
|
|
|
|
---
|
|
|
|
## Packages
|
|
|
|
| Package | Responsibilities | Key Dependencies |
|
|
|---|---|---|
|
|
| `ZB.MOM.WW.Health` | Core tier convention, `MapZbHealth` extension, canonical JSON writer (`ZbHealthWriter`), `IActiveNodeGate` seam, `GrpcDependencyHealthCheck` reachability probe, 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` / `OtOpcUaCompat`); `ClusterActiveNode` — the family-wide **oldest-`Up`-member** rule, optionally role-scoped; `ActiveNodeHealthCheck` over it (role-preference list, `activeRole`/`activeNode` in the result `data`); and `AkkaActiveNodeGate`, which backs `IActiveNodeGate` from the same rule. | `ZB.MOM.WW.Health`, `Akka.Cluster` |
|
|
| `ZB.MOM.WW.Health.EntityFrameworkCore` | `DatabaseHealthCheck<TContext>` — resolves `IDbContextFactory<TContext>` when registered, else a scoped `TContext`; pool-safe. Default probe: `CanConnectAsync`. Optional `ProbeQuery` delegate for query-based 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 | yes | yes |
|
|
| **MxAccessGateway** | yes | — | — |
|
|
| **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.
|
|
|
|
---
|
|
|
|
## Build, test, and pack commands
|
|
|
|
```bash
|
|
# From ZB.MOM.WW.Health/
|
|
|
|
# Build
|
|
dotnet build ZB.MOM.WW.Health.slnx
|
|
dotnet build ZB.MOM.WW.Health.slnx -c Release
|
|
|
|
# Test (no external dependencies — no running Akka cluster, no database)
|
|
dotnet test ZB.MOM.WW.Health.slnx
|
|
|
|
# Pack (three .nupkg files land in artifacts/)
|
|
dotnet pack ZB.MOM.WW.Health.slnx -c Release -o ./artifacts
|
|
```
|
|
|
|
All three test assemblies run offline. The Akka suite forms real in-process clusters on loopback: a
|
|
single-node one for the cluster-data payload, and a **two-node one built so the oldest member is not
|
|
the lowest-addressed one** — the only condition under which age- and leader-ordering disagree, and so
|
|
the only fixture that can catch a leader-based active-node regression. It binds fixed ports
|
|
19540/19541 (distinct from OtOpcUa's equivalent fixture) and carries a sanity check that the
|
|
divergence actually occurred, so the assertions cannot pass for the wrong reason:
|
|
|
|
| Assembly | Tests |
|
|
|---|---|
|
|
| `ZB.MOM.WW.Health.Tests` | 25 |
|
|
| `ZB.MOM.WW.Health.Akka.Tests` | 45 |
|
|
| `ZB.MOM.WW.Health.EntityFrameworkCore.Tests` | 6 |
|
|
| **Total** | **76** |
|
|
|
|
`GeneratePackageOnBuild` is off — pack explicitly with the command above.
|
|
|
|
---
|
|
|
|
## Status
|
|
|
|
Built at **0.3.0** and published to the Gitea NuGet feed. **Adopted** by all four apps — adoption is tracked in the component backlog:
|
|
|
|
- `~/Desktop/scadaproj/components/health/GAPS.md` — adoption order, effort, and risk
|
|
|
|
Design documentation:
|
|
|
|
- `~/Desktop/scadaproj/components/health/spec/SPEC.md` — normalized three-tier target
|
|
- `~/Desktop/scadaproj/components/health/shared-contract/ZB.MOM.WW.Health.md` — proposed API (aligned to shipped code)
|
|
- `~/Desktop/scadaproj/components/health/current-state/` — per-project current state (code-verified)
|