fix(historian): #687 re-review — a row at/before the Unix epoch is never a stored sample

With StartTime at or before the epoch, RequestStartFor has no margin left, so
AVEVA's synthetic no-data/boundary row is stamped exactly at StartTime and was
kept: raw reads returned a fabricated Bad sample at 1970-01-01 and the per-sample
aggregates put it in bucket 0 (Start/End fabricated, Min/Max status Uncertain).
IsWindowSample and the aggregate loop now drop any row with t <= ProbeFloor
(RawAggregateReader.IsAfterFloor), matching the #686 walk-back's assumption.
This commit is contained in:
Joseph Doherty
2026-10-12 15:47:39 -04:00
parent 8114f13a28
commit a3ee917fb4
4 changed files with 43 additions and 2 deletions
+3 -1
View File
@@ -686,7 +686,9 @@ data.
the gateway is asked from **1 ms before** the window (never before the Unix epoch, the start of the historian's time encoding),
so the boundary row lands before it, and only rows `RawAggregateReader.IsWindowSample` 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.
Bad), and a stored NULL inside the window is still returned (Bad), as it is a stored sample. A row stamped **at or before the
epoch** is never a stored sample (AVEVA stores nothing there — the #686 walk-back's floor too): a window starting at or before
the epoch gets no margin, so AVEVA's no-data row lands exactly at its start; raw reads and the per-sample aggregates both drop it.
- **The forward end is exclusive** (`[StartTime, EndTime)`, the `IHistorianDataSource` contract and what the SQL backends return;
the gateway's own `ReadRaw` end is inclusive, so a sample stored exactly at EndTime used to be returned). A point read
(`StartTime == EndTime` — the server's tie-cluster over-fetch) still returns the samples at that instant. The #686 reverse