fix(historian): #688 clamp the Gateway per-sample row budget at the gateway's default ceiling
The default MaxTimeWeightedRawRows (1,000,000) + 18 exceeded the gateway's default MaxValuesCeiling (1,000,000), so with AllowStreamingBeyondCeiling=false every Gateway per-sample aggregate read was refused. The allowance is now clamped at 1,000,000 while the cap is at or under it (RawAggregateReader. ClampToGatewayCeiling); a cap above it keeps its allowance. Raw reads are not clamped (rebased onto #686).
This commit is contained in:
+31
-1
@@ -369,7 +369,7 @@ public sealed class GatewayRawAggregateTests
|
||||
await Source(count).ReadProcessedAsync("A", At(0), At(80), TimeSpan.FromSeconds(20), HistoryAggregateType.Count, Ct);
|
||||
// The window read ends AT the window end; probe + look-ahead are separate 2-row reads, all consumed to completion.
|
||||
count.RawCalls.ShouldBe([
|
||||
(At(0) - TimeSpan.FromMilliseconds(1), At(80), 1_000_000 + 2 + 16),
|
||||
(At(0) - TimeSpan.FromMilliseconds(1), At(80), 1_000_000), // cap + 18, clamped at the gateway default ceiling (#688)
|
||||
(DateTime.UnixEpoch, At(0).AddTicks(-1), 2),
|
||||
(At(80), Now + TimeSpan.FromDays(1), 2),
|
||||
]);
|
||||
@@ -473,6 +473,36 @@ public sealed class GatewayRawAggregateTests
|
||||
ds2.GetHealthSnapshot().TotalFailures.ShouldBe(1);
|
||||
}
|
||||
|
||||
[Fact] // #688: the default cap's budget fits the gateway's default ceiling, so a strict gateway serves the default config
|
||||
public async Task The_default_caps_row_budget_does_not_exceed_the_gateways_default_ceiling()
|
||||
{
|
||||
var fake = Store([D(-5, 1), D(5, 2)], avevaBoundary: true);
|
||||
await Source(fake).ReadProcessedAsync("T", At(0), At(10), TimeSpan.Zero, HistoryAggregateType.Average, Ct);
|
||||
TimeWeightedAggregator.DefaultMaxWindowRows.ShouldBe(1_000_000);
|
||||
fake.RawCalls[0].MaxValues.ShouldBe(RawAggregateReader.GatewayDefaultMaxValuesCeiling);
|
||||
}
|
||||
|
||||
[Theory] // #688: the + 18 allowance is clamped at the gateway's default ceiling, never below the cap itself
|
||||
[InlineData(10, 28)]
|
||||
[InlineData(999_982, 1_000_000)]
|
||||
[InlineData(999_990, 1_000_000)]
|
||||
[InlineData(1_000_000, 1_000_000)]
|
||||
[InlineData(2_000_000, 2_000_018)] // a cap above the default ceiling is a deliberate choice for a streaming gateway
|
||||
[InlineData(0, int.MaxValue)]
|
||||
[InlineData(-1, int.MaxValue)]
|
||||
public void The_window_row_budget_is_clamped_at_the_gateways_default_ceiling(int cap, int expected) =>
|
||||
RawAggregateReader.MaxValuesFor(cap).ShouldBe(expected);
|
||||
|
||||
[Fact] // #688: near the ceiling the window is still served when it is not full
|
||||
public async Task A_cap_at_the_ceiling_still_serves_a_window_below_it()
|
||||
{
|
||||
var data = Enumerable.Range(0, 10).Select(i => D(i, i)).ToArray();
|
||||
var fake = Store(data, avevaBoundary: true);
|
||||
var r = await Source(fake, maxRawRows: 1_000_000)
|
||||
.ReadProcessedAsync("T", At(0), At(10), TimeSpan.Zero, HistoryAggregateType.Average, Ct);
|
||||
r.Samples.Single().Value.ShouldBe(4.5);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task A_caller_cancellation_is_not_counted_as_a_backend_failure()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user