Gateway historian: reverse and end-only capped raw HistoryReads fail (start > end sent to the gateway) #686

Closed
opened 2026-10-12 14:20:49 -04:00 by dohertj2 · 1 comment
Owner

Found by the live AVEVA gate (2026-10-12). Regression from #669 (50b504d2), undocumented.

OtOpcUaNodeManager.cs:6222-6227 turns an end-only raw read with NumValuesPerNode > 0 into a reverse read (start=End, end=0001-01-01) for every backend — the events path guards on SupportsReverseEvents, the raw path does not. GatewayHistorianDataSource.ReadRawAsync (:79-104) forwards start > end; the gateway rejects it InvalidArgument "start_time must be less than or equal to end_time", which is recorded as a backend health failure and the node returns Bad.

Evidence: harness raw Gate681 18:07:45 → 0001-01-01 max=3 ⇒ HistorianGatewayException … (InvalidArgument).

Scope: every explicit reverse raw read on Gateway (the default backend), and every end-only capped raw read (which read forward before #669).

Fix options: a raw-read capability flag (like SupportsReverseEvents) so end-only capped reads stay forward on Gateway and explicit reverse reads are refused cleanly (BadHistoryOperationUnsupported, not counted as a backend fault); or implement reverse raw on Gateway by reading forward and reversing within the cap (bounded). Document either way.

Found by the live AVEVA gate (2026-10-12). Regression from #669 (`50b504d2`), undocumented. `OtOpcUaNodeManager.cs:6222-6227` turns an end-only raw read with `NumValuesPerNode > 0` into a reverse read `(start=End, end=0001-01-01)` for **every** backend — the events path guards on `SupportsReverseEvents`, the raw path does not. `GatewayHistorianDataSource.ReadRawAsync` (`:79-104`) forwards `start > end`; the gateway rejects it `InvalidArgument "start_time must be less than or equal to end_time"`, which is recorded as a backend health failure and the node returns Bad. Evidence: harness `raw Gate681 18:07:45 → 0001-01-01 max=3` ⇒ `HistorianGatewayException … (InvalidArgument)`. Scope: every explicit reverse raw read on Gateway (the default backend), and every end-only capped raw read (which read forward before #669). Fix options: a raw-read capability flag (like `SupportsReverseEvents`) so end-only capped reads stay forward on Gateway and explicit reverse reads are refused cleanly (`BadHistoryOperationUnsupported`, not counted as a backend fault); or implement reverse raw on Gateway by reading forward and reversing within the cap (bounded). Document either way.
Author
Owner

Fixed on master 1f5d3100 (merge 9ced6455, commits 410c8d89, cf1af557 + docs).

  • New capability IHistorianDataSource.SupportsReverseRaw (SqlServer/QuestDB/Null true, Gateway false). The node manager never sends start > end to a backend without it; ReverseRawReader serves end-only capped and explicit reverse reads by walking back in bounded forward segments (density-sized, ms-aligned, now+1 day / 1970 clamps). An uncapped reverse read is one forward read reversed in memory. Gateway ReadRawAsync refuses start > end before sending, not counted as a backend fault.
  • Also fixed: uncapped Gateway raw reads (forward too) sent max_values = 0, which the client/gateway reject — now int.MaxValue; a ceiling refusal ⇒ BadTooManyOperations. Caller cancellation is no longer counted as a backend failure. An oversized tie cluster is BadTooManyOperations on raw and events (was BadHistoryOperationUnsupported).
  • Live AVEVA gate PASS: end-only N=3 at 18:07:45 on HistGW.LiveTest.Gate681 (the failing case) returns newest-first and pages all 8 rows; explicit reverse paged complete; 1970 bound accepted; uncapped reverse = one read; 234 gateway reads, none start > end, no backend fault.
  • Follow-ups: #689 (page sizing on busy tags), #691.
Fixed on master `1f5d3100` (merge `9ced6455`, commits `410c8d89`, `cf1af557` + docs). - New capability `IHistorianDataSource.SupportsReverseRaw` (SqlServer/QuestDB/Null `true`, Gateway `false`). The node manager never sends start > end to a backend without it; `ReverseRawReader` serves end-only capped and explicit reverse reads by walking back in bounded forward segments (density-sized, ms-aligned, now+1 day / 1970 clamps). An uncapped reverse read is one forward read reversed in memory. Gateway `ReadRawAsync` refuses start > end before sending, not counted as a backend fault. - Also fixed: uncapped Gateway raw reads (forward too) sent `max_values = 0`, which the client/gateway reject — now `int.MaxValue`; a ceiling refusal ⇒ `BadTooManyOperations`. Caller cancellation is no longer counted as a backend failure. An oversized tie cluster is `BadTooManyOperations` on raw and events (was `BadHistoryOperationUnsupported`). - **Live AVEVA gate PASS:** end-only N=3 at 18:07:45 on `HistGW.LiveTest.Gate681` (the failing case) returns newest-first and pages all 8 rows; explicit reverse paged complete; 1970 bound accepted; uncapped reverse = one read; 234 gateway reads, none start > end, no backend fault. - Follow-ups: #689 (page sizing on busy tags), #691.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dohertj2/lmxopcua#686