fix(opcua): #855 perf review 1 — groups evaluate their own filter clone; lazy fields; tighter equivalence tests
- A filter group owns a deep clone of its first filter, validated once and evaluated under the group's lock: no item-owned filter is mutated or raced (Validate mutates a select clause). - The projection is computed in the SDK's order: where clause first, fields only for an item that will queue. - Unambiguous locale key; counter for filters given a private group past the registry cap; DeliveryCache getter returns its own allocation. - UseProjectedEventDelivery=false also asks the role and grant checks per item, so the equivalence test compares against the pre-optimisation path. - Equivalence test: same select list under three where clauses, a filter modified mid-run, a session with other locales, an ungranted anonymous Server-object item, per-rig counter; plus a small overflowing queue with DiscardOldest true and false. Claude-Session: https://claude.ai/code/session_01GT67AD99zQQqpLCVm3p7xk
This commit is contained in:
@@ -112,9 +112,10 @@ whose duplicate scan over the last ≤ 1000 queued events grows with the queue (
|
||||
`ProjectedEventDelivery` / `EventDeliveryCache` (per report, hung on the condition snapshot, dropped when the walk ends):
|
||||
|
||||
- the SDK role verdict once per (session, effective identity); the #826 grant check once per identity per hop;
|
||||
- the where clause and the select-clause projection once per (value-equal `EventFilter`, preferred locales) — filters
|
||||
are grouped once per filter object (`Utils.IsEqual`, ≤ 4096 shared groups) — and the field values shared read-only by
|
||||
every item queued from it (each item still gets its own `EventFieldList`: the SDK rewrites a queued list's
|
||||
- the where clause and the select-clause projection once per (value-equal `EventFilter`, preferred locales), in the
|
||||
SDK's order (fields only for an item that will queue) — filters are grouped once per filter object (`Utils.IsEqual`,
|
||||
≤ 4096 shared groups, then private groups, counted); a group evaluates its OWN deep clone, validated once, under its own
|
||||
lock, never an item's filter object — and the field values shared read-only by every item queued from it (each item still gets its own `EventFieldList`: the SDK rewrites a queued list's
|
||||
`ClientHandle` on modify);
|
||||
- the SDK's `QueueEvent(instance, false)` body reproduced under the item's own lock (event item; Disabled ⇒ nothing;
|
||||
duplicate ⇒ nothing — asked of the report's "already queued" set instead of the queue; the filter in use;
|
||||
@@ -123,6 +124,13 @@ whose duplicate scan over the last ≤ 1000 queued events grows with the queue (
|
||||
non-`EventFilter` filter, a `SupportsFilteredRetain` condition, a non-snapshot event, refresh replays) uses the SDK's
|
||||
own `QueueEvent(IFilterTarget)`.
|
||||
|
||||
`ProjectedEventDeliveryWireTests` runs one scenario through each path (`UseProjectedEventDelivery`) and requires
|
||||
`ProjectedEventDeliveryWireTests` runs one scenario through each path (`UseProjectedEventDelivery`, which also turns the
|
||||
verdict sharing off) and requires
|
||||
identical streams on folder and Server-object items (value-equal filters, a severity where clause, an OfType where
|
||||
clause with an absent field, a Disabled item); it fails if the where clause or a field is projected differently.
|
||||
clause with an absent field, the same select list under three where clauses, a filter modified mid-run, a second session
|
||||
with other locales, an ungranted anonymous Server-object item, a Disabled item) plus a small overflowing queue with
|
||||
DiscardOldest true and false; it fails if the where clause, a field or the grouping is wrong.
|
||||
|
||||
Residual: at a full queue the SDK's own `EventMonitoredItemQueue` still does `List.RemoveAt(0)` per item (memmove of the
|
||||
whole queue) — ~1–10 µs/item at QueueSize 1000–10 000; replacing the queue (`IMonitoredItemQueueFactory`) is a separate
|
||||
change.
|
||||
|
||||
Reference in New Issue
Block a user