What
No alert in the catalog requires a condition to persist before it fires. A single sample over the bar fires, and the next sample under it resolves. On a gauge like CPU that makes momentary spikes indistinguishable from sustained saturation, and the delivered pair is unactionable by the time anyone reads it.
This is filed as the shared surface for a design question, not just as a defect — see the last two sections.
Measured, on both engines
SQL Server, one target, believable values and a stable baseline:
| fired |
resolved |
elapsed |
| High CPU, 81% |
"back to 45%" |
55 seconds |
| High CPU, 90% |
"back to 45%" |
87 seconds |
It resolved to the same 45% both times, an hour apart. Those were two brief excursions above a steady baseline — correctly detected, and not incidents.
PostgreSQL, one target, five fire/resolve pairs in under two hours; fleet-wide, 39 of the most recent 50 alert rows are High CPU / CPU Resolved pairs, and those 50 rows cover only about 11 hours because they hit the read's row cap.
Why this is NOT the re-fire class already closed seven times
Worth stating precisely, because the titles look identical and this would otherwise read as a duplicate of #1091, #2101, #2703/#2704, #2707/#2708, #1154 or #981.
That cohort is the same unrefreshed row being reported twice — the collector had not written a new sample, the watermark did not advance, and the alert re-delivered a stale observation. Every fix there was to stop trusting a row that had not moved.
This is the opposite cause. Every fire and every resolve here is a genuine threshold crossing on a fresh sample. The data is new, the watermark advanced correctly, the state machine is behaving exactly as designed, and the alert is truthful about the instant it describes. The defect is that no one ever asked the condition to last.
So the existing cooldown and watermark machinery cannot address it: a cooldown suppresses repeats of an alert that is still true, and this alert stops being true before the cooldown matters.
What already exists, so this does not get re-scoped
Delivery is not a gap. PerformanceMonitor.Notifications/WebhookAlertService.cs implements Slack (Block Kit), Teams (MessageCard), PagerDuty (Events API v2) and a fully operator-templated generic webhook, driven by config.config_notification. #2712 documented where a headless box configures it, #2721 promoted involved-objects and database to top-level Datadog-parity tags, and #2729 added a linked, computed-on-read triage artifact — gated on web.publicBaseUrl, which is worth knowing because an unset base URL silently costs every alert its triage link.
Per-subject cooldown keying, mute rules, and restart-surviving watermarks (#2716 for all five PostgreSQL alerts) are all in place. The machinery is mature. Persistence is the one primitive it has never had.
The design question, and why it is not obviously ours to answer
"How long must this condition hold before it counts" is exactly the kind of primitive a user-authored alert system would want to own, alongside threshold, scope and cooldown. If that system is going to expose duration, then hardcoding a separate debounce into the built-in evaluators now produces two mechanisms that drift, and the built-ins should consume the shared one instead.
There is a second, larger version of the same question. These PostgreSQL signals are collected today with nothing alerting on them:
- autovacuum health — the leading indicator whose end state is the wraparound alert that already exists, so today we alert on the cliff and not the slope
- replication lag, as distinct from replication slot risk, which is alerted
- connection / session saturation
- table bloat
- storage growth
Five new built-in evaluators is one answer. Good starter templates over an authoring surface is another, and it generalises to signals nobody has thought of yet. Which one is right depends entirely on what the authoring surface is going to be.
For the custom-alert design work
Three things would let the built-in catalog be fixed rather than guessed at, in priority order:
- Will duration/persistence be a user-authored primitive? If yes, the built-ins should adopt it rather than growing their own, and this issue becomes "port the built-ins onto it."
- Built-in versus authored for the five signals above — should those ship as evaluators, as templates, or not at all?
- Does authored alerting reuse
IAlertDeliverer / config_notification and inherit the same cooldown and mute semantics? If so, persistence has to live low enough in the stack for both to share it, which constrains where it can be implemented.
Nothing in the built-in catalog is being changed in that seam until those are answered. The one PostgreSQL alerting defect being worked in parallel, #3281, is deliberately outside it: that one is about the CPU metric's denominator being percent-of-allocated capacity on Aurora Serverless v2, which is a collection-and-banding problem rather than an alert-semantics one.
Not in scope here
Raising thresholds. An 81% excursion above a 45% baseline is real, and a higher bar would only move the flap point. The missing concept is time, not altitude.
What
No alert in the catalog requires a condition to persist before it fires. A single sample over the bar fires, and the next sample under it resolves. On a gauge like CPU that makes momentary spikes indistinguishable from sustained saturation, and the delivered pair is unactionable by the time anyone reads it.
This is filed as the shared surface for a design question, not just as a defect — see the last two sections.
Measured, on both engines
SQL Server, one target, believable values and a stable baseline:
It resolved to the same 45% both times, an hour apart. Those were two brief excursions above a steady baseline — correctly detected, and not incidents.
PostgreSQL, one target, five fire/resolve pairs in under two hours; fleet-wide, 39 of the most recent 50 alert rows are High CPU / CPU Resolved pairs, and those 50 rows cover only about 11 hours because they hit the read's row cap.
Why this is NOT the re-fire class already closed seven times
Worth stating precisely, because the titles look identical and this would otherwise read as a duplicate of #1091, #2101, #2703/#2704, #2707/#2708, #1154 or #981.
That cohort is the same unrefreshed row being reported twice — the collector had not written a new sample, the watermark did not advance, and the alert re-delivered a stale observation. Every fix there was to stop trusting a row that had not moved.
This is the opposite cause. Every fire and every resolve here is a genuine threshold crossing on a fresh sample. The data is new, the watermark advanced correctly, the state machine is behaving exactly as designed, and the alert is truthful about the instant it describes. The defect is that no one ever asked the condition to last.
So the existing cooldown and watermark machinery cannot address it: a cooldown suppresses repeats of an alert that is still true, and this alert stops being true before the cooldown matters.
What already exists, so this does not get re-scoped
Delivery is not a gap.
PerformanceMonitor.Notifications/WebhookAlertService.csimplements Slack (Block Kit), Teams (MessageCard), PagerDuty (Events API v2) and a fully operator-templated generic webhook, driven byconfig.config_notification. #2712 documented where a headless box configures it, #2721 promoted involved-objects and database to top-level Datadog-parity tags, and #2729 added a linked, computed-on-read triage artifact — gated onweb.publicBaseUrl, which is worth knowing because an unset base URL silently costs every alert its triage link.Per-subject cooldown keying, mute rules, and restart-surviving watermarks (#2716 for all five PostgreSQL alerts) are all in place. The machinery is mature. Persistence is the one primitive it has never had.
The design question, and why it is not obviously ours to answer
"How long must this condition hold before it counts" is exactly the kind of primitive a user-authored alert system would want to own, alongside threshold, scope and cooldown. If that system is going to expose duration, then hardcoding a separate debounce into the built-in evaluators now produces two mechanisms that drift, and the built-ins should consume the shared one instead.
There is a second, larger version of the same question. These PostgreSQL signals are collected today with nothing alerting on them:
Five new built-in evaluators is one answer. Good starter templates over an authoring surface is another, and it generalises to signals nobody has thought of yet. Which one is right depends entirely on what the authoring surface is going to be.
For the custom-alert design work
Three things would let the built-in catalog be fixed rather than guessed at, in priority order:
IAlertDeliverer/config_notificationand inherit the same cooldown and mute semantics? If so, persistence has to live low enough in the stack for both to share it, which constrains where it can be implemented.Nothing in the built-in catalog is being changed in that seam until those are answered. The one PostgreSQL alerting defect being worked in parallel, #3281, is deliberately outside it: that one is about the CPU metric's denominator being percent-of-allocated capacity on Aurora Serverless v2, which is a collection-and-banding problem rather than an alert-semantics one.
Not in scope here
Raising thresholds. An 81% excursion above a 45% baseline is real, and a higher bar would only move the flap point. The missing concept is time, not altitude.