Commit Graph

5 Commits

Author SHA1 Message Date
Joseph Doherty 3f074dc4e7 test(historian): pass TestContext.Current.CancellationToken in the batched alarm-writer test (lmxopcua#746)
The batched-alarm-history test came from the lane-H2 branch, which predates
the xUnit1051 sweep on master (lmxopcua#725). Two no-token calls broke the
Release build under TreatWarningsAsErrors after the rebase.

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 15:40:47 -05:00
Joseph Doherty 29665c1872 feat(opcua): HistoryReadEvents projects EventType, ConditionName, SourceNode; honours the where-clause (lmxopcua#746)
HistoryReadEvents projected only EventId / SourceName / Time / ReceiveTime /
Message / Severity: a select clause asking for EventType, ConditionName or
SourceNode (what most generic alarm viewers ask for) got nulls, and the
event filter's where-clause (an OfType) was ignored.

- HistoricalEvent gains optional EventTypeName, ConditionName and AlarmId.
  SqlServer, QuestDB and Gateway event reads fill them from what their
  alarm writers already store (AlarmTypeName / AlarmName / AlarmId; the
  gateway event Type and its AlarmName / AlarmId properties).
- The node manager maps the type name to its Part 9 event type (unknown or
  non-event => AlarmConditionType), projects ConditionName, and resolves
  SourceNode to the materialised condition the alarm id names (the live
  events' SourceNode), else null.
- The where-clause is evaluated per event with the SDK content filter
  through an IFilterTarget (OfType via the type tree; other operators
  against the projected fields), after the page's cursor is taken, so
  paging is unchanged.

Tested in the node manager, over a real client session (the Client.CLI has
no `historyread --events` mode yet; filed as a follow-up), and by alarm
writer -> event read round trips on SqlServer and QuestDB.

Rebased from the unpushed remed/lane-H2 branch (originally authored 2026-10-22
in session_01Ub9hGmYLQvn9XETzw7CMsT) onto current master.

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 15:40:47 -05:00
Joseph Doherty 74afc3f90a perf(historian): batch alarm-history writes; pipeline gateway SendEvent (lmxopcua#746)
All three alarm-history writers inserted one event per round trip, serially
(SqlServer also opened a pooled connection per event), so a 10000-event
alarm storm drained at 10000 x RTT while the backlog sat in LocalDb.

- Sql.Common.AlarmBatchWrite: one statement per chunk; a chunk that fails
  PermanentFail-class is re-sent event by event so only the poison event
  dead-letters; a transient failure retries the chunk; a cancellation never
  dead-letters.
- SqlServer: multi-row insert-if-absent over a VALUES set (150 events per
  statement, under the 2100-parameter limit), duplicate ids in a batch
  written once; a duplicate-key race isolates per event, where it is Ack.
  No new table type, so no schema migration (the review suggested a TVP;
  the VALUES form gets the same one-round-trip-per-chunk without one).
- QuestDb: multi-row INSERTs of 500 events; pre-1970 events still
  dead-letter unsent.
- Gateway: no batch RPC exists, so sends are pipelined, at most 8 in
  flight, outcomes kept in input order.

Rebased from the unpushed remed/lane-H2 branch (originally authored 2026-10-22
in session_01Ub9hGmYLQvn9XETzw7CMsT) onto current master.

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 15:40:46 -05:00
Joseph Doherty 1d7a4b7a53 test: pass TestContext.Current.CancellationToken at xUnit1051 call sites (analyzer code fix, 451 sites) (lmxopcua#725)
Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-09 13:19:34 -05:00
Joseph Doherty bef47f89da feat(historian): SQL Server value writer, tag provisioner, alarm writer (T9)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-09 13:08:43 -04:00