Gateway historian: reverse and end-only capped raw HistoryReads fail (start > end sent to the gateway) #686
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found by the live AVEVA gate (2026-10-12). Regression from #669 (
50b504d2), undocumented.OtOpcUaNodeManager.cs:6222-6227turns an end-only raw read withNumValuesPerNode > 0into a reverse read(start=End, end=0001-01-01)for every backend — the events path guards onSupportsReverseEvents, the raw path does not.GatewayHistorianDataSource.ReadRawAsync(:79-104) forwardsstart > end; the gateway rejects itInvalidArgument "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.Fixed on master
1f5d3100(merge9ced6455, commits410c8d89,cf1af557+ docs).IHistorianDataSource.SupportsReverseRaw(SqlServer/QuestDB/Nulltrue, Gatewayfalse). The node manager never sends start > end to a backend without it;ReverseRawReaderserves 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. GatewayReadRawAsyncrefuses start > end before sending, not counted as a backend fault.max_values = 0, which the client/gateway reject — nowint.MaxValue; a ceiling refusal ⇒BadTooManyOperations. Caller cancellation is no longer counted as a backend failure. An oversized tie cluster isBadTooManyOperationson raw and events (wasBadHistoryOperationUnsupported).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.