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:
@@ -355,6 +355,40 @@ See `docs/Configuration.md` (`LocalDb` section), `docs/Redundancy.md` (pair-loca
|
||||
cache), `docs/AlarmHistorian.md`, and the runbook
|
||||
`docs/operations/2026-07-20-localdb-pair-replication.md`.
|
||||
|
||||
## Phase 4 — cut the driver-side ConfigDb connection
|
||||
|
||||
Per-cluster mesh **Phase 4** finished the job Phase 3 started: a **driver-only** node (`Cluster:Roles`
|
||||
has `driver`, not `admin`) now boots with **no `ConfigDb` connection string at all**. `Program.cs`
|
||||
gates `AddOtOpcUaConfigDb` (and its health check) on `hasAdmin`; a fused `admin,driver` node is
|
||||
unaffected (it keeps ConfigDb via its admin role). Consequences:
|
||||
|
||||
- **`ConfigSource:Mode` must be `FetchAndCache` on a driver-only node** — `ConfigSourceOptionsValidator`
|
||||
fails host start if such a node is left on `Direct` (there is nothing to read Direct from). A fused
|
||||
node may stay `Direct`.
|
||||
- **ServiceLevel semantics change — client-visible.** With no ConfigDb, `DbHealthProbeActor` is not
|
||||
spawned (`ServiceCollectionExtensions.WithOtOpcUaRuntimeActors` skips it when the resolved db
|
||||
factory is null), and `OpcUaPublishActor` runs `dbLess`: `DbReachable` is fed constant `true` and
|
||||
`Stale` comes from the redundancy-snapshot age alone. A healthy driver-only node therefore publishes
|
||||
**240** (or **250** as driver Primary) even with central SQL unreachable — the survive-alone posture
|
||||
— where a `Direct`/DB-backed node in the same state drops to 100/0. `ServiceLevelCalculator` itself
|
||||
is unchanged; only the inputs a DB-less node feeds it changed. See `docs/Redundancy.md` §"DB-less
|
||||
(driver-only) nodes".
|
||||
- **Scripted-alarm Part 9 condition state moved to LocalDb.** `LocalDbAlarmConditionStateStore`
|
||||
(replicated `alarm_condition_state` table, pair-local like the Phase-2 alarm S&F buffer) replaces
|
||||
`EfAlarmConditionStateStore` on every driver-role node, fused central included. The DB-backed
|
||||
`ScriptedAlarmState` table is now unused (dropping it is a deferred follow-up, tracked as Task 9).
|
||||
- **Central persists deploy acks.** `DriverHostActor` no longer writes `NodeDeploymentState` to SQL on
|
||||
a driver-only node — `ConfigPublishCoordinator.PersistNodeAck` (already fed by every ApplyAck) is
|
||||
the system of record.
|
||||
- **LDAP group→role DB grants don't reach driver-only nodes.** They register the empty
|
||||
`NullLdapGroupRoleMappingService` (no ConfigDb to read `LdapGroupRoleMapping` from), so
|
||||
`OtOpcUaGroupRoleMapper` falls back to the `Security:Ldap:GroupToRole` appsettings baseline only —
|
||||
the same rows a driver node never received via config either, so this is not a regression.
|
||||
|
||||
Code-complete on `feat/mesh-phase4`; the table-drop (Task 9) and the live gate (Task 10) are still
|
||||
open. See `docs/plans/2026-07-23-mesh-phase4-cut-driver-configdb.md` and
|
||||
`docs/plans/2026-07-22-per-cluster-mesh-program.md` §Phase 4.
|
||||
|
||||
## LDAP Authentication
|
||||
|
||||
The server uses LDAP-based user authentication via the `Security:Ldap` section in `appsettings.json`. When enabled, credentials are validated by LDAP bind against a GLAuth server, and LDAP group membership maps to OPC UA permissions: `ReadOnly` (browse/read), `WriteOperate` (write FreeAccess/Operate attributes), `WriteTune` (write Tune attributes), `WriteConfigure` (write Configure attributes), `AlarmAck` (alarm acknowledgment). `LdapOpcUaUserAuthenticator` (`src/Server/ZB.MOM.WW.OtOpcUa.Host/OpcUa/LdapOpcUaUserAuthenticator.cs`) implements `IOpcUaUserAuthenticator`, delegating the LDAP bind + group lookup to `OtOpcUaLdapAuthService` (`src/Server/ZB.MOM.WW.OtOpcUa.Security/Ldap/OtOpcUaLdapAuthService.cs`, an `ILdapAuthService`). See `docs/security.md` for the full guide.
|
||||
|
||||
Reference in New Issue
Block a user