fix(virtual-tags): evaluate scripts off the Akka dispatcher thread (lmxopcua#779)
VirtualTagActor called the blocking IVirtualTagEvaluator.Evaluate, and RoslynVirtualTagEvaluator blocked on the run guard with .GetAwaiter().GetResult(), so every script run parked a dispatcher thread for up to its 2 s budget (a first-use Roslyn compile did too). This is the #750 P0 refactor for virtual tags. - IVirtualTagEvaluator.EvaluateAsync (default: a completed task over the snapshot overload, so the null evaluator and fakes are unchanged). - RoslynVirtualTagEvaluator implements it without blocking: passthrough and compile failures complete synchronously, a first-use compile goes to the pool, the run is awaited. The blocking overload now delegates to it. - VirtualTagActor: a completed task is applied in the same turn, as before; an incomplete one switches to an "evaluating" behaviour that stashes every dependency/reassert message until a private completion message arrives, then applies the result and unstashes. Each trigger is still evaluated against its own inputs, results are applied/published in trigger order and the dedup/Bad-transition state sees the same sequence. The dependency map is not touched while a run reads it. Calculation and scripted-alarm serialisation is NOT relaxed: neither blocks a thread, and running independent scripts in parallel is not semantics-neutral (pool consumption bound, cross-tag publish order, timer/pump exclusion for Calc; state-machine/persist boundary, transition timestamps and the store-failure path for alarms). Recorded in docs/VirtualTags.md and the #750 design's P0 entry. Tests: VirtualTagActorAsyncEvaluationTests — on a one-thread dispatcher a slow script no longer starves a sibling actor and the slow actor answers Identify (fails when the actor calls the blocking Evaluate); triggers during a run are evaluated afterwards in order against their own inputs; a reassert during a run is applied after it; a faulted evaluation takes the evaluator-threw path (the ordering/reassert tests fail with the stash removed). RoslynVirtualTagEvaluatorAsyncTests — a slow script and a cold compile return an incomplete task (fail without the override); cheap outcomes and timeouts match the blocking overload. Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
@@ -656,6 +656,10 @@ per-shard state, restarts in the last hour and the quarantine list. Quarantines
|
||||
- Gate: every existing Scripting / VirtualTags / Calculation / ScriptedAlarms suite stays green unchanged,
|
||||
plus new ordering tests for batch-then-apply.
|
||||
- This phase has value on its own: it ends the dispatcher-thread blocking.
|
||||
- **Partly landed in lmxopcua#779:** VT evaluation is async (`IVirtualTagEvaluator.EvaluateAsync` + stash in
|
||||
`VirtualTagActor`), so the dispatcher-thread blocking is gone. The Calc pump and the alarm engine still run one
|
||||
script at a time; why that was not relaxed in-process is recorded in `docs/VirtualTags.md` §"Upstream-tag feed".
|
||||
`IScriptRunner` and batch-submit remain to do.
|
||||
2. **P1, worker + protocol behind the flag.**
|
||||
- Ship `ScriptWorker`, `OutOfProcessScriptRunner` and the PE cache.
|
||||
- Default stays `InProcess`. Enable on the docker-dev rig only.
|
||||
|
||||
Reference in New Issue
Block a user