Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Darling/Darling.Tests/DarlingMcpTrendToolsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,10 @@ public void DurationTrendHourlySql_ReadsTheRollup_BucketsByIt_ProjectsTheSharedS
Assert.DoesNotContain("FROM " + rawTable + "\n", sql, StringComparison.Ordinal);
Assert.DoesNotContain("collection_time >=", sql, StringComparison.Ordinal);
Assert.Contains("bucket >= $2", sql, StringComparison.Ordinal);
Assert.Contains("bucket <= $3", sql, StringComparison.Ordinal);
/* A bucket is stamped at its START, so the window end is exclusive: `bucket <= $3` would take the whole
hour that begins at an end falling on the hour (RollupWindowEndBoundTests pins the same for every rollup read). */
Assert.Contains("bucket < $3", sql, StringComparison.Ordinal);
Assert.DoesNotContain("bucket <= $3", sql, StringComparison.Ordinal);
Assert.Contains("GROUP BY bucket", sql, StringComparison.Ordinal);

/* Since #3897 the rollup's hours are gathered into $4-minute buckets on the shared origin; the columns and
Expand Down
25 changes: 25 additions & 0 deletions Darling/Darling.Tests/HourlyAttributionSpanTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,31 @@ public void Hourly_ShareDividesByTheServedSpan_NotTheRequestedWindow()
Assert.NotEqual(served.AttributedCpuRatio, requested.AttributedCpuRatio);
}

[Fact]
public void Hourly_OnTheHourAsOf_DividesByExactlyTheHoursBeforeIt()
{
// An on-the-hour as_of divides N hours of CPU by N hours, not N+1 (#4859): the read stops before the hour
// that begins at as_of, so that hour is not in the denominator. The ceiling sits above as_of, so it does
// not cut the span.
var start = Day.AddHours(10);
var asOf = Day.AddHours(14);
var (from, to, _) = DarlingMcpDataTools.HourlyAttributionSpan(true, start, asOf, Day.AddHours(10), Day.AddHours(18));

Assert.Equal(Day.AddHours(10), from);
Assert.Equal(Day.AddHours(14), to);

// 25% of 8 cores over exactly 4 hours = 28,800 CPU-seconds; 21,600 ranked seconds is 0.75.
var served = CpuAttribution.Compute(21600, from, to, 240, from, to, 25, 8);
Assert.Equal(28800, served.SqlCpuSecondsInWindow);
Assert.Equal(0.75, served.AttributedCpuRatio);

// Contrast: an as_of 20 minutes into the hour still counts the bucket holding it whole, so the span ends
// at the top of the next hour.
var (_, unalignedTo, _) = DarlingMcpDataTools.HourlyAttributionSpan(
true, start, Day.AddHours(14).AddMinutes(20), Day.AddHours(10), Day.AddHours(18));
Assert.Equal(Day.AddHours(15), unalignedTo);
}

[Fact]
public void Hourly_NullCeiling_SaysUnknown_NotThatNoSpanWasServed()
{
Expand Down
24 changes: 19 additions & 5 deletions Darling/Darling.Tests/HourlyWindowEdgesTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,30 @@ public void AlignedStart_HasNoLeadingSentence()
Assert.DoesNotContain("partial hour", note, StringComparison.Ordinal);
}

[Theory]
[InlineData(20)]
[InlineData(0)]
public void End_CountsTheWholeHourPastAsOf(int minute)
[Fact]
public void UnalignedEnd_CountsTheWholeHourPastAsOf()
{
var end = Aligned.AddHours(4).AddMinutes(minute);
var end = Aligned.AddHours(4).AddMinutes(20);
var note = HourlyWindowEdges.Note(Aligned, Aligned, end, Aligned.AddHours(9));
Assert.Contains("up to " + Aligned.AddHours(5).ToString("o"), note, StringComparison.Ordinal);
}

/// <summary>The hourly reads stop BEFORE the window end (<c>bucket &lt; end</c>), so an end exactly on the
/// hour no longer takes the hour that begins there: the served span ends at the window end and the note
/// claims nothing past it. (This case used to be pinned as "counts the whole hour", when the bound was
/// <c>bucket &lt;= end</c>.)</summary>
[Fact]
public void AlignedEnd_StopsAtTheEnd_AndCountsNothingPastAsOf()
{
var end = Aligned.AddHours(4);
var note = HourlyWindowEdges.Note(Aligned, Aligned, end, Aligned.AddHours(9));
Assert.DoesNotContain("included whole", note, StringComparison.Ordinal);
Assert.DoesNotContain("counted past", note, StringComparison.Ordinal);

var (_, servedEnd) = HourlyWindowEdges.ServedSpan(Aligned, Aligned, end, Aligned.AddHours(9));
Assert.Equal(end, servedEnd);
}

[Fact]
public void EndCutAtTheCeiling_SaysNothingAfterItWasRead()
{
Expand Down
5 changes: 4 additions & 1 deletion Darling/Darling.Tests/QueryStoreTrendRoutingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,10 @@ public void RollupTrendSql_ServesTheRollup_AndPartitionsAtTheBoundary(bool withD
/* The partition seam: rollup buckets strictly BELOW $4 (load-bearing against a refresh landing
between the probe and the read), raw points at or above it. */
Assert.Contains("bucket >= $2", sql, StringComparison.Ordinal);
Assert.Contains("bucket <= $3", sql, StringComparison.Ordinal);
/* The window end is exclusive too: a bucket is stamped at its START, so `bucket <= $3` would take the
whole hour that begins at an end falling on the hour. */
Assert.Contains("bucket < $3", sql, StringComparison.Ordinal);
Assert.DoesNotContain("bucket <= $3", sql, StringComparison.Ordinal);
Assert.Contains("bucket < $4", sql, StringComparison.Ordinal);
Assert.Contains("interval_start_time_utc >= $4", sql, StringComparison.Ordinal);

Expand Down
Loading
Loading