fix(runtime): every spawn hands the child its desired subscriptions (archreview 03/S16)
This commit is contained in:
@@ -504,3 +504,20 @@ Deviation from the plan's literal step text (cosmetic, recorded for the reviewer
|
||||
step 1 says `_desiredByDriver` *replaces* `_lastComposition` at `:347-349` while T3 step 2 says T3
|
||||
deletes `_lastComposition`. Keeping T2 compilable means the two fields coexist for one commit;
|
||||
`_lastComposition` and its assignment are deleted in T3 as T3's own step text specifies.
|
||||
|
||||
### T3 — `SpawnChild` always sends the desired set; `_lastComposition` deleted
|
||||
|
||||
The falsification for this production slice is **T5**, written and observed red afterwards (below) —
|
||||
T3 lands the fix first because the plan sequences it that way and the ordering guard needs the whole
|
||||
25-suite regression before any new test is trusted on top of it.
|
||||
|
||||
**HIGH-RISK regression gate (every child's spawn-time state changes, deploy path included):** the
|
||||
FULL `Runtime.Tests` project, not just the `DriverHostActor` filter —
|
||||
**639 passed, 3 skipped, 0 failed, 55 s**. The 3 skips are pre-existing (`DeploymentArtifact*` parity
|
||||
tests, skipped on the base commit too).
|
||||
|
||||
`_lastComposition` is gone: `grep -c _lastComposition DriverHostActor.cs` → **0**. Its xmldoc had also
|
||||
drifted (it claimed the field was set at the end of `PushDesiredSubscriptions`; the assignment was in
|
||||
`ApplyDesiredSubscriptions`) — deleted with the field rather than corrected. The stale
|
||||
"dropped-driver fallback" comment on the old `SendAuthoredOnly` local function was rewritten as the
|
||||
`SendDesiredSubscriptions` remarks naming the three real consumers.
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
{
|
||||
"id": "T3",
|
||||
"subject": "SpawnChild always sends the desired set; delete the write-only _lastComposition (03/S16)",
|
||||
"status": "pending",
|
||||
"status": "done",
|
||||
"blockedBy": [
|
||||
"T2"
|
||||
]
|
||||
|
||||
@@ -344,10 +344,6 @@ public sealed class DriverHostActor : ReceiveActor, IWithTimers, IWithUnboundedS
|
||||
/// the config (#516). Bounded by the number of driver children with a delta in flight.</summary>
|
||||
private readonly Dictionary<CorrelationId, DriverInstanceSpec> _pendingDelta = new();
|
||||
|
||||
/// <summary>The composition from the most-recent apply (set at the END of
|
||||
/// <see cref="PushDesiredSubscriptions"/>). Null until the first apply.</summary>
|
||||
private AddressSpaceComposition? _lastComposition;
|
||||
|
||||
/// <summary>One driver child's desired subscription payload — exactly the
|
||||
/// <see cref="DriverInstanceActor.SetDesiredSubscriptions"/> the apply's bulk push sent it.</summary>
|
||||
/// <param name="ValueRefs">The non-alarm raw tags' RawPaths this driver should keep subscribed.</param>
|
||||
@@ -2721,11 +2717,6 @@ public sealed class DriverHostActor : ReceiveActor, IWithTimers, IWithUnboundedS
|
||||
_log.Info("DriverHost {Node}: applied {Count} Equipment ScriptedAlarm(s) to the ScriptedAlarm host",
|
||||
_localNode, composition.EquipmentScriptedAlarms.Count);
|
||||
}
|
||||
|
||||
// Cache the applied composition LAST. Set here (not in HandleArtifactForApply) so both the fresh-apply and
|
||||
// bootstrap-restore paths — which both route through this method — leave a current composition.
|
||||
_lastComposition = composition;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -2827,6 +2818,21 @@ public sealed class DriverHostActor : ReceiveActor, IWithTimers, IWithUnboundedS
|
||||
|
||||
_children[spec.DriverInstanceId] = new ChildEntry(child, spec, stub);
|
||||
|
||||
// EVERY spawn hands the child its desired set — deploy reconcile, operator Restart (03/S16), and the
|
||||
// supervision reprovision. Sending it HERE rather than at each call site is the point: a child that
|
||||
// exists without its subscription set is a dead driver, so the two facts are established together.
|
||||
// Before this, the only sender was the apply's bulk push, so an operator's Restart produced a child
|
||||
// that connected and then read nothing until the next deployment — the #488 periodic reconcile cannot
|
||||
// help, because it early-returns on an empty desired set.
|
||||
//
|
||||
// ORDERING, deliberately: ApplyDriverSpecs (which spawns) runs BEFORE ApplyDesiredSubscriptions (which
|
||||
// rebuilds _desiredByDriver), so a newly-spawned child is handed the PREVIOUS apply's set — or nothing,
|
||||
// on the first deploy. That transient is superseded milliseconds later by the bulk push on the same
|
||||
// sender→receiver pair (Akka guarantees per-pair ordering), and the child is still Connecting, where
|
||||
// SetDesiredSubscriptions only STORES; the subscribe happens at Connected entry. So the stale set is
|
||||
// never acted on. Sent after InitializeRequested above, which keeps init-before-subscribe ordering.
|
||||
SendDesiredSubscriptions(child, spec.DriverInstanceId);
|
||||
|
||||
// v3 (WP7): if the (real, non-stub) driver consumes other tags' live values, spawn a mux-adapter
|
||||
// child that registers its dependency refs on the per-node dependency mux and forwards changes
|
||||
// into the driver. Requires a wired mux (dev/None deployments have none — then calc inputs simply
|
||||
|
||||
Reference in New Issue
Block a user