Repository navigation
Custom alert rules: an alert no channel delivered is sent again while its condition holds (#4795) - #4827
Merged
Conversation
… how a failed send is retried (#4795) The decision half of one rule evaluation moves into an internal method that takes the loaded state and one value and returns the state to save, so a test can drive it pass by pass without a store. Behavior is unchanged in this commit. The new tests pin the retry the next commit adds: when every channel fails on the rising edge the incident is not recorded as delivered and the fire is sent again after 1 minute, then 2, up to 15; no resolve goes out for an alert that never arrived; a restart sends an undelivered fire on its first pass; a failed severity change keeps the old band and is sent again when due. Seven of the ten fail here; the partial-failure, muted-fire and delivered-then-resolved controls pass.
… its condition holds (#4795) A rule has no cooldown, so when every channel failed on the rising edge the evaluator still saved the incident as delivered and stayed quiet until the condition cleared and came back. A resolve also went out for an alert that never arrived. The fire is now sent through DeliverAndReportAsync and its answer is kept. When every channel failed, the incident stays open with no delivered severity, and the next passes that still see the condition send it again after 1 minute, then 2, 4 and so on, up to 15 minutes. A restart leaves nothing recorded, so the first pass after it sends. A severity change that no channel took keeps the old band and is sent again when due. A resolve goes out only for an incident that was delivered, and ends the failure streak. A partial failure, a muted or throttled fire and a null report still count as delivered.
…ource-name guard allows (#4795)
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.
Part of #4795 (custom alert rules; the PostgreSQL alerts follow separately).
Why
A custom alert rule has no cooldown. It fires once, on the rising edge, and the evaluator saved the incident as open and delivered whether or not the send worked. When every channel failed (a webhook answering HTTP 429 or 5xx, an unreachable mail server) the alert was lost until the condition cleared and came back. The resolve still went out, because it only checks that a severity was recorded, so an operator could get a "resolved" for an alert that never arrived. A severity change on an open incident used the same send and had the same gap.
What changes
All in
Darling/PerformanceMonitor.Darling.Service/CustomAlertEvaluator.cs. No schema change, andFailedSendBackoff,AlertEngine, the persistence gate,ClassifySeverityChange, the saved-state record and the resolve message are untouched.DeliverFireAsyncsends throughDeliverAndReportAsyncand returns theAlertDelivery, where it used to send throughDeliverAsyncand return nothing.FailedSendBackoff(family = the rule'sCustom:<id>metric name, key = the server id as invariant text) and an in-memory table of when each (rule, server) may be sent again.FailedSendRetryCapis 15 minutes: a rule has no cooldown, so it is the cap on the 1, 2, 4 ... minute wait.FiredSeveritystays null (the incident is open but not delivered), the failure is counted, the next attempt is set 1 minute out (then 2, 4, up to 15), and one Information line is logged with the rule id, server, failure count and wait. Otherwise the severity is recorded as before and the failure streak ends.FiredSeverityat the old band, so the next pass still sees the change, and it is sent again only when due. On success the new band is recorded and the streak ends.FiredSeverityis set, so an incident whose send failed gets no resolve. It also ends the failure streak and drops the wait, so a later incident starts again at 1 minute.ApplyValueAsync, so it can be driven pass by pass without a database.EvaluateRuleForServerAsyncstill loads the state, reads the value, calls it and saves what it returns. That move is its own commit and changes no behavior.Test plan
CustomAlertFailedSendRetryTests(10 tests, no database): every channel fails on the rising edge (no severity saved, sent again after 1 minute, then 2); a delivered retry records the severity and the next pass sends nothing; the wait doubles 1, 2, 4, 8 and stops at 15; no resolve when the condition clears after a failed send, and the next rising edge starts again at 1 minute; a resolve does go out for an incident whose retry was delivered; a restart (a new evaluator over the saved firing state with no delivered severity) sends on its first pass, at the current severity; a failed severity change keeps the old band and is sent again when due; controls for a partial failure and a muted fire (severity recorded at once, never sent again).Darling.Tests: 0 warnings, 0 errors.AlertMasterSwitchSurfaceTests,AlertReadFailureSurfaceTests, theCustomAlert*classes that run without a database, and the new class): 112 passed, 0 failed.Darling.Testssuite withoutDARLING_TEST_PG, after mergingorigin/dev(already up to date): Total 17587, Failed 0, Skipped 1145 (live classes that need a database), Not Run 1. The first full run had one failure,FleetIdentifierScrubTests.NoTrackedSourceFileNamesAServerByShortNameAndOrdinal, because the new tests named a serversql-retry-01; they now usealpha-01, and the second full run is clean.CustomAlertSeverityEscalationLiveTests) were not run: no PostgreSQL was available. They go throughEvaluateRuleForServerAsync, which now callsApplyValueAsync.Worth a second look
(long RuleId, int ServerId), becauseCustomAlertRule.Idis along.CHANGELOG
SECTION: Fixed
ENTRY:
REF:
[Custom alert rules: an alert no channel delivered is sent again while its condition holds (#4795) #4827]: Custom alert rules: an alert no channel delivered is sent again while its condition holds (#4795) #4827