fix(server): resolve Medium code-review finding (Server-003)

Fix ReadRawAsync: correct XML doc from newest-first to oldest-first
(ascending source timestamp per OPC UA Part 11); move maxValuesPerNode
cap inside the time-window filter loop so paging limits apply to
in-window results only, not the whole buffer snapshot.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-05-22 10:54:08 -04:00
parent e774b6f88d
commit 2003b343bf
2 changed files with 15 additions and 7 deletions

View File

@@ -90,8 +90,12 @@ public sealed class RingBufferHistoryWriter : IHistoryWriter, IHistorianDataSour
/// <summary>
/// Returns samples in the ring buffer whose source timestamp falls within
/// [<paramref name="startUtc"/>, <paramref name="endUtc"/>), newest-first.
/// <paramref name="maxValuesPerNode"/> caps the result count.
/// [<paramref name="startUtc"/>, <paramref name="endUtc"/>), oldest-first
/// (ascending source timestamp, matching OPC UA Part 11 §6.4 raw-values default).
/// <paramref name="maxValuesPerNode"/> caps the count of <em>in-window</em> results,
/// not the whole buffer snapshot — a paged read therefore returns the oldest
/// <paramref name="maxValuesPerNode"/> samples within the time window, not the oldest
/// samples in the buffer irrespective of the window (Server-003).
/// </summary>
public Task<HistoryReadResult> ReadRawAsync(
string fullReference,
@@ -104,15 +108,19 @@ public sealed class RingBufferHistoryWriter : IHistoryWriter, IHistorianDataSour
return Task.FromResult(new HistoryReadResult([], null));
var all = buffer.Snapshot();
var limit = (int)Math.Min(maxValuesPerNode, (uint)all.Length);
var result = new List<DataValueSnapshot>(limit);
// Iterate the full snapshot (oldest → newest) and apply the time-window filter first;
// then cap with maxValuesPerNode so the limit applies to in-window results only.
// Snapshot() returns an array bounded by _capacity so this is O(capacity) at most.
var result = new List<DataValueSnapshot>();
foreach (var snap in all)
{
if (result.Count >= limit) break;
var ts = snap.SourceTimestampUtc ?? snap.ServerTimestampUtc;
if (ts >= startUtc && ts < endUtc)
{
result.Add(snap);
if (maxValuesPerNode > 0 && result.Count >= (int)maxValuesPerNode) break;
}
}
return Task.FromResult(new HistoryReadResult(result, null));