perf(otopcua): one SetDesiredSubscriptions per driver per redeploy (follow-up D)
This commit is contained in:
@@ -1241,9 +1241,21 @@ public sealed class DriverHostActor : ReceiveActor, IWithTimers
|
||||
_driverRefByNodeId[nodeId] = key;
|
||||
}
|
||||
|
||||
// Snapshot the cached (FixedTree-discovered) driver set BEFORE the bulk loop, while _discoveredByDriver
|
||||
// is still untouched (the re-inject tail below drops/removes entries). Cached drivers are SKIPPED in the
|
||||
// bulk loop because the tail sends each of them EXACTLY ONE SetDesiredSubscriptions for this pass: the
|
||||
// authored∪discovered union (ApplyDiscoveredPlansForDriver) for a survivor, or — if its plan is fully
|
||||
// dropped — an authored-only fallback. Sending the bulk authored-only set HERE too would force the child
|
||||
// to drop the whole handle (authored tags included) then re-subscribe — an extra unsub/resub blip of the
|
||||
// authored values once per cached driver per redeploy. Net effect: exactly ONE send per driver per pass.
|
||||
var cachedDriverIds = _discoveredByDriver.Keys.ToHashSet(StringComparer.Ordinal);
|
||||
|
||||
var total = 0;
|
||||
foreach (var (driverId, entry) in _children)
|
||||
{
|
||||
// Cached drivers are owned exclusively by the re-inject tail (one send each) — skip here. Non-cached
|
||||
// drivers keep the bulk authored-only send exactly as before.
|
||||
if (cachedDriverIds.Contains(driverId)) continue;
|
||||
var refs = refsByDriver.TryGetValue(driverId, out var r) ? r : Array.Empty<string>();
|
||||
var alarmRefs = alarmRefsByDriver.TryGetValue(driverId, out var ar) ? ar : Array.Empty<string>();
|
||||
entry.Actor.Tell(new DriverInstanceActor.SetDesiredSubscriptions(refs, SubscriptionPublishingInterval, alarmRefs));
|
||||
@@ -1353,6 +1365,19 @@ public sealed class DriverHostActor : ReceiveActor, IWithTimers
|
||||
if (plansByEquipment.Count == 0)
|
||||
{
|
||||
_discoveredByDriver.Remove(driverId);
|
||||
// FALLBACK (one-send invariant): this driver was SKIPPED in the bulk loop (it was cached), and its
|
||||
// plan is now FULLY DROPPED — so ApplyDiscoveredPlansForDriver won't run for it and it would
|
||||
// otherwise receive ZERO sends this pass, losing its AUTHORED subscriptions. Send the authored-only
|
||||
// set NOW (the SAME payload the bulk loop computes), so the authored tags subscribe in THIS pass.
|
||||
// (The TriggerRediscovery above handles the async FixedTree re-graft separately; this just keeps
|
||||
// the authored values live meanwhile.) Guarded on the child still existing — a driver removed by
|
||||
// ReconcileDrivers has no child and correctly gets no send.
|
||||
if (_children.TryGetValue(driverId, out var fallbackEntry))
|
||||
{
|
||||
var refs = refsByDriver.TryGetValue(driverId, out var r) ? r : Array.Empty<string>();
|
||||
var alarmRefs = alarmRefsByDriver.TryGetValue(driverId, out var ar) ? ar : Array.Empty<string>();
|
||||
fallbackEntry.Actor.Tell(new DriverInstanceActor.SetDesiredSubscriptions(refs, SubscriptionPublishingInterval, alarmRefs));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
ApplyDiscoveredPlansForDriver(driverId, plansByEquipment);
|
||||
|
||||
Reference in New Issue
Block a user