HistoryRead paging (#669) follow-ups: reverse capped read, events end-only, Gateway year-9999 bound #675

Closed
opened 2026-10-11 10:18:14 -04:00 by dohertj2 · 2 comments
Owner

Follow-ups from #669 (HistoryRead continuation points).

  1. A reverse read with a value cap is silently truncated — Good, no continuation point, returned in ascending order. Either page it or document/return GoodMoreData.
  2. An events read with StartTime unset (end-only) is still treated as forward-from-beginning, while raw end-only now reads backward. Align or document.
  3. Gateway backend: the year-9999 (UTC max) upper bound used by start-only forward reads is unverified against the live gateway; QuestDB extreme-bounds conformance not run.
Follow-ups from #669 (HistoryRead continuation points). 1. A reverse read with a value cap is silently truncated — Good, no continuation point, returned in ascending order. Either page it or document/return `GoodMoreData`. 2. An events read with StartTime unset (end-only) is still treated as forward-from-beginning, while raw end-only now reads backward. Align or document. 3. Gateway backend: the year-9999 (UTC max) upper bound used by start-only forward reads is unverified against the live gateway; QuestDB extreme-bounds conformance not run.
Author
Owner

Items 1 and 2 done — merged to master (8e6ac4b3, review follow-ups 7594120a).

  • Reverse event reads (StartTime > EndTime) now return events newest-first and page with continuation points; window (EndTime, StartTime]; ties in descending id; direction lives in the cursor (a resumed reverse read continues reverse). New IHistorianDataSource.SupportsReverseEvents — SqlServer/QuestDB serve reverse natively (ORDER BY time DESC, id DESC); Gateway/Null refuse an explicit reverse read with BadHistoryOperationUnsupported without calling the backend. A guard test fails if any implementation inherits the reverse overload/capability from the interface default.
  • End-only capped events read ⇒ backward from EndTime on SqlServer/QuestDB (mirrors raw); stays forward on Gateway.
  • Resumed event pages are now full (the boundary-tie drop is over-fetched).
  • Item 3: offline tests pin UTC DateTime.MaxValue through the Gateway client's proto Timestamp + FILETIME path and onto the wire (in-process capture server).

Live gate (docker-dev, livegate-6) PASS on SqlServer + QuestDB: 28 events incl. a 9-event tie cluster, reverse reads at caps 1/3/4/5 on both centrals — every event exactly once, exact reverse order, ties descending id, resume stays reverse, window bounds correct, end-only backward. Gateway: explicit reverse ⇒ BadHistoryOperationUnsupported.

Left open: how the live AVEVA Historian treats a year-9999 end bound — needs the VPN to wonder-sql-vd03.

Items 1 and 2 done — merged to master (`8e6ac4b3`, review follow-ups `7594120a`). - **Reverse event reads** (StartTime > EndTime) now return events newest-first and page with continuation points; window `(EndTime, StartTime]`; ties in descending id; direction lives in the cursor (a resumed reverse read continues reverse). New `IHistorianDataSource.SupportsReverseEvents` — SqlServer/QuestDB serve reverse natively (`ORDER BY time DESC, id DESC`); Gateway/Null refuse an explicit reverse read with `BadHistoryOperationUnsupported` without calling the backend. A guard test fails if any implementation inherits the reverse overload/capability from the interface default. - **End-only capped events read** ⇒ backward from EndTime on SqlServer/QuestDB (mirrors raw); stays forward on Gateway. - Resumed event pages are now full (the boundary-tie drop is over-fetched). - Item 3: offline tests pin UTC `DateTime.MaxValue` through the Gateway client's proto `Timestamp` + FILETIME path and onto the wire (in-process capture server). **Live gate (docker-dev, livegate-6) PASS on SqlServer + QuestDB:** 28 events incl. a 9-event tie cluster, reverse reads at caps 1/3/4/5 on both centrals — every event exactly once, exact reverse order, ties descending id, resume stays reverse, window bounds correct, end-only backward. Gateway: explicit reverse ⇒ `BadHistoryOperationUnsupported`. **Left open:** how the live AVEVA Historian treats a year-9999 end bound — needs the VPN to `wonder-sql-vd03`.
Author
Owner

Live AVEVA leg PASSED (2026-10-12, VPN, local HistorianGateway → wonder-sql-vd03). The year-9999 end bound (OtOpcUaNodeManager maps an unset EndTime to DateTime.MaxValue): raw start-only forward reads [18:06:30 .. 9999-12-31T23:59:59.9999999Z] with caps 5/50 (and SysTimeSec cap 3) were accepted in ~200 ms with correct rows (no terminal row at 9999 or at now); events ReadEvents [2026-10-01, 9999-12-31] returned 1068 events in 1.5 s. Closing — all legs done.

The same gate found that reverse / end-only capped raw reads fail on Gateway (a #669 regression) — filed as #686.

**Live AVEVA leg PASSED (2026-10-12, VPN, local HistorianGateway → wonder-sql-vd03).** The year-9999 end bound (`OtOpcUaNodeManager` maps an unset EndTime to `DateTime.MaxValue`): raw start-only forward reads `[18:06:30 .. 9999-12-31T23:59:59.9999999Z]` with caps 5/50 (and `SysTimeSec` cap 3) were accepted in ~200 ms with correct rows (no terminal row at 9999 or at now); events `ReadEvents [2026-10-01, 9999-12-31]` returned 1068 events in 1.5 s. Closing — all legs done. The same gate found that reverse / end-only capped **raw** reads fail on Gateway (a #669 regression) — filed as #686.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dohertj2/lmxopcua#675