fix(historian): #687 Gateway raw reads drop AVEVA's synthetic start-boundary row
An AVEVA Full raw read leads with a row stamped at the query start (the value in effect, Quality=133, or a no-data NULL row); it was returned as a stored sample. Raw reads now use the #681 classification (RawAggregateReader.RequestStartFor / IsWindowSample — one authority, by timestamp, since a stored NULL looks the same on the wire): request from 1 ms early with NumValuesPerNode + 16 rows, keep rows at or after StartTime, drain the whole stream. A full page still holds exactly the cap, so #669 paging/resume/tie-cluster reads are unchanged; a margin that swallows the allowance fails the node rather than ending paging early. ReturnBounds is not plumbed on any backend (documented).
This commit is contained in:
@@ -673,6 +673,32 @@ are disposed when the session closes). Resuming an unknown / evicted / released
|
||||
`BadContinuationPointInvalid`. `releaseContinuationPoints` drops the stored cursors without reading
|
||||
data.
|
||||
|
||||
### Gateway raw reads return stored samples only (#687)
|
||||
|
||||
> ⚠️ **Client-visible (#687).** An AVEVA `Full` raw read starts with a synthetic row stamped exactly at the **query start**: the
|
||||
> value in effect there (AVEVA `Quality = 133`, the previous sample's value and quality — a value never stored at that time) or,
|
||||
> before the tag's first sample, a NULL row (value 0, Bad, `Quality = 1`). The `Gateway` backend used to return that row as a stored
|
||||
> sample: a window with no stored sample in it answered one fabricated value stamped at StartTime (live: `[18:06:21, 18:06:39]` ⇒
|
||||
> `18:06:21 v=30 Good`), a window before the tag's history answered `v=0 Bad` instead of `GoodNoData`, and a start-only read led
|
||||
> with the fabricated row. It is now dropped; such windows read `GoodNoData`.
|
||||
|
||||
- **How.** The same classification the raw-evaluated aggregates use (#681, `RawAggregateReader.RequestStartFor` /
|
||||
`IsWindowSample` — one authority): the gateway is asked from **1 ms before** the window, so the boundary row lands before it, and
|
||||
only rows stamped at or after StartTime are returned. The decision is by **timestamp**, not by value or quality — a stored NULL
|
||||
sample looks exactly like the no-data boundary row on the wire (value 0, Bad), and a stored NULL inside the window is still
|
||||
returned (Bad), as it is a stored sample.
|
||||
- **Paging (#669) is unaffected.** The gateway is asked for `NumValuesPerNode + 16` rows (`NumValuesPerNode = 0` stays unlimited),
|
||||
so the boundary row cannot shorten a page: a full page still holds exactly `NumValuesPerNode` stored samples and carries a
|
||||
continuation point, and a resumed page (which re-reads from the last returned timestamp inclusive, and the tie-cluster over-fetch
|
||||
`start == end` read) no longer gets a fabricated row at its resume point. If more than 16 rows lie in the 1 ms before the window
|
||||
and exhaust that allowance, the page cannot be read completely and the node fails (`HistoryReadLimitExceededException`, not
|
||||
counted as a backend fault) rather than answering a short page that would end the client's paging early. Every gateway stream is
|
||||
still drained to completion.
|
||||
- **`ReturnBounds` is not honoured** — on any backend: the server does not pass it to the data source, so no bounding values are
|
||||
returned (see [Known limitations](#known-limitations)). The AVEVA boundary row is not used as a bound either: it is stamped at the
|
||||
query start rather than carrying the time of the real prior sample, so it could only ever be a Part 11 *interpolated* bound, which
|
||||
this backend does not compute.
|
||||
|
||||
### Continuation-point paging (Events) (#669)
|
||||
|
||||
A capped event read (`NumValuesPerNode > 0`) pages exactly like Raw, with one difference: the trigger is the **source's
|
||||
@@ -964,6 +990,8 @@ whatever the SDK sets — #682 touches only the i=11201 folder (follow-up candid
|
||||
sample per requested timestamp, so the single-shot backend returns the complete result in one
|
||||
read and there is no "full page ⇒ maybe more" signal to page on. Returning the full result with
|
||||
no continuation point is spec-conformant.
|
||||
- **`ReturnBounds` is ignored** (every backend): Raw reads return stored values only; no bounding values are added at the window
|
||||
edges. The node manager does not pass the flag to `IHistorianDataSource`.
|
||||
- **No modified-value history on the `Gateway` backend.** Modified reads (`IsReadModified = true`) are served by
|
||||
`SqlServer` and `QuestDb` ([Modified-value history](#modified-value-history-part-11), #668); the HistorianGateway exposes no
|
||||
modified/edited-history surface, so on `Gateway` (and with no historian) they return `BadHistoryOperationUnsupported`.
|
||||
|
||||
Reference in New Issue
Block a user