Delete the dead in-place ApplyDelta seam (config change = respawn only) #665

Closed
opened 2026-10-09 11:20:02 -04:00 by dohertj2 · 1 comment
Owner

Follow-up to #663 / #664.

Problem. The in-place config path (host ApplyChildDelta → DriverInstanceActor.ApplyDelta → ReinitializeAsync, or AdoptConfigDuringInit mid-connect) has had no producer since #516: a DriverConfig change is stop + respawn, and DriverSpawnPlan.ToApplyDelta is always empty (pinned by DriverSpawnPlannerTests). #663 and #664 were both latent bugs in this dead code. Its remaining known gaps are also unreachable: a refused Galaxy change acks success on adoption, and Galaxy RawTags changes are not compared.

Change. Delete the seam so that a config change has exactly one path, the respawn:

  • Host: ApplyChildDelta, _pendingDelta, HandleApplyResult and the DeltaApplied handling.
  • Plan: DriverSpawnPlan.ToApplyDelta.
  • Actor: the ApplyDelta, ApplyResult and DeltaApplied messages, HandleApplyDeltaAsync, AdoptConfigDuringInit, and the adoption-notify flag.
  • Tests: the tests that exercise the seam.

IDriver.ReinitializeAsync and each driver's implementation and unit tests stay: it is an interface member, and removing it is a separate, larger decision.

Follow-up to #663 / #664. **Problem.** The in-place config path (host `ApplyChildDelta` → `DriverInstanceActor.ApplyDelta` → `ReinitializeAsync`, or `AdoptConfigDuringInit` mid-connect) has had no producer since #516: a DriverConfig change is stop + respawn, and `DriverSpawnPlan.ToApplyDelta` is always empty (pinned by `DriverSpawnPlannerTests`). #663 and #664 were both latent bugs in this dead code. Its remaining known gaps are also unreachable: a refused Galaxy change acks success on adoption, and Galaxy RawTags changes are not compared. **Change.** Delete the seam so that a config change has exactly one path, the respawn: - **Host:** `ApplyChildDelta`, `_pendingDelta`, `HandleApplyResult` and the `DeltaApplied` handling. - **Plan:** `DriverSpawnPlan.ToApplyDelta`. - **Actor:** the `ApplyDelta`, `ApplyResult` and `DeltaApplied` messages, `HandleApplyDeltaAsync`, `AdoptConfigDuringInit`, and the adoption-notify flag. - **Tests:** the tests that exercise the seam. `IDriver.ReinitializeAsync` and each driver's implementation and unit tests stay: it is an interface member, and removing it is a separate, larger decision.
Author
Owner

Done on master in 88ef1ea9 (merging 1ea1da82): 33 files, +112 / −559.

Deleted

  • Host: ApplyChildDelta, _pendingDelta, HandleApplyResult, HandleDeltaApplied, and their Receive registrations in all three states.
  • Plan: DriverSpawnPlan.ToApplyDelta. The record is now (ToSpawn, ToStop), so an in-place path can no longer be added without a compile-visible change.
  • Actor: the ApplyDelta / ApplyResult / DeltaApplied records, the Stubbed/Connecting/Reconnecting/Connected handlers, HandleApplyDeltaAsync, ReleaseAlarmSubscriptionAsync, AdoptConfigDuringInit, and the #664 notify flag.
  • Tests: six actor tests that drove the seam (four in DriverInstanceActorTests, the two-case #663 theory), the #663 stub additions, and the ten ToApplyDelta.ShouldBeEmpty() lines in DriverSpawnPlannerTests. The record no longer has the member, so those asserts are moot.

Kept

  • IDriver.ReinitializeAsync, each driver's implementation, and their unit tests. The interface doc now says the runtime does not call it.
  • The #664 driver changes: Calculation adopts a config with a body, and Galaxy refuses a non-equivalent one. Both keep InitializeAsync honest to the IDriver contract.
  • The #630 release-prior-state-at-init code in every driver, because a live driver can still be re-initialised without a teardown when a reconnect retry overlaps a running attempt. The comments that justified it with AdoptConfigDuringInit were rewritten to cite that instead.
  • The actor's init-generation guard, re-documented as deduping overlapping retry attempts.

Coverage lost: ApplyDelta_supersedes_in_flight_init_so_stale_result_is_ignored was the only test of the generation guard, and it drove the guard through ApplyDelta. The guard itself is unchanged.

Verification: full suite on the merged master: 13213 passed, 0 failed (exactly −6 from the deleted tests).

Docs: docs/DriverLifecycle.md, docs/IncrementalSync.md, docs/drivers/Mqtt.md, docs/v2/Runtime.md, CLAUDE.md. The scadaproj history entry is c713925. No behaviour change; ScadaBridge impact is none.

Done on master in `88ef1ea9` (merging `1ea1da82`): 33 files, +112 / −559. **Deleted** - **Host:** `ApplyChildDelta`, `_pendingDelta`, `HandleApplyResult`, `HandleDeltaApplied`, and their `Receive` registrations in all three states. - **Plan:** `DriverSpawnPlan.ToApplyDelta`. The record is now `(ToSpawn, ToStop)`, so an in-place path can no longer be added without a compile-visible change. - **Actor:** the `ApplyDelta` / `ApplyResult` / `DeltaApplied` records, the Stubbed/Connecting/Reconnecting/Connected handlers, `HandleApplyDeltaAsync`, `ReleaseAlarmSubscriptionAsync`, `AdoptConfigDuringInit`, and the #664 notify flag. - **Tests:** six actor tests that drove the seam (four in `DriverInstanceActorTests`, the two-case #663 theory), the #663 stub additions, and the ten `ToApplyDelta.ShouldBeEmpty()` lines in `DriverSpawnPlannerTests`. The record no longer has the member, so those asserts are moot. **Kept** - `IDriver.ReinitializeAsync`, each driver's implementation, and their unit tests. The interface doc now says the runtime does not call it. - The #664 driver changes: Calculation adopts a config with a body, and Galaxy refuses a non-equivalent one. Both keep `InitializeAsync` honest to the `IDriver` contract. - The #630 release-prior-state-at-init code in every driver, because a live driver can still be re-initialised without a teardown when a reconnect retry overlaps a running attempt. The comments that justified it with `AdoptConfigDuringInit` were rewritten to cite that instead. - The actor's init-generation guard, re-documented as deduping overlapping retry attempts. **Coverage lost:** `ApplyDelta_supersedes_in_flight_init_so_stale_result_is_ignored` was the only test of the generation guard, and it drove the guard through ApplyDelta. The guard itself is unchanged. **Verification:** full suite on the merged master: 13213 passed, 0 failed (exactly −6 from the deleted tests). **Docs:** `docs/DriverLifecycle.md`, `docs/IncrementalSync.md`, `docs/drivers/Mqtt.md`, `docs/v2/Runtime.md`, `CLAUDE.md`. The scadaproj history entry is `c713925`. No behaviour change; ScadaBridge impact is none.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dohertj2/lmxopcua#665