docs(mesh-phase4): ServiceLevel semantics on DB-less nodes + driver ConfigDb cut
Claude-Session: https://claude.ai/code/session_01GASWkNEi68FSCtvr6rLoEW
This commit is contained in:
@@ -141,7 +141,7 @@ admin-change refresh), clusters know central from appsettings (static, restart t
|
||||
flow over ClusterClient (DPS paths deleted or dark-switched), including an Ask timing out cleanly
|
||||
against a stopped node.
|
||||
|
||||
### Phase 3 — Config fetch-and-cache from central — **CODE-COMPLETE 2026-07-23 (Tasks 0–8; live gate Task 10 pending)**
|
||||
### Phase 3 — Config fetch-and-cache from central — **DONE 2026-07-23 (merged `d01b0695`, live gate PASSED, pushed to origin)**
|
||||
**Decisions taken:** the deploy artifact is served **by central over a gRPC fetch RPC**
|
||||
(`DeploymentArtifactService.Fetch`, streamed chunks — chosen over token-gated HTTP), authenticated by
|
||||
a **shared node bearer key** (`ConfigServe:ApiKey == ConfigSource:ApiKey`, `FixedTimeEquals`,
|
||||
@@ -160,13 +160,14 @@ config SQL read); #485 coverage; and a **real two-Host gRPC boundary test** (rig
|
||||
wrong-key control returns null, failover). Chunking, SHA-256 verify, newest-2 retention, pair
|
||||
replication carry over unchanged. **Phase boundary held:** config **reads** only — the
|
||||
`NodeDeploymentState` ack-row write, `DbHealthProbe`, `EfAlarmConditionStateStore` stay for Phase 4.
|
||||
**Remaining (Tasks 9–10):** rig config + docs (done) and the live gate — deploy on a site pair flipped
|
||||
to `FetchAndCache` with central SQL stopped mid-fetch → retry lands; #485 last-known-good re-proven on
|
||||
the new path.
|
||||
**Exit gate:** live gate green on the rig; a driver node with an empty LocalDb and reachable central
|
||||
boots into the current config; with central down it boots last-known-good.
|
||||
**Live gate PASSED:** deployed on a site pair flipped to `FetchAndCache` with central SQL stopped
|
||||
mid-fetch — retry landed; #485 last-known-good re-proven on the new path. Merged to master
|
||||
`d01b0695` and pushed to origin; the scadaproj umbrella index was updated + pushed (`b5e7bc8`) in the
|
||||
same pass.
|
||||
**Exit gate — MET:** live gate green on the rig; a driver node with an empty LocalDb and reachable
|
||||
central boots into the current config; with central down it boots last-known-good.
|
||||
|
||||
### Phase 4 — Cut the driver-side ConfigDb connection
|
||||
### Phase 4 — Cut the driver-side ConfigDb connection — **CODE-COMPLETE 2026-07-23 (Tasks 0–7 + 1b; Task 9 table-drop deferred; Task 10 rig + Task 11 live gate remain)**
|
||||
**Scope (design §6.1 audit table):** re-home `EfAlarmConditionStateStore` to LocalDb (pair-local
|
||||
state, same journey as the Phase-2 alarm S&F buffer); **resolve the `DbHealthProbeActor`
|
||||
question** — driver nodes have no DB to probe, and DB health currently feeds ServiceLevel tiering,
|
||||
@@ -176,6 +177,27 @@ transports) — this is a client-visible ServiceLevel semantics change and must
|
||||
design) and re-source it; registration cleanup in `ServiceCollectionExtensions`; driver-role
|
||||
`Program.cs` branch registers no EF ConfigDb context at all (mirror ScadaBridge's central-only
|
||||
`AddConfigurationDatabase`).
|
||||
**Shipped (branch `feat/mesh-phase4`, Tasks 0–7 + 1b):** `Program.cs` gates `AddOtOpcUaConfigDb` +
|
||||
the ConfigDb health check on `hasAdmin`, so a driver-only node registers no ConfigDb connection
|
||||
string at all; `ConfigSourceOptionsValidator` fails host start if a driver-only node's
|
||||
`ConfigSource:Mode` is left `Direct` (fused `admin,driver` may stay `Direct`); the DB-health
|
||||
question resolved as **retire, don't replace** — `DbHealthProbeActor` is simply not spawned when no
|
||||
ConfigDb is present, and `OpcUaPublishActor` runs a `dbLess` mode feeding `DbReachable=true`
|
||||
constant with staleness from the redundancy-snapshot age alone (documented as the client-visible
|
||||
change in `docs/Redundancy.md`); `LocalDbAlarmConditionStateStore` (replicated
|
||||
`alarm_condition_state` table) replaces `EfAlarmConditionStateStore` on every driver-role node;
|
||||
`DriverHostActor` no longer writes `NodeDeploymentState` on a driver-only node (central's
|
||||
`ConfigPublishCoordinator.PersistNodeAck` was already the ack system of record); driver-only nodes
|
||||
register `NullLdapGroupRoleMappingService` so LDAP group→role falls back to the
|
||||
`Security:Ldap:GroupToRole` appsettings baseline (DB grants were never delivered to driver nodes via
|
||||
config anyway). Docs updated: `docs/Redundancy.md`, `docs/Configuration.md`, `CLAUDE.md`.
|
||||
**Deferred:** Task 9 — dropping the now-unused `ScriptedAlarmState` ConfigDb table (a follow-up, not
|
||||
a blocker; the table is simply unread).
|
||||
**Remaining (Tasks 10–11):** rig config for a driver-only node with no `ConfigDb` connection string,
|
||||
and the live gate itself — a driver pair runs a full deploy + alarm + historian cycle with no
|
||||
ConfigDb configured at all, confirming ServiceLevel 240/250 while central SQL is down and
|
||||
grep-level proof no driver-branch service can resolve the ConfigDb context. **Not yet run — treat
|
||||
Phase 4 as code-complete, not verified, until this lands.**
|
||||
**Exit gate:** its own live gate — a driver pair runs a full deploy + alarm + historian cycle with
|
||||
**no ConfigDb connection string configured at all**; grep-level proof no driver-branch service can
|
||||
resolve the ConfigDb context.
|
||||
@@ -243,8 +265,8 @@ resource sizing on the site VMs should be checked once both products run the ful
|
||||
| Prereq: selfform-fallback + manual-failover plan | **DONE 2026-07-22**, merged `a78425ea`. Shipped *not* as the planned `SelfFormAfter` watchdog — the live gate caught that islanding a manually-failed-over node — but as self-first seed ordering + `AkkaClusterOptionsValidator`. Phase 6's seed-ordering scope is therefore already discharged. |
|
||||
| 1 ClusterNode columns + DB ack set | **DONE 2026-07-22**, merged `7654f24d`. Deploy-seal semantics changed as flagged, but the escape hatch is **`ClusterNode.MaintenanceMode`**, not `Enabled=0` — the live gate found `Enabled=0` is rejected by `DraftValidator.ValidateClusterTopology` on any 2-node pair. Gate record: `2026-07-22-mesh-phase1-live-gate.md`. |
|
||||
| 2 ClusterClient transport | **DONE 2026-07-22** (live gate PASSED, shipped dark) — shipped dark (`MeshTransport:Mode=Dps` default), both comm actors registered in both modes. Corrections: `SendToAll` not `Send`, one fleet-wide client not one per cluster, `/user/node-communication` not `/user/cluster-communication`. The gate's "Ask timing out" item does not exist to test — no cross-boundary Ask in this phase; see the phase section. |
|
||||
| 3 fetch-and-cache | **CODE-COMPLETE 2026-07-23** (Tasks 0–8 merged to the phase branch, all green + sabotage-checked; live gate Task 10 pending). gRPC fetch RPC + shared node key; dark switch `ConfigSource:Mode` (Direct default). Rig flip: `OTOPCUA_CONFIG_MODE=FetchAndCache` on the site nodes. See `2026-07-22-mesh-phase3-config-fetch-and-cache.md`. |
|
||||
| 4 cut driver ConfigDb | not started |
|
||||
| 3 fetch-and-cache | **DONE 2026-07-23**, merged `d01b0695`, live gate PASSED, pushed to origin (scadaproj umbrella index updated + pushed `b5e7bc8`). gRPC fetch RPC + shared node key; dark switch `ConfigSource:Mode` (Direct default). Rig flip: `OTOPCUA_CONFIG_MODE=FetchAndCache` on the site nodes. See `2026-07-22-mesh-phase3-config-fetch-and-cache.md`. |
|
||||
| 4 cut driver ConfigDb | **CODE-COMPLETE 2026-07-23** on `feat/mesh-phase4` (Tasks 0–7 + 1b done; Task 9 table-drop deferred; Task 10 rig + Task 11 live gate remain — not yet run). ConfigDb admin-only; `DbHealthProbeActor` not spawned driver-only (client-visible ServiceLevel 240/250 with central down); alarm condition state in LocalDb; central persists acks; driver-only LDAP maps from appsettings only. See `2026-07-23-mesh-phase4-cut-driver-configdb.md`. |
|
||||
| 5 gRPC telemetry | not started |
|
||||
| 6 mesh partition + co-location | not started |
|
||||
| 7 drill + live gates | not started |
|
||||
|
||||
@@ -76,14 +76,16 @@
|
||||
{
|
||||
"id": 7,
|
||||
"subject": "Task 7: Registration cleanup + full-graph driver-only DI sweep",
|
||||
"status": "pending",
|
||||
"status": "completed",
|
||||
"blockedBy": [
|
||||
"1b",
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
6
|
||||
]
|
||||
],
|
||||
"commit": "6f21213f",
|
||||
"result": "12/12; sweep verified clean; ordering+lazy-resolve pins APPROVED (red-check confirmed load-bearing)"
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
@@ -96,10 +98,11 @@
|
||||
{
|
||||
"id": 9,
|
||||
"subject": "Task 9: Drop dead ConfigDb ScriptedAlarmState table + retire EfAlarmConditionStateStore",
|
||||
"status": "pending",
|
||||
"status": "deferred",
|
||||
"blockedBy": [
|
||||
6
|
||||
]
|
||||
],
|
||||
"result": "DEFERRED to follow-up (plan-sanctioned): EF store is inert on all real nodes (test-harness fallback only); table-drop migration is irreversible. Not needed for the ConfigDb-cut goal. Tracked in program doc."
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
|
||||
Reference in New Issue
Block a user