Skip to content

Add the authored Blocking and Deadlocks alert-notebook templates (#4222) - #4395

Merged
erikdarlingdata merged 4 commits into
devfrom
fix/4222-authored-templates
Sep 26, 2026
Merged

erikdarlingdata merged 4 commits into
devfrom
fix/4222-authored-templates

Conversation

@erikdarlingdata

@erikdarlingdata erikdarlingdata commented Sep 26, 2026 •

Copy link
Copy Markdown
Owner

Refs #4222.

Why

The alert-notebook MVP (#4222) ships every metric through a mechanical read-cell conversion (#4366). This adds the two authored templates the spec calls for: Blocking (Blocking Detected / Blocking Wait Time) and Deadlocks (Deadlocks Detected) get a forensic, multi-cell layout instead of a flat read list, reusing the same panel shapes the blocking-rca and deadlock-postmortem seed notebooks already use.

What changes

  • AlertNotebookEndpoint.cs: adds AuthoredTemplate(metric), checked before the mechanical conversion. It returns an authored/blocking or authored/deadlocks entry for the three alert-engine metric names ("Blocking Detected", "Blocking Wait Time", "Deadlocks Detected"), each with its own version constant (BlockingTemplateVersion, DeadlocksTemplateVersion). Every other metric is unchanged — same mechanical/<metric> id, same cells.
    • Blocking: header, status, get_blocking (limit 20), a blocked_process_reports timeline (composed cell, annotations: ["deadlocks"]), three ranked breakdowns ported from blocking-rca (most-blocked objects, by database, lock modes — bar/bar/pie, topN 10/10/8), then get_active_queries blocking_only=true.
    • Deadlocks: header, status, get_deadlock_detail (limit 3), a deadlocks timeline (composed cell, annotations: ["blocked_process_reports"]), one ranked breakdown from deadlock-postmortem (deadlocks by database), then get_deadlock_trend over 24h.
    • Composed cells carry an absolute range equal to the computed window (window_end - 24h .. window_end, the existing mechanical anchor math); the matched incident's Database, when present, is bound as an eq filter on every composed cell EXCEPT the by-database breakdown (which must show every database, not just the incident's one).
    • Read cells carry server / as_of=window_end / an explicit hours+limit, same convention as the mechanical path, except get_deadlock_trend, which declares no limit param at all (a bucketed trend, not a row-capped list) — exempted by name rather than forced into an undeclared-param 400.
    • Every cell carries an explicit viz ("table" for reads, "line"/"bar"/"pie" for the composed cells).
    • Degrades honestly: with no matched incident/row, the same 8 (blocking) / 6 (deadlocks) cells still build and still validate — no incident just means no database filter is bound.
  • DarlingWebEndpoints.cs: ValidateNotebookDefinition gains case "header":/case "status": arms (with their own strict-key sets, s_headerCellKeys/s_statusCellKeys) so the endpoint's own lead-in cells — which it already emitted for every template, mechanical included — can be validated at all. Before this, only the mechanical path's live-HTTP round trip ever exercised those two cell shapes; the validator itself rejected them as an "unknown type".

Test plan

  • dotnet build Darling/Darling.Tests/Darling.Tests.csproj -p:EnableWindowsTargeting=true — 0 warnings / 0 errors.
  • dotnet build Lite.Tests/Lite.Tests.csproj -p:EnableWindowsTargeting=true — 0 warnings / 0 errors.
  • Real xUnit pins, run in-process on macOS (the Darling.Tests.dll in-process recipe: build Release, strip the Microsoft.WindowsDesktop.App framework entry from the runtimeconfig, run dotnet Darling.Tests.dll -class <name> — no rig needed, these are pure tests):
    • Darling.Tests.AlertNotebookAuthoredTemplateTests (new file, 23 tests): metric routing for all three alert-engine metric names plus a non-authored control metric; both templates pass ValidateNotebookDefinition against a fixed incident; the degrade case (no matched incident) still validates with the same cell count; every authored read cell names a BuildReadDispatch entry (a gap EveryMechanicalReadCell_NamesAKnownDispatchEntry couldn't see, since it only walks SectionsFor); every composed (panel) cell's measure/source/groupBy is checked through ComposeSpec.TryParsePanel — the SAME authority a saved dashboard/notebook panel is checked against — plus one dedicated pin (ComposeCatalogCheck_RedsOnARenamedMeasure) proving that check actually reds on a bogus measure name, not just trivially passing; budget (explicit limit on every read cell except get_deadlock_trend, no audit_config/analyze_*, every composed window ≤ 24h); binding (range == computed window, read as_of == window_end); explicit viz on every read/panel cell; the by-database breakdown never binds a database filter even with a matched incident. Result: 23/23 PASS, 0.12s.
    • Darling.Tests.AlertNotebookEndpointTests (pre-existing, unmodified): still 24/24 PASS — confirms the widened visibility (AuthoredTemplate/AuthoredTemplateEntry private → internal) and the validator's new header/status cases didn't disturb anything already pinned.
    • RED, proved against origin/dev (git worktree add --detach <tmp> origin/dev, copy in the new test file only, build): compile failure, 11× CS0117: 'AlertNotebookEndpoint' does not contain a definition for 'AuthoredTemplate' — AuthoredTemplate doesn't exist on dev at all, so the new pins cannot even compile there, let alone pass. Confirms the pins are real (they exercise code that doesn't exist pre-fix), not a change blind to the fix.
  • Full Windows suite — CI decides (net10.0-windows target; builds on macOS, cannot run there beyond the in-process recipe above, which this PR already used for every new/touched test class).

Validator fix, settled

wwwroot/js/pages/views.js:704's saveAsNotebookButton posts the in-memory definition to /api/views unmodified (api.createView({ name, description, definition })) — no strip of header/status cells anywhere in the call path (renderNotebookDoc → saveAsNotebookButton, views.js:517-706). Every alert-mode notebook, mechanical or authored, carries a header cell and a status cell (AlertNotebookEndpoint.HeaderCell/StatusCell) as its first two cells. So "Save as notebook" was broken on dev for every single alert before this PR: ValidateNotebookDefinition rejected any definition carrying those two cell types with "cell 0 has an unknown type 'header'", and /api/views' create path runs every definition through that same validator (DarlingWebEndpoints.cs:1632). The validator change in this PR (case "header":/case "status":, s_headerCellKeys/s_statusCellKeys) is therefore a real, necessary fix — kept, not reverted — and is a fix to #4366/#4368 (the mechanical template and its "Save as notebook" affordance), not just an enabler for this PR's own pins. AuthoredTemplate_BuiltAgainstAMatchedIncident_PassesValidateNotebookDefinition and EveryMechanicalReadCell_NamesAKnownDispatchEntry's sibling render path both exercise the validated shape end to end; the render side (renderAlertCell in views.js:611-660) already has explicit if (cell.type === "header")/"status" branches predating this PR, so no render-side change was needed — only the validator was the gap.

CHANGELOG entry

SECTION: Added

ENTRY:

  • Authored Blocking and Deadlocks alert-notebook templates ([Add the authored Blocking and Deadlocks alert-notebook templates (#4222) #4395]) - /api/alert-notebook now returns a forensic, multi-cell layout for Blocking Detected, Blocking Wait Time, and Deadlocks Detected instead of the flat mechanical read list every other alert type gets: a blocked-process-reports or deadlocks timeline with a cross-annotation, ranked breakdowns by object/database/lock-mode, and the existing header/status/drill-down reads, all bound to the alert's own window and database.

REF:
[#4395]: #4395

Adds authored/blocking and authored/deadlocks server-side templates to
AlertNotebookEndpoint, checked before the mechanical conversion, for the
three metric names the alert engine emits: Blocking Detected, Blocking Wait
Time, and Deadlocks Detected. Every other metric keeps the byte-identical
mechanical path.

Ports the blocking-rca and deadlock-postmortem panel specs from
notebook.js into server-side composed cells with an absolute range bound
to the mechanical window (window_end - 24h family lookback), plus a
database_name filter when the matched incident carries one. Read cells
carry server/as_of/hours/limit per the existing mechanical convention;
get_deadlock_trend is the one bucketed-trend read with no limit param, so
it is exempted rather than forcing an undeclared param onto it.

Extends DarlingWebEndpoints.ValidateNotebookDefinition to accept the
header/status lead-in cell types (previously unvalidatable, since only
the endpoint's own live-HTTP output exercised them) with their own
strict-key sets.
Adds Darling.Tests.AlertNotebookAuthoredTemplateTests.cs (23 tests,
in-process on macOS, 23/23 PASS): metric routing, ValidateNotebookDefinition
against both templates, degrade, dispatch-entry census for authored read
cells, a new compose-catalog check for composed cells' measures/breakdowns
(with its own red-on-a-renamed-measure pin), budget, binding, explicit viz,
and the by-database breakdown's no-filter rule.

Widens AuthoredTemplate/AuthoredTemplateEntry from private to internal so
the test project calls them directly via the existing InternalsVisibleTo,
no reflection.

RED confirmed against origin/dev: 11x CS0117 compile failure, AuthoredTemplate
does not exist there.
@erikdarlingdata
erikdarlingdata marked this pull request as ready for review September 26, 2026 11:13
@erikdarlingdata
erikdarlingdata merged commit 3428da4 into dev Sep 26, 2026
17 of 18 checks passed
@erikdarlingdata
erikdarlingdata deleted the fix/4222-authored-templates branch September 26, 2026 11:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant