diff --git a/code-reviews/Driver.Historian.Wonderware/findings.md b/code-reviews/Driver.Historian.Wonderware/findings.md index 4052a95..70d9b9d 100644 --- a/code-reviews/Driver.Historian.Wonderware/findings.md +++ b/code-reviews/Driver.Historian.Wonderware/findings.md @@ -235,7 +235,7 @@ treat an SDK error as an empty history. | Severity | Medium | | Category | Performance and resource management | | Location | `Backend/HistorianDataSource.cs:382-395`, `Ipc/Contracts.cs:85-99` | -| Status | Open | +| Status | Resolved | **Description:** `ReadAggregateAsync` drains `query.MoveNext` into `results` with no upper bound, unlike `ReadRawAsync`, which honours `maxValues` / @@ -252,7 +252,7 @@ sidecar holds the whole result set in memory. `ReadProcessedRequest`. Reject or truncate result sets that would exceed the frame cap with an explicit error reply rather than letting `WriteAsync` throw. -**Resolution:** _(open)_ +**Resolution:** Resolved 2026-05-22 — applied `_config.MaxValuesPerRead` as a bucket cap in `ReadAggregateAsync` mirroring the raw-read path; truncation logs a Warning with the limit and a hint to widen `IntervalMs` or reduce the time range. ### Driver.Historian.Wonderware-010 diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Backend/HistorianDataSource.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Backend/HistorianDataSource.cs index 770e7cb..4e0def7 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Backend/HistorianDataSource.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware/Backend/HistorianDataSource.cs @@ -373,6 +373,12 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend return Task.FromResult(results); } + // Apply the same bucket cap as the raw-read path so a wide time range with a + // small IntervalMs cannot produce an unbounded result set that would overflow + // the 16 MiB FrameWriter frame cap and lose the entire reply. + var bucketLimit = _config.MaxValuesPerRead; + var bucketCount = 0; + while (query.MoveNext(out error)) { ct.ThrowIfCancellationRequested(); @@ -386,6 +392,15 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Backend Value = value, TimestampUtc = timestamp, }); + + bucketCount++; + if (bucketLimit > 0 && bucketCount >= bucketLimit) + { + Log.Warning( + "HistoryRead aggregate ({Aggregate}): {Tag} truncated at {Limit} buckets — widen IntervalMs or reduce time range", + aggregateColumn, tagName, bucketLimit); + break; + } } query.EndQuery(out _);