diff --git a/ZB.MOM.WW.Health/README.md b/ZB.MOM.WW.Health/README.md index eafd782..60990a1 100644 --- a/ZB.MOM.WW.Health/README.md +++ b/ZB.MOM.WW.Health/README.md @@ -10,7 +10,7 @@ The library normalizes the three-tier health endpoint convention (`/health/ready | 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` | Core tiers, `MapZbHealth` extension, canonical JSON writer (`ZbHealthWriter`), `IActiveNodeGate` seam, `GrpcDependencyHealthCheck` reachability probe (available; no app currently uses it), 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` with `CanConnectAsync` by default and an optional `ProbeQuery` delegate for custom connectivity validation. | `ZB.MOM.WW.Health`, `Microsoft.EntityFrameworkCore` | @@ -20,8 +20,8 @@ The library normalizes the three-tier health endpoint convention (`/health/ready | 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) | — | — | +| **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. diff --git a/components/health/GAPS.md b/components/health/GAPS.md index 5de22d3..b28afd3 100644 --- a/components/health/GAPS.md +++ b/components/health/GAPS.md @@ -84,10 +84,11 @@ Checks that can only return **Degraded** (so never cause a 503): OtOpcUa `akka` failures and reset only on a factory success, which needs an RPC, which a 503-ed pod never receives; caller cancellation also counted. Fixed on main `5de3dd9`: caller cancellation no longer counts and each keepalive tick probes one session while the count is non-zero; still present on deployed builds until historiangw#78 redeploys. Detail: [current-state/historiangateway](current-state/historiangateway/CURRENT-STATE.md). -- **SPEC text is stale on §2.4.** It names OtOpcUa and MxGateway as `GrpcDependencyHealthCheck` users; +- **SPEC text was stale on §2.4 - corrected (scadaproj#28).** It named OtOpcUa and MxGateway as `GrpcDependencyHealthCheck` users; **no app uses it** (HistorianGateway, once cited as a user, now probes the Historian through its - connection pool). The check stays in the library but has zero consumers; the SPEC/README wording should - be corrected when the spec is next revised. + connection pool). Decision: **keep** the check (a breaking public-API removal for no gain; small, unit-tested, + a ready building block if a gRPC dependency ever needs a readiness probe) but document it as having no current + consumers. SPEC §2.4/§6/§7, both READMEs and the shared-contract doc now say so. ## Deferred items — status from code (2026-11-09) diff --git a/components/health/README.md b/components/health/README.md index 944659c..2ccc2b5 100644 --- a/components/health/README.md +++ b/components/health/README.md @@ -40,8 +40,8 @@ below describe the original 2026-06 design rationale. writer wiring needed). - `IActiveNodeGate` seam — generalized from ScadaBridge's `ActiveNodeGate`; wired into `MapZbHealth` for automatic active-tier response. -- `GrpcDependencyHealthCheck` — reachability probe for a downstream gRPC dependency (covers - OtOpcUa → MxAccessGateway channel and MxAccessGateway → worker IPC). +- `GrpcDependencyHealthCheck` — reachability probe for a downstream gRPC dependency. Shipped but + **currently has no consumers** (neither OtOpcUa nor MxAccessGateway adopted it; scadaproj#28). - `AkkaClusterHealthCheck` (in `ZB.MOM.WW.Health.Akka`) with a configurable status policy. Default = ScadaBridge's three-way policy; `OtOpcUaCompat` preset preserves OtOpcUa's two-way self-Up-among-members scan. @@ -62,7 +62,7 @@ below describe the original 2026-06 design rationale. - MxAccessGateway's `GatewayHealthReply` metadata (`DefaultBackend`, `WorkerProtocolVersion`) — keep as a bespoke `/info` endpoint. - The x86 worker process — out of process and out of scope; the gateway-side - `GrpcDependencyHealthCheck` observes it indirectly. + no probe observes it directly (worker IPC is named pipes, not gRPC). ## Package structure @@ -70,7 +70,7 @@ below describe the original 2026-06 design rationale. | Package | Contents | Consumers | |---|---|---| -| `ZB.MOM.WW.Health` | Core tiers, `MapZbHealth`, canonical writer, `IActiveNodeGate`, `GrpcDependencyHealthCheck` | All three | +| `ZB.MOM.WW.Health` | Core tiers, `MapZbHealth`, canonical writer, `IActiveNodeGate`, `GrpcDependencyHealthCheck` (no consumers) | All three | | `ZB.MOM.WW.Health.Akka` | `AkkaClusterHealthCheck` + status presets, `ActiveNodeHealthCheck` + role filter | OtOpcUa, ScadaBridge | | `ZB.MOM.WW.Health.EntityFrameworkCore` | `DatabaseHealthCheck` + optional probe delegate | OtOpcUa, ScadaBridge | diff --git a/components/health/shared-contract/ZB.MOM.WW.Health.md b/components/health/shared-contract/ZB.MOM.WW.Health.md index 3ee6cd6..7dc4a92 100644 --- a/components/health/shared-contract/ZB.MOM.WW.Health.md +++ b/components/health/shared-contract/ZB.MOM.WW.Health.md @@ -31,8 +31,8 @@ into each app — there is no central health service. Consumers reference only w | `…Health.EntityFrameworkCore` | EF Core | — | ✅ | ✅ | **Why MxGateway takes only core:** it is not Akka-based and does not use EF Core. The -`GrpcDependencyHealthCheck` in the core package covers its only probe need (worker channel -reachability), so it avoids the Akka and EF transitive trees entirely. +Core is all it needs, so it avoids the Akka and EF transitive trees entirely. (An earlier draft said +`GrpcDependencyHealthCheck` would cover a worker-channel probe; the worker IPC is named pipes, so no app uses it - scadaproj#28.) ## `ZB.MOM.WW.Health` @@ -277,7 +277,7 @@ public sealed class DatabaseHealthCheckOptions | Consumer | Packages | Notes | |---|---|---| -| **MxGateway** | `ZB.MOM.WW.Health` (core only) | `GrpcDependencyHealthCheck` on the worker channel; all three tiers via `MapZbHealth()`; `IActiveNodeGate` not needed (not Akka-based) | +| **MxGateway** | `ZB.MOM.WW.Health` (core only) | no gRPC-dependency probe (worker IPC is named pipes); all three tiers via `MapZbHealth()`; `IActiveNodeGate` not needed (not Akka-based) | | **OtOpcUa** | All three | `AkkaClusterHealthCheck` + `OtOpcUaCompat` preset → `Default` on convergence; `ActiveNodeHealthCheck(role: "admin")`; `DatabaseHealthCheck` with `ProbeQuery` delegate | | **ScadaBridge** | All three | `AkkaClusterHealthCheck` + `Default` policy; `ActiveNodeHealthCheck` (role-less); `DatabaseHealthCheck` default probe; `AkkaActiveNodeGate` replaces inline `ActiveNodeGate` | diff --git a/components/health/spec/SPEC.md b/components/health/spec/SPEC.md index 9cc6b98..e25bc19 100644 --- a/components/health/spec/SPEC.md +++ b/components/health/spec/SPEC.md @@ -15,7 +15,7 @@ OtOpcUa and ScadaBridge; the `ClusterActiveNode` oldest-`Up`-member rule and the `OldestNodeActiveHealthCheck` — the two private replacements each app wrote after the original leader-based check proved wrong on a live rig; a generic `DatabaseHealthCheck` that covers both apps' EF Core probe patterns; a -`GrpcDependencyHealthCheck` for downstream gRPC reachability. +`GrpcDependencyHealthCheck` for downstream gRPC reachability (shipped, currently no consumers - see §2.4). **Explicitly NOT normalized** (domain-specific — keep per project): which probes each app registers and how it wires them to tags; orchestrator / Traefik routing rules and routing priorities; @@ -123,12 +123,17 @@ payload alone. Checks that a downstream gRPC channel is reachable by invoking a caller-supplied probe delegate (`Func>`). The default probe calls -`GrpcChannel.ConnectAsync`. Used by: +`GrpcChannel.ConnectAsync`. -- OtOpcUa — checks the MxAccessGateway gRPC channel. -- MxGateway — checks the x86 worker gRPC channel. +**Current consumers: none** (verified 2026-11-09 against every app's default branch). Earlier drafts +named OtOpcUa (MxAccessGateway channel) and MxGateway (x86 worker channel) as users, but neither adopted it: +MxGateway's worker IPC is named pipes, not gRPC, and workers exist only per session; OtOpcUa creates its +gateway channels per driver from DB config and deliberately keeps serving last-known-good when a source is +down, so it does not gate readiness on them (see `GAPS.md`, "Downstream dependency probes"). The check is +retained in the library as an available, unit-tested building block for a future gRPC dependency; it is not +part of any app's convergence target. -Registered to the `ready` tag. +If used, register it on the `ready` tag. ## 3. Response-writer contract @@ -219,14 +224,14 @@ predicates and response writer. |---|---|---|---| | **OtOpcUa** | All three tiers present (`/health/ready`, `/health/active`, `/healthz`); `DatabaseHealthCheck`, `AkkaClusterHealthCheck`, `AdminRoleLeaderHealthCheck` inline. | Inline probes diverge from the shared policy model; no `IActiveNodeGate`. | Replace inline `AkkaClusterHealthCheck` with shared + `OtOpcUaCompat` preset; replace `AdminRoleLeaderHealthCheck` with shared `ActiveNodeHealthCheck(role: "admin")`; replace inline `DatabaseHealthCheck` with shared generic; call `app.MapZbHealth()`. | | **ScadaBridge** | `/health/ready` + `/health/active` present; no `/healthz`; `DatabaseHealthCheck`, `AkkaClusterHealthCheck`, `ActiveNodeHealthCheck`, `ActiveNodeGate` inline. | Missing `/healthz` live tier; inline implementations. | Add `/healthz` via `MapZbHealth()`; replace inline probes with shared equivalents (Default policy); replace inline `ActiveNodeGate` with `AkkaActiveNodeGate`. | -| **MxGateway** | Only `/health/live` (custom `GatewayHealthReply`); `AddHealthChecks()` called but zero probes registered. | Missing `ready` and `active` tiers; no probes; not using standard health middleware. | Replace custom endpoint with `app.MapZbHealth()`; register `GrpcDependencyHealthCheck` for the x86 worker channel on the `ready` tag. | +| **MxGateway** | Only `/health/live` (custom `GatewayHealthReply`); `AddHealthChecks()` called but zero probes registered. | Missing `ready` and `active` tiers; no probes; not using standard health middleware. | Replace custom endpoint with `app.MapZbHealth()`; no gRPC-dependency probe (the worker IPC is named pipes - see §2.4 / GAPS). | ## 7. Acceptance (what "converged" means) A project is converged when: (a) it calls `app.MapZbHealth()` and exposes all three canonical endpoints; (b) its Akka probes (if applicable) use the `AkkaClusterHealthCheck` + `ActiveNodeHealthCheck` from `ZB.MOM.WW.Health.Akka` with the Default policy; (c) its DB probe uses `DatabaseHealthCheck` -from `ZB.MOM.WW.Health.EntityFrameworkCore`; (d) its gRPC-dependency probe (if applicable) uses +from `ZB.MOM.WW.Health.EntityFrameworkCore`; (d) its gRPC-dependency probe (if it has one; none currently do) uses `GrpcDependencyHealthCheck`; (e) its `IActiveNodeGate` implementation is `AkkaActiveNodeGate` (or a project-specific implementation of the shared interface); (f) all health endpoints return the canonical JSON shape defined in §3.