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
4 changes: 3 additions & 1 deletion Darling/Darling.Tests/DarlingRetentionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,9 @@ referenced by all three positions. */
"DELETE FROM collection_log WHERE collection_time < $1 AND collection_time >= (SELECT min(collection_time) FROM collection_log WHERE collection_time < $1) AND collection_time < (SELECT min(collection_time) FROM collection_log WHERE collection_time < $1) + INTERVAL '1 days'",
DarlingRetention.TimeSlicedDeleteSql("collection_log", "collection_time"));

/* The slice width IS the hypertable chunk width — the fallback's unit of work stays one chunk. */
/* The slice width matches ChunkIntervalDays, the ladder's CEILING since #4211 (RawChunkIntervalPlanner
can narrow a raw table's actual chunk_time_interval below a day) — the fallback's unit of work stays
bounded to at most one day's worth even where it is no longer exactly one chunk. */
Assert.Equal(1, TimescaleSupport.ChunkIntervalDays);
}

Expand Down
358 changes: 358 additions & 0 deletions Darling/Darling.Tests/RawChunkIntervalPlannerTests.cs

Large diffs are not rendered by default.

22 changes: 20 additions & 2 deletions Darling/Darling.Tests/TimescaleSupportTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,10 @@ so a moved grid is loud there rather than silently agreed with here. */
+ waitStatsPhase.ToString(CultureInfo.InvariantCulture) + " minutes')",
TimescaleSupport.AddCompressionPolicySql(byName["wait_stats"]));

/* 1 day matches the 1-day chunk interval so chunks become compressible quickly, keeping the
managed store compact (#1458). */
/* 1 day, fixed on every table since #4211 (decision 1: A no longer tracks a narrowed raw table's
chunk_time_interval — RawChunkIntervalPlanner only ever moves I). Originally chosen to match the
1-day chunk interval so chunks become compressible quickly, keeping the managed store compact
(#1458). */
Assert.Equal(1, TimescaleSupport.CompressAfterDays);

/* #1778: schedule_interval is passed EXPLICITLY on every table. Omitting it does not mean "some
Expand Down Expand Up @@ -156,6 +158,22 @@ managed store compact (#1458). */
TimescaleSupport.AddCompressionPolicySql(TimescaleSupport.CollectionLogTable));
}

/* ---------------- raw compress_after floor (#4211) ---------------- */

[Fact]
public void CompressAfterDays_IsAtLeastTheHourlyRefreshStartOffset()
{
/* #4211 ruling decision 1: a raw table's compress_after (A) stays fixed regardless of how far
RawChunkIntervalPlanner narrows that table's own chunk_time_interval (I). The reason is this
invariant — A below HourlyRefreshStartOffset would let a chunk compress before the hourly
continuous-aggregate refresh has finished reading it uncompressed, removing the gap the product's
lock safety relies on. Pinned as a span comparison, not a re-parse of the "1 day" strings, so it
breaks loudly if either side ever moves without the other. */
Assert.True(
TimeSpan.FromDays(TimescaleSupport.CompressAfterDays) >= TimescaleSupport.HourlyRefreshStartSpan,
$"CompressAfterDays ({TimescaleSupport.CompressAfterDays} days) must stay at least HourlyRefreshStartOffset ({TimescaleSupport.HourlyRefreshStartSpan}).");
}

/* ---------------- compression-job self-heal (#1581) — pure predicate ---------------- */

private static readonly DateTime s_now = new(2026, 7, 19, 12, 0, 0, DateTimeKind.Utc);
Expand Down
19 changes: 12 additions & 7 deletions Darling/PerformanceMonitor.Darling.Storage/EventWindowFloor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,25 @@ namespace PerformanceMonitor.Darling.Storage;
/// clock, which stamps the event, and the collector host's, which stamps the collection. Measured on the same
/// store: blocked-process reports land 4.8 s to 62 s after their <c>event_time</c>, a DMV blocking snapshot's
/// <c>event_time</c> IS its <c>collection_time</c>, and the worst deadlock sat 27 ms AFTER its collection.
/// <see cref="SkewAllowance"/> is one whole chunk — a day — so a monitored clock would have to run a day
/// ahead of the collector's before a row fell out, and the floor costs at most one chunk more than the window
/// itself while still excluding every chunk older than that.</para>
/// <see cref="SkewAllowance"/> is one day — a day — so a monitored clock would have to run a day ahead of the
/// collector's before a row fell out, and the floor costs at most one day more than the window itself while
/// still excluding every chunk older than that.</para>
///
/// <para><b>Deliberately no upper bound on <c>collection_time</c>.</b> An event collected late — the catch-up
/// after an outage — is still an event inside the window, and it is exactly the row an upper bound would
/// lose.</para>
/// </summary>
public static class EventWindowFloor
{
/// <summary>How far before an event's own timestamp its <c>collection_time</c> may fall and still be read:
/// the store's chunk width, derived from it so "at most one chunk beyond the window" stays true if the
/// width ever moves.</summary>
public static readonly TimeSpan SkewAllowance = TimeSpan.FromDays(TimescaleSupport.ChunkIntervalDays);
/// <summary>How far before an event's own timestamp its <c>collection_time</c> may fall and still be
/// read. Its OWN 1-day constant (#4211) — a clock-skew tolerance, not the store's chunk width: since
/// #4211, a raw hypertable's <c>chunk_time_interval</c> can be narrower than a day for the heaviest
/// tables (<see cref="RawChunkIntervalPlanner"/>), while <see cref="TimescaleSupport.ChunkIntervalDays"/>
/// stays only the ladder's CEILING. <c>blocked_process_reports</c>, <c>dmv_blocking_snapshots</c> and
/// <c>deadlocks</c> — the three tables this floor exists for — are not among the heavy query tables the
/// planner narrows, so they stay at the 24-hour ceiling regardless, and a day of allowance still costs at
/// most one of their chunks.</summary>
public static readonly TimeSpan SkewAllowance = TimeSpan.FromDays(1);

/// <summary>The <c>collection_time</c> floor for an event window starting at
/// <paramref name="eventWindowStartUtc"/>, as naive UTC — the store's timestamp convention, so the bind
Expand Down
Loading
Loading