docs(health): align shared-contract to shipped API + per-lib CLAUDE.md + cleanup
- Contract: DatabaseHealthCheck<TContext> ctor now shows IServiceProvider (resolves IDbContextFactory<TContext> when registered, else a scoped TContext; pool-safe) - Contract: RequireActiveNode gains retryAfterSeconds = 5 default parameter - Packages: remove dangling AspNetCore.HealthChecks.UI.Client PackageVersion (no csproj referenced it) - Tests: fix CS8625 in RoleLessCases — use object?[] so null role rows compile warning-free under Nullable=enable - Add ZB.MOM.WW.Health/CLAUDE.md (packages, responsibilities, consumer matrix, build/test/pack commands, status + pointer to components/health/)
This commit is contained in:
@@ -0,0 +1,72 @@
|
|||||||
|
# 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.1.0. NOT yet adopted by the three 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`), `ActiveNodeHealthCheck` with an optional role filter, and `AkkaActiveNodeGate` that backs `IActiveNodeGate` from cluster member state. | `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:
|
||||||
|
|
||||||
|
| Assembly | Tests |
|
||||||
|
|---|---|
|
||||||
|
| `ZB.MOM.WW.Health.Tests` | 20 |
|
||||||
|
| `ZB.MOM.WW.Health.Akka.Tests` | 32 |
|
||||||
|
| `ZB.MOM.WW.Health.EntityFrameworkCore.Tests` | 6 |
|
||||||
|
| **Total** | **58** |
|
||||||
|
|
||||||
|
`GeneratePackageOnBuild` is off — pack explicitly with the command above.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Built at **0.1.0** and published to the Gitea NuGet feed. **Not yet adopted** by the three 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)
|
||||||
@@ -11,7 +11,6 @@
|
|||||||
|
|
||||||
<!-- Health Checks / ASP.NET Core -->
|
<!-- Health Checks / ASP.NET Core -->
|
||||||
<PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" Version="10.0.7" />
|
<PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" Version="10.0.7" />
|
||||||
<PackageVersion Include="AspNetCore.HealthChecks.UI.Client" Version="9.0.0" />
|
|
||||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.7" />
|
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.7" />
|
||||||
|
|
||||||
<!-- gRPC -->
|
<!-- gRPC -->
|
||||||
|
|||||||
@@ -14,12 +14,12 @@ namespace ZB.MOM.WW.Health.Akka.Tests;
|
|||||||
public sealed class ActiveNodeDecisionTests
|
public sealed class ActiveNodeDecisionTests
|
||||||
{
|
{
|
||||||
// Role-less: requiredRole == null. hasRole is irrelevant. Healthy iff (selfUp && isLeader), else Unhealthy.
|
// Role-less: requiredRole == null. hasRole is irrelevant. Healthy iff (selfUp && isLeader), else Unhealthy.
|
||||||
public static IEnumerable<object[]> RoleLessCases() => new[]
|
public static IEnumerable<object?[]> RoleLessCases() => new[]
|
||||||
{
|
{
|
||||||
new object[] { true, true, false, (string?)null, HealthStatus.Healthy },
|
new object?[] { true, true, false, (string?)null, HealthStatus.Healthy },
|
||||||
new object[] { true, false, false, (string?)null, HealthStatus.Unhealthy },
|
new object?[] { true, false, false, (string?)null, HealthStatus.Unhealthy },
|
||||||
new object[] { false, true, false, (string?)null, HealthStatus.Unhealthy },
|
new object?[] { false, true, false, (string?)null, HealthStatus.Unhealthy },
|
||||||
new object[] { false, false, false, (string?)null, HealthStatus.Unhealthy },
|
new object?[] { false, false, false, (string?)null, HealthStatus.Unhealthy },
|
||||||
};
|
};
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|||||||
@@ -91,7 +91,8 @@ public interface IActiveNodeGate
|
|||||||
public static class ActiveNodeGateExtensions
|
public static class ActiveNodeGateExtensions
|
||||||
{
|
{
|
||||||
public static IEndpointConventionBuilder RequireActiveNode(
|
public static IEndpointConventionBuilder RequireActiveNode(
|
||||||
this IEndpointConventionBuilder builder);
|
this IEndpointConventionBuilder builder,
|
||||||
|
int retryAfterSeconds = 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks that a downstream gRPC channel is reachable.
|
/// Checks that a downstream gRPC channel is reachable.
|
||||||
@@ -221,8 +222,9 @@ namespace ZB.MOM.WW.Health.EntityFrameworkCore;
|
|||||||
public sealed class DatabaseHealthCheck<TContext> : IHealthCheck
|
public sealed class DatabaseHealthCheck<TContext> : IHealthCheck
|
||||||
where TContext : DbContext
|
where TContext : DbContext
|
||||||
{
|
{
|
||||||
|
// Resolves IDbContextFactory<TContext> when registered, else a scoped TContext; pool-safe.
|
||||||
public DatabaseHealthCheck(
|
public DatabaseHealthCheck(
|
||||||
IDbContextFactory<TContext> factory,
|
IServiceProvider serviceProvider,
|
||||||
DatabaseHealthCheckOptions<TContext>? options = null);
|
DatabaseHealthCheckOptions<TContext>? options = null);
|
||||||
|
|
||||||
public Task<HealthCheckResult> CheckHealthAsync(
|
public Task<HealthCheckResult> CheckHealthAsync(
|
||||||
|
|||||||
Reference in New Issue
Block a user