fix(runtime): every spawn hands the child its desired subscriptions (archreview 03/S16)

This commit is contained in:
Joseph Doherty
2026-08-20 09:36:52 -04:00
parent bdc81a1372
commit daaeb854d6
3 changed files with 33 additions and 10 deletions
@@ -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