Repository navigation
Fix Collector Cost Regression alert re-firing on an unrefreshed collector_cost row - #2708
Conversation
…ctor_cost row (#2707) DarlingSelfAlertEvaluator.ApplyCostRegressionsAsync gated re-firing on cooldown-elapsed alone, with no memory of which collect.collector_cost data point it last alerted on. If the hourly store-metrics tick's cadence ever outpaces the cooldown, or the flush itself lags a tick, re-asking GetCostRegressionsAsync hands back the identical latest_ms computed from the identical underlying rows - observed today as byte-identical repeat alerts on harvest-01/plan_correction and multi-24/ag_replica_states. Same shape as #2703/#2704 (Poison Wait). Added LatestMetricTime to CostRegression - the newest collect.collector_cost row actually folded into latest_ms - and require it to have advanced past what was last alerted on before re-firing, in addition to cooldown-elapsed. Verified the SQL change against a real local Postgres 17: latest_metric_time stays put with no new hourly row, advances only once one lands. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
| max(sql_ms) FILTER (WHERE day = latest_day) AS latest_ms, | ||
| avg(sql_ms) FILTER (WHERE day < latest_day) AS baseline_ms, | ||
| count(*) FILTER (WHERE day < latest_day) AS baseline_days, | ||
| max(latest_metric_time_in_day) FILTER (WHERE day = latest_day) AS latest_metric_time |
There was a problem hiding this comment.
Nit: the new latest_metric_time column is wider than the padding used to align the other three AS aliases, so this block no longer lines up (AS sits at a different column on line 153 than on 150-152). Not a functional issue, just breaks the visual-alignment convention this file otherwise follows for multi-line SELECT/aggregate lists.
|
Reviewed. This is a clean, well-scoped fix that mirrors the #2704 (Poison Wait) pattern correctly:
Left one minor inline nit (broken |
Realign the FILTER column so it lines up with the other three aggregates now that latest_metric_time is the widest expression. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Reviewed. This is a clean, well-scoped fix — no blocking issues found. Correctness
Lite/Darling parity
Security / performance
No further changes requested. |
Never deduping RootBackendId == 0 rows (the fix for the earlier collision finding) traded one bug for another: the SAME persisting vanished-root block, sampled every sweep, added a new list entry every cycle, so RollingCountAlertGate's watermark kept climbing and "Blocking Detected" would re-fire every cooldown for one ongoing incident - exactly the #1091/#2704/#2708 class this design is supposed to be immune to. Fixed: sentinel rows now dedupe by RootPid instead of never deduping, narrowing the risk to pid reuse inside one rolling 1-hour window rather than either merging unrelated incidents (the original bug) or guaranteed re-alerting on a persisting one (this regression). RootPid was already the identity BuildPgBlockingIncident folds into that case's DedupKey, so this makes the count and the fingerprint agree on what identifies a sentinel incident. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fixes #2707. Sibling defect to #2703, fixed for Poison Wait in #2704 — same category of bug, different code path (
DarlingSelfAlertEvaluator, not the sharedAlertEngine;collect.collector_costhourly aggregate, notwait_statsdeltas).Problem
ApplyCostRegressionsAsyncgated re-firing onCooldownElapsed(_lastCostRegressionAlert, key, now)alone. If the hourly store-metrics tick's actual cadence ever outpaces the cooldown, or the flush itself lags a tick, re-askingGetCostRegressionsAsynchands back the exact samelatest_mscomputed from the exact same underlyingcollect.collector_costrows — observed live today as byte-identical repeat "Collector Cost Regression" alerts an hour apart onupsilon-01/plan_correction(207,313 ms/day at both 09:07:15 and 10:03:58 UTC) andmulti-24/ag_replica_states(16,916 ms/day, same two timestamps).Fix
Added
LatestMetricTimetoCostRegression— the newestcollect.collector_costrow actually folded intolatest_ms, computed the same FILTER-aggregation waylatest_ms/baseline_msalready are.ApplyCostRegressionsAsyncnow requires that anchor to have advanced past what was last alerted on (tracked in a new_lastCostRegressionDataPointdictionary, mirroring_lastCostRegressionAlert's existing lifecycle including the resolve-cleanup), in addition to cooldown-elapsed, before re-firing. Same shape as #2704'sPoisonWaitDelta.CollectionTimegate.Verification
dotnet build(service + tests) clean, 0 errors.RegressionSqlstring (spliced out of the source file, not a retyped copy): with only an early hourly row landed,latest_metric_timereports that row's timestamp; after a second hourly row lands for the same day,latest_metric_timeadvances to it andlatest_msgrows accordingly. Confirms the freshness anchor tracks real data movement rather than being a coincidental proxy.CollectorCostRegression_DoesNotRefire_OnTheSameMetricTime_EvenAfterCooldownElapsestoDarlingSelfAlertTests.cs: fires once, cooldown elapses with the SAMELatestMetricTime→ no re-fire; a genuinely newLatestMetricTime(still regressed) → fires again. Couldn't runDarling.Testslocally (macOS lacks the WindowsDesktop runtime the net10.0-windows TFM needs) — hand-traced against the two-condition gate (hasFreshDataPoint && CooldownElapsed(...)), same limitation noted on Fix Poison Wait alert re-firing on an unrefreshed wait_stats row #2704 and Fix QUERY_HIGH_DOP firing on a stale lifetime max_dop #2706; CI'sDarling PostgreSQL tests/buildjobs are the arbiter.FiresOnEntry_SuppressedWithinCooldown_ResolvesWhenGone,DistinctCollectors_FireIndependently) needed no behavioral changes, just an updatedRegression()test-helper signature for the new required record field.🤖 Generated with Claude Code