Repository navigation
A second PagerDuty recovery inside the alert cooldown now resolves the incident (#5469) - #5470
Merged
Merged
Conversation
…oldown resolves the incident (#5469)
… cooldown window (#5469) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ZGNj3cTaf9E9jYjdAa9vA
…gerDuty-routed replica scope (#5469) The cooldown history seed now skips a pair edge's stored send when the other edge sent after it, so a restart in the middle of an outage no longer throttles the second incident's first notice. The per-replica cooldown scope applies only when auto-resolve is on and PagerDuty is routed for the alert. The delivery reads the flag once. Stale comments are fixed, the tooltip says who shares the per-replica window, and new tests pin each gate. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ZGNj3cTaf9E9jYjdAa9vA
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ZGNj3cTaf9E9jYjdAa9vA
… in Lite, restart and tie pins, clearer tooltip (#5469) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ZGNj3cTaf9E9jYjdAa9vA
erikdarlingdata
marked this pull request as ready for review
October 7, 2026 15:36
This was referenced Oct 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #5469.
Why
With PagerDuty auto-resolve on, down, up, down, up inside
EmailCooldownMinutessent trigger, resolve, trigger, and then nothing. The first "Server Restored" stamped its own cooldown key. The delivered close only cleared the "Server Unreachable" key. So the second recovery was throttled and PagerDuty's second incident stayed open. The AG Replica pair had the same shape. This is unreleased code from #5368.What changes
WebhookAlertService: after a delivered PagerDuty send of a firing metric, the closing edge's cooldown key is cleared withClearMetric. The firing metrics are Server Unreachable and AG Replica Disconnected. The flag must be on. The condition matches the existing clear in the other direction (PagerDuty delivered without error, flag on).ClearMetricis sticky against history re-seeds, so a pre-clear history row cannot throttle the recovery again.AlertFamily.TryGetRecoveryOf: the reverse lookup ofRecoveryPairs.{{dedup_key}}: code unchanged. It stays on per-state keys with the flag on or off, because its send uses the defaultautoResolve = false. I searched tooltips, Lite and viewer XAML, Darling config docs, README and docs/. None of that text says it follows the flag. Two code comments did say so (theDerivePagerDutyDedupKeyremarks and the generic send's key comment). I corrected both. The CHANGELOG buffer entry for PagerDuty: de-duplicate edge-type alerts into one incident #5368 does not make the claim either.Test plan
Part 2: the AG cooldown per replica
What was wrong
With auto-resolve on, each AG replica is its own PagerDuty incident, because the dedup key carries the replica. The webhook cooldown key was still per server and metric. Take two replicas of one AG with all four edges inside the window. The first replica's alert stamped the shared key. So the second replica's down alert was throttled and its incident never opened. The first replica's recovery stamp likewise throttled the second replica's resolve, which was a lost resolve.
What changes
WebhookAlertService.PairedReplicaScopeis the one place the rule lives. Auto-resolve is on, the metric is either edge of the AG Replica Disconnected / Reconnected pair, and the alert carriesAgReplicaIdentity. The PagerDuty dedup key and the webhook cooldown key both read it. Anything else gets the same keys as before, byte for byte. That covers the flag off, the server pair, and an AG edge with no identity.IncidentCooldown.EvaluateAsyncandClearMetrictake an optionalscopethat joins the metric-level key only. The firing and the recovery of the AG pair are both scoped. So one replica's recovery and the clears from part 1 touch only that replica's entries. That also removes the "sibling is re-armed" side effect the old "Accepted" comment described.ClearMetricdoc now describe what the code does. TheAgReplicaIdentitydoc says the cooldown reads it.EmailSendCoreholds its ownIncidentCooldownwith its own key space and passes no scope. So email keeps one notice per server and metric inside the window.PerformanceMonitor.Notifications.Test plan (part 2)
SendPath_AutoResolveOn_TwoReplicasDownThenUpInsideTheCooldown_ResolvesBothIncidents, with and without a history store). Darling 1 failed (TheDeliverer_ResolvesBothReplicaIncidents_WhenTwoReplicasFlapInsideTheCooldown_WithTheFlagOn). The collections differed at index 1, the second replica's down. The two auto-resolve-off controls passed before and after.StatementFilterAlertTests.Apply_AFourMegabyteReport_FinishesInsideTheBudget) is a timing budget. It passed when run alone (14 of 14).IncidentCooldownTests.Scope_SplitsTheMetricLevelKey_ClearsAndSeedsPerScopefor the scope on its own.The changelog entry already covers the fix in one line.
Review fixes
ClearMetricdoc no longer calls the restart case accepted. It says what the seed does now.{{dedup_key}}token follows the flag. The link and PagerDuty follow it, the token does not. The token code is unchanged.pairedEdgedoc now says the seed is looser than the in-memory clear. After a restart the seed counts any delivered webhook row of the other edge, in Teams, Slack or the generic webhook. The in-memory clear needs a delivered PagerDuty post. So a restart after a failed PagerDuty close can let one extra notice through. It never swallows one, and PagerDuty folds it into the open incident. Querying PagerDuty rows alone changes both stores, so I left it.pairedEdgedoc now also says it is passed only when PagerDuty is routed for the alert, not just when auto-resolve is on.IncidentCooldownare fixed. One was the shape of a cleared key. The other was thatClearMetricis now also called for the recovery metric after a delivered firing.PagerDutyWebhookTests.SendPath_AutoResolveFlagChangesAfterTheCooldownRead_PostsWithTheValueReadFirstmakes the flag read true once and false after. The second send must still be a resolve under the pair's key.IncidentCooldownTests.PairedEdge_Seed_TreatsEqualTimesAsNotSupersededpins the tie rule. Both edges stored at the same instant still throttle.AlertDeliveryChannelTests.TheDeliverer_AfterARestart_SkipsTheSupersededSeed_OnlyWhenPagerDutyIsRoutedForTheAlertis Darling's restart pin. History holds down at t-3 and up at t-2, then a new service starts. With no PagerDuty key or a key routed to another metric, the second outage stays throttled (0 Teams posts). With PagerDuty routed it posts (1).DelivererForhelper. It takes an optional PagerDuty key, Teams URL and routes, and now an optional history store.Test plan (review fixes)
>=instead of>in the seed comparison: the tie test failed. The route gate onpairedEdgechanged to the raw flag: 2 of 3 Darling restart cases failed (no key, other-metric route).PagerDutyWebhookTestsandIncidentCooldownTests68 passed. Lite classes matching Alert, Webhook, Cooldown, Census, Pin and PagerDuty: 1077 passed, 0 failed. DarlingAlertDeliveryChannelTests59 passed. Darling classes matching Alert, Webhook, Cooldown, Census, Ratchet, Hygiene, Pin, EffectiveStartUtcTests and PagerDuty: 3301 total, 2 failed, 95 skipped. The two failures wereStatementFilterAlertTests.Engine_AFourMegabyteReport_ReachesTheDelivererFilteredNotWithheldWholeandStatementCollectionCensusTests.ABlockingRingBufferOfAboutFourMegabytes_IsJudgedWellUnderTheBudget. Both are timing budgets under load. They passed when run alone (59 of 59 with their classes).