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
15 changes: 9 additions & 6 deletions Darling/Darling.Tests/AlertEngineTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,8 @@ into the process-wide AlertReadFailureCounter.Shared. */
public AlertReadFailureCounter? ReadFailures { get; set; }
/// <summary>#5715 part B: the alert-history reader that seeds the escalation tier; null (the default) leaves the engine memory-only.</summary>
public Func<EscalationSeedQuery, CancellationToken, Task<AlertSeverityLevel?>>? EscalationSeedReader { get; set; }
/// <summary>#5910: the threshold overrides; null (the default) is the engine before #5910, so every pin above is unchanged.</summary>
public IAlertThresholdOverrides? Overrides { get; set; }

/* #3497: the Agent-job resolver seam, null by default so every pre-existing pin builds the
pre-#3497 engine. Tests set AgentJobNames (the answer map), flip AgentJobResolverThrows for
Expand Down Expand Up @@ -489,7 +491,8 @@ into the process-wide AlertReadFailureCounter.Shared. */
logger: null,
utcNow: () => Now,
readFailures: ReadFailures,
escalationSeedReader: EscalationSeedReader);
escalationSeedReader: EscalationSeedReader,
overrides: Overrides);

/* #3282: every snapshot gets a DISTINCT, increasing CPU sample instant unless the test says
otherwise, because that is the realistic case — the sweep sees a new ring-buffer sample each
Expand Down Expand Up @@ -3885,7 +3888,7 @@ public async Task LowDisk_GradesCriticallyLowBreaches_AndAStandingBreachDoesNotR

/* ---------------- database file growth (#2349; #3539 A8c: the rise is MB per HOUR) ---------------- */

private static DatabaseFileGrowthInfo GrowingFile(double growthMb, double windowMinutes) => new()
internal static DatabaseFileGrowthInfo GrowingFile(double growthMb, double windowMinutes) => new()
{
DatabaseName = "tempdb", FileName = "tempdev", PhysicalName = @"D:\data\tempdev.mdf", FileTypeDesc = "ROWS",
TotalSizeMb = 90_000, GrowthMb = growthMb, GrowthWindowMinutes = windowMinutes,
Expand Down Expand Up @@ -3967,12 +3970,12 @@ public async Task FileGrowth_ARateUnderTheBar_IsSilentOnAnyLookback(int lookback
/// <summary>Two consecutive hourly collections of <c>database_size_stats</c>, so the pins below replay the
/// shape the issue describes rather than an invented one: a rise observed at the top of the hour, twelve
/// five-minute cooldowns of re-reads against the same two rows, the next collection an hour later.</summary>
private static readonly DateTime HourlyCollection0 = new(2026, 9, 18, 6, 0, 0, DateTimeKind.Utc);
private static readonly DateTime HourlyCollection1 = HourlyCollection0.AddHours(1);
internal static readonly DateTime HourlyCollection0 = new(2026, 9, 18, 6, 0, 0, DateTimeKind.Utc);
internal static readonly DateTime HourlyCollection1 = HourlyCollection0.AddHours(1);

/// <summary>A rise-only file (2% of a 4 TB volume — the level gate cannot see it) stamped with the collection
/// that produced it. 20 GB in the 60-minute window is twice the default 10,240 MB/hr bar.</summary>
private static DatabaseFileGrowthInfo RiseOnlyFile(DateTime? observedAt, double growthMb = 20_480, string fileName = "tempdev")
internal static DatabaseFileGrowthInfo RiseOnlyFile(DateTime? observedAt, double growthMb = 20_480, string fileName = "tempdev")
{
var f = GrowingFile(growthMb, windowMinutes: 60);
f.FileName = fileName;
Expand All @@ -3982,7 +3985,7 @@ private static DatabaseFileGrowthInfo RiseOnlyFile(DateTime? observedAt, double

/// <summary>A level-only file: 80% of a small volume and not growing at all. The standing-level shape that
/// re-fires on the cooldown by design.</summary>
private static DatabaseFileGrowthInfo LevelOnlyFile(DateTime? observedAt) => new()
internal static DatabaseFileGrowthInfo LevelOnlyFile(DateTime? observedAt) => new()
{
DatabaseName = "Sales", FileName = "Sales_log", PhysicalName = @"L:\log\Sales_log.ldf", FileTypeDesc = "LOG",
TotalSizeMb = 400_000, GrowthMb = 0, GrowthWindowMinutes = 60,
Expand Down
10 changes: 7 additions & 3 deletions Darling/Darling.Tests/AlertReadFailureSurfaceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -915,7 +915,9 @@ real failure rather than a matcher that never matches anything. */
/// </summary>
private static readonly (string Path, int Counted, int Exempt)[] s_wholeFileScopes =
{
(Path.Combine("PerformanceMonitor.Alerting", "AlertEngine.cs"), 18, 6),
(Path.Combine("PerformanceMonitor.Alerting", "AlertEngine.cs"), 18, 7),
/* 7th AlertEngine exempt since #5910: the threshold-override fallback in Threshold(), an in-memory lookup and
not a read; the check it serves still reads, and counts, exactly as before. */
/* 14th exempt since #4215: EvaluateStoreSettingsAsync's wrapper catch — the store-settings
self-alert's report is a parameter, exactly like its StaleMute/WebTls siblings. 15th exempt
since #4299: EvaluateRawPurgeOverHorizonAsync's wrapper catch — the raw-purge-over-horizon
Expand Down Expand Up @@ -1087,6 +1089,7 @@ holds the standing alert on a failed read. */
["delivery stamp could not be written"] = "a write (#3580): the daily document was already delivered and process memory already gates it; the dropped stamp costs one re-announcement at the next restart and never a delivery - the stamp READ beside it is the read, and it is counted",
["Store statement history is still failing"] = "the Debug repeat of a telemetry write sweep (#5097); the first failure warns, no alert is judged on its result",
["Collection log hourly rollup failed"] = "a maintenance write (#5810): the pass upserts the hourly rollup and no alert is judged on its result; a failed pass is redone by the next, and get_collection_health reads the hours after the newest rolled hour from the raw log",
["The threshold override for"] = "an in-memory override lookup (#5910), not a read: the global value applies and the check's own read is counted as before",
["Read-latency flush failed"] ="the read-latency histogram flush writes instrumentation; losing an hour's rows can't hide an alert condition",
};

Expand Down Expand Up @@ -1280,8 +1283,9 @@ WebTlsCertificateState report the web host publishes - there is no store read to
wrapper catch, handed its report as a parameter. 39th since #5810: the collection log hourly rollup's catch, a
maintenance write no alert is judged on, redone by the next pass. 40th since #5867: the Compose Rollup Hours Stuck
self-alert's wrapper catch, handed its failure record as a parameter. 41st since #5867: the worker's read of that
record, which holds the standing alert on a failure. */
Assert.Equal(41, totalExempt);
record, which holds the standing alert on a failure. 42nd since #5910: the engine's threshold-override
fallback, an in-memory lookup and not a read. */
Assert.Equal(42, totalExempt);

/* Every exemption in the table is actually used. An exemption for a message that no longer exists
is a hole this pin would otherwise keep open indefinitely — the shape that lets a real new catch
Expand Down
Loading
Loading