90bfcbe47a
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
72 lines
5.0 KiB
Markdown
72 lines
5.0 KiB
Markdown
# Incremental Sync
|
|
|
|
How a change reaches the running server's address space in v3. There is exactly one path that
|
|
changes the served address space — the **deploy artifact** — and one advisory signal
|
|
(`IRediscoverable`) that never changes it.
|
|
|
|
## The deploy-artifact path
|
|
|
|
1. **Compose.** `ConfigComposer` (`src/Server/ZB.MOM.WW.OtOpcUa.ControlPlane/AdminOperations/ConfigComposer.cs`)
|
|
snapshots the whole Config DB into one gzip-compressed JSON blob (`Deployment.ArtifactBlob`) whose
|
|
`RevisionHash` is the SHA-256 of those exact bytes. A deployment is fleet-wide; each node
|
|
self-scopes the artifact (`DeploymentArtifact.ResolveClusterScope`).
|
|
2. **Dispatch.** `ConfigPublishCoordinator` (`.../ControlPlane/Coordinators/`) sends each enabled
|
|
`ClusterNode` a payload-free dispatch and waits for acks until the apply deadline. A node obtains the
|
|
bytes either straight from central SQL (`ConfigSource:Mode=Direct`) or over gRPC with a SHA-256
|
|
check (`FetchAndCache`) — see `docs/Configuration.md` §`ConfigSource`.
|
|
3. **Apply.** `DriverHostActor` (`src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/`) opens the artifact and
|
|
reconciles in two halves:
|
|
- **Drivers** — `DriverSpawnPlanner.Compute` yields `ToStop` / `ToApplyDelta` / `ToSpawn`. A changed
|
|
driver config reaches the live driver as `IDriver.ReinitializeAsync` on its `DriverInstanceActor`;
|
|
a removed driver is stopped. An unreadable or empty blob is "no answer": the apply fails and the
|
|
running children are left alone (never an empty configuration).
|
|
- **Address space** — `AddressSpaceComposer` builds an `AddressSpaceComposition` from the artifact;
|
|
`AddressSpacePlan` diffs it against the last applied composition; `AddressSpaceChangeClassifier`
|
|
picks the delta class; `AddressSpaceApplier`
|
|
(`src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/`) mutates `OtOpcUaNodeManager` through the sink.
|
|
4. **Classes** (`AddressSpaceChangeKind`): `Empty`, `AttributeOnly`, `PureAdd`, `PureRemove`,
|
|
`AddRemoveMix` — all applied surgically, so unrelated MonitoredItems survive — and `Rebuild`, the
|
|
default-closed fallback for anything the classifier does not whitelist. Any surgical `false`/throw
|
|
also ratchets to a safe rebuild, and a degraded non-rebuild apply does not advance the last-applied
|
|
composition (the next deploy re-diffs and re-ensures). The whitelist, the UNS-mirror repair rule and
|
|
the add/remove announcements are described in the "surgical deploys" paragraph of `CLAUDE.md`
|
|
and `docs/plans/2026-08-15-surgical-deploys.md`.
|
|
|
|
Virtual tags and scripted alarms ride the same artifact and the same plan; a script edit is just another
|
|
changed record in the composition (see `docs/VirtualTags.md`, `docs/ScriptedAlarms.md`).
|
|
|
|
## Driver-backend rediscovery is advisory
|
|
|
|
Drivers whose backend signals a tag-set change implement `IRediscoverable`
|
|
(`src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IRediscoverable.cs`; `OnRediscoveryNeeded` carries a
|
|
`Reason` and an optional `ScopeHint`). Raisers today: **Galaxy** (`DeployWatcher`, new
|
|
`time_of_last_deploy`), **TwinCAT** (ADS symbol-version-changed), **MQTT/Sparkplug** and **MTConnect**.
|
|
AbCip, AbLegacy, FOCAS, Modbus, S7 and the rest raise nothing.
|
|
|
|
`DriverInstanceActor` is the only production consumer: it subscribes in `PreStart`, detaches in
|
|
`PostStop`, and folds the raise into the driver-health snapshot
|
|
(`DriverHealthChanged.RediscoveryNeededUtc` / `.RediscoveryReason`), which the AdminUI `/hosts` page shows
|
|
as a **re-browse** chip. **It never rebuilds or edits the served address space** — v3 raw tags are authored
|
|
explicitly in `/raw`, so an operator re-browses the device and commits, and the result reaches the
|
|
server as an ordinary deployment. `ScopeHint` is not read by the actor. (The older runtime
|
|
injection path was removed, #507 closed as superseded.)
|
|
|
|
## Not part of the v3 path
|
|
|
|
- `sp_PublishGeneration` / `sp_ComputeGenerationDiff` are stubs that `RAISERROR` in the v3 baseline
|
|
migration; there is no generation-publish flow and no `DraftRevisionToken`.
|
|
- `GenericDriverNodeManager` (`Core/OpcUa/`) is retained scaffolding with no production caller; the
|
|
diff / teardown / subscription-replay "rebuild flow" described by earlier versions of this page does not
|
|
exist in `src/`.
|
|
|
|
## Key source files
|
|
|
|
- `src/Server/ZB.MOM.WW.OtOpcUa.ControlPlane/AdminOperations/ConfigComposer.cs` — artifact composition + hash
|
|
- `src/Server/ZB.MOM.WW.OtOpcUa.ControlPlane/Coordinators/ConfigPublishCoordinator.cs` — dispatch + ack set
|
|
- `src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverHostActor.cs` — apply + driver reconcile
|
|
- `src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DeploymentArtifact.cs` — node-side artifact parsing
|
|
- `src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/AddressSpaceComposer.cs`, `AddressSpacePlan.cs`,
|
|
`AddressSpaceChangeClassifier.cs`, `AddressSpaceApplier.cs` — compose, diff, classify, apply
|
|
- `src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverInstanceActor.cs` — `IRediscoverable` consumer
|
|
- `src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/ISurgicalAddressSpaceSink.cs` — in-place update seam
|