You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Store Job Over Cadence is mis-tuned and mis-worded for the hourly refresh: its remedy changes the materialised window and its default sits one point from firing #3060
#2136's Store Job Over Cadence alert judges a background job's last_run_duration against a percentage of its schedule_interval. Two things about it are wrong for the heaviest hourly continuous-aggregate refresh, and both are operator-facing.
1. Its remedy text is actively wrong for this job
The alert tells the operator to extend the job's schedule_interval. For the hourly refresh family, HourlyRefreshScheduleInterval is also the aggregate's end_offset — so extending it does not just relax the deadline, it changes what window the refresh materialises. Following the instruction silently alters collection behaviour to quiet an alert.
2. Its default sits one percentage point from firing on a healthy run
Verified arithmetically against origin/dev:
fact
value
hourly cadence
3,600 s
RefreshPhaseSlots
4, so one slot is 900 s
the 25% default fires at
900 s — exactly one slot
recorded ceiling for this job (HeaviestHourlyRefreshObservedCeilingSeconds)
864 s
that ceiling as a share of cadence
24.0%
So at its recorded worst this job runs at 24.0% of cadence against a 25% default. One point of margin, on a figure that has been observed at 66% of its slot this evening and rises with retail load.
And #2136's clamp is justified on a number that does not describe this job. Its reasoning cites "the production worst runs ~7% of cadence" — 252 s. The job in question has a recorded ceiling 3.4x that. The clamp was chosen against a population this job is not in.
3. The coincidence that makes it look fine is fragile in both directions
The 25% default landing exactly on one slot is an accident of two independent decisions — nobody tied StoreJobOverCadence's knob to RefreshPhaseStepMinutes. The knob is an operator setting clamped [5,100] with no reference to the slot constant, so:
at the clamp's top 100 it fires at 3,600 s — four slots past;
and if RefreshPhaseStepMinutes were changed to 10, the slot becomes 600 s while a 25% knob still fires at 900 s — past invalidation rather than before it.
In every one of those cases the alert fires after the condition it would need to warn about.
What this is not
Not a request for a second alert. PR #3055 (#3044) deliberately took the quiet form for exactly this reason: the alert seam is occupied, and a second signal at the slot width would double-fire on the same job, reading and hour. This issue is about the occupant being mis-tuned and mis-worded, not about adding another.
Fix, and the part worth guarding
The remedy text is the user-visible half and the cheaper one: it should not tell an operator to extend an interval that doubles as an end_offset, or should name the exception.
The coupling is the durable half. Either tie the knob's validation to RefreshPhaseSlotSeconds so a setting that fires past invalidation is rejected, or assert the relationship in a test so the two constants cannot drift apart silently. Right now nothing connects them, and the thing that makes the default look correct is a coincidence.
Also worth correcting while there: the DarlingAlertSettings comment justifying the clamp on "~7% of cadence" is stale against this job's measured behaviour.
Provenance
Found by the #3044 lane while establishing that a new alert was not the right shape — it checked what already occupies the seam rather than assuming it was empty. The arithmetic above is recomputed here from RefreshPhaseSlots, RefreshPhaseStepMinutes and HeaviestHourlyRefreshObservedCeilingSeconds on origin/dev.
Closed by #3066, merged to dev at fd49edea874d9e37cdfe4de6a25b270b49d303b5. Closing by hand because a closing keyword does not fire against dev.
Both halves fixed, and the coupling half turned a coincidence into an identity.RefreshSlotPercentOfHourlyCadence = 100 / RefreshPhaseSlots — so #3055 had to argue at length that the 25% default landing on exactly one slot was "a coincidence of two independent decisions with nothing tying the knob to RefreshPhaseStepMinutes", and after this, moving the grid step moves the knob default with it. Pinned cross-multiplied in both units, with the seconds side checked against RefreshPhaseSlotSeconds — the unit the alert actually compares in — and slotSeconds * slots == HourlyRefreshScheduleSpan is not a tautology: a 7-minute step gives 3,360 s of a 3,600 s hour.
The remedy text names the exception rather than softening the sentence, branching on ScheduleIntervalDoublesAsEndOffset, with the blanket rewording itself red-proofed so it cannot creep back. And a third consequence neither the issue nor I had: on the hourly tier, widening the interval also shortens QueryStoreBackfill.RollupStoreHorizon, derived as HourlyRefreshStartSpan minus that interval. So the old advice was wrong three ways, and the third is the one an operator could not possibly anticipate.
The clamp's justification is replaced with measured data and the number survives. Both stores, read-only: us-east-1 48,030 readings with p50/p90/p99/p99.9 at 0.003/0.056/6.15/69.55% and a max of 369.47%; us-east-2 64,646 readings, max 62.58%. "~7% of cadence" was wrong by 53x and described neither store. Floor stays at 5 — ~90x the p90, ~1,700x the median, 1.11% of readings reach it, 73 of 48,030 sit in [5,7) — so the reasoning changed and the value did not, which is the honest outcome and is stated that way.
Note us-east-2 is unrepresentative for this measurement: post-#2296 its CAGG family maxes at 6%, so measuring only there would have confirmed the stale figure with no sign anything went wrong.
The review bot found a fourth copy of the literal, and it was the consequential one.AlertSettingsRow's initializer — BuildAlertRowFromControls assigns the textbox only when it parses inside the clamp, so out-of-range input writes the initializer straight to the store. The three already derived from the constant were the visible copies; this was the one that persists.
Not verified: no live store ran the alert path. The one pin with no local counterpart is confirmed executed by counted delta — Darling.Tests 7774 → 7775 with skips flat at 317 — rather than by a silent grep, and the lane states outright that its test names appearing nowhere in the log proves nothing on its own.
#3069 carries the related finding: HeaviestHourlyRefreshObservedCeilingSeconds rests on one sample of a twelve-hour-old regime.
Activity
Fixes #3060 (#3066)
erikdarlingdata commentedon Sep 6, 2026
Claude posting for Erik Darling
Closed by #3066, merged to
devatfd49edea874d9e37cdfe4de6a25b270b49d303b5. Closing by hand because a closing keyword does not fire againstdev.Both halves fixed, and the coupling half turned a coincidence into an identity.
RefreshSlotPercentOfHourlyCadence = 100 / RefreshPhaseSlots— so #3055 had to argue at length that the 25% default landing on exactly one slot was "a coincidence of two independent decisions with nothing tying the knob toRefreshPhaseStepMinutes", and after this, moving the grid step moves the knob default with it. Pinned cross-multiplied in both units, with the seconds side checked againstRefreshPhaseSlotSeconds— the unit the alert actually compares in — andslotSeconds * slots == HourlyRefreshScheduleSpanis not a tautology: a 7-minute step gives 3,360 s of a 3,600 s hour.The remedy text names the exception rather than softening the sentence, branching on
ScheduleIntervalDoublesAsEndOffset, with the blanket rewording itself red-proofed so it cannot creep back. And a third consequence neither the issue nor I had: on the hourly tier, widening the interval also shortensQueryStoreBackfill.RollupStoreHorizon, derived asHourlyRefreshStartSpanminus that interval. So the old advice was wrong three ways, and the third is the one an operator could not possibly anticipate.The clamp's justification is replaced with measured data and the number survives. Both stores, read-only: us-east-1 48,030 readings with p50/p90/p99/p99.9 at 0.003/0.056/6.15/69.55% and a max of 369.47%; us-east-2 64,646 readings, max 62.58%. "~7% of cadence" was wrong by 53x and described neither store. Floor stays at 5 — ~90x the p90, ~1,700x the median, 1.11% of readings reach it, 73 of 48,030 sit in [5,7) — so the reasoning changed and the value did not, which is the honest outcome and is stated that way.
Note us-east-2 is unrepresentative for this measurement: post-#2296 its CAGG family maxes at 6%, so measuring only there would have confirmed the stale figure with no sign anything went wrong.
The review bot found a fourth copy of the literal, and it was the consequential one.
AlertSettingsRow's initializer —BuildAlertRowFromControlsassigns the textbox only when it parses inside the clamp, so out-of-range input writes the initializer straight to the store. The three already derived from the constant were the visible copies; this was the one that persists.Not verified: no live store ran the alert path. The one pin with no local counterpart is confirmed executed by counted delta — Darling.Tests 7774 → 7775 with skips flat at 317 — rather than by a silent grep, and the lane states outright that its test names appearing nowhere in the log proves nothing on its own.
#3069 carries the related finding:
HeaviestHourlyRefreshObservedCeilingSecondsrests on one sample of a twelve-hour-old regime.Page on a same-statement pileup while it is happening, from the colle…