Skip to content

Serve the Current Waits series (#2484) - #2496

Merged
erikdarlingdata merged 1 commit into
devfrom
feat/2484-blocking-stats
Aug 22, 2026
Merged

erikdarlingdata merged 1 commit into
devfrom
feat/2484-blocking-stats

Conversation

@erikdarlingdata

Copy link
Copy Markdown
Owner

Second item on #2484. get_waiting_tasks gives the snapshot rows and can never answer "was this worse an hour ago" — which is the question that decides whether anything is actually wrong. The tab existed only in the WPF viewer, so it was invisible to the browser and to any agent.

No new collection. Both series read waiting_tasks, which the snapshot read already exposes row by row.

One read, two series, deliberately

A wait-type spike with no blocked sessions is a resource wait. The same spike with them is contention. Split across two tools, a caller can fetch one and draw the opposite conclusion — so they come back in one payload, and the web panels go through fanout() rather than two line() calls.

The empty answer is sharper here

State Status Says
Sampled, quiet window empty a genuine all-clear for the window
Never sampled unavailable NOT an all-clear — there is nothing to read

For the collection log the wrong answer was merely unhelpful. Here the wrong answer is the reassuring one: "nothing was waiting" reads as all-clear, and a caller who believes it stops looking. Pinned on both SKUs — same zero rows, and the quiet case must not reach for the word the never-sampled case uses.

Three things kept rather than simplified away

The viewer's database filter. Dropping it would have been a smaller signature and a different question — on a busy instance one database usually owns the blocking. The wait series stays server-wide, and the test pins that, because narrowing both would quietly change what the left-hand chart means.

Lite's probe reads v_waiting_tasks, not the base table. Every other reader in that file goes through the view; a probe on a different source could report a server as sampled for rows the trend cannot see — the wrong branch in exactly the case the probe exists to get right. Darling uses the base table throughout, so its probe does too.

fanout() over two line() calls. The no-duplicate-fetch pin would have caught it, but the rule and the payload agree here for the same reason.

Verification

  • Darling (DarlingCurrentWaitsTrendTests, live store): never-sampled vs quiet-window wording; both series in one payload; a PAGEIOLATCH row counted as a wait and not as blocked; the database filter narrowing the blocked series while leaving the wait series at full width.
  • Lite (CurrentWaitsTrendToolTests, DuckDB fixture): the same three, at the tool level.
  • Service, Lite, and both test projects build clean. JS parses clean.
  • Swept every touched .cs file for stranded doc blocks — the failure mode from Serve the raw collection log (#2484) #2494 — all clean.

Ported to Lite in the same change rather than parked on the ratchet: census 102 → 103 tools, 77 → 78 shared, Darling-unique unchanged.

Next on #2484

Blocking Stats. It needs AggregateDeadlockSeverity moved out of the Viewer into Common first — it is internal there and parses the deadlock XML — so that move is the substance of its own PR rather than a footnote in this one.

Second of the #2484 list. get_waiting_tasks returns the snapshot rows and
can never say whether now is worse than an hour ago, which is the question
that actually decides whether anything is wrong. The tab existed only in the
WPF viewer.

Both series ship in one payload deliberately. A wait-type spike with no
blocked sessions is a resource wait; the same spike with blocked sessions is
contention. Split across two tools a caller can fetch one and draw the
opposite conclusion, so they are not separable.

The empty answer is sharper here than for the collection log, because the
WRONG answer is the reassuring one. "Nothing was waiting" reads as an
all-clear and a caller who believes it stops looking. A server the collector
has never sampled gets "unavailable" and is told outright that this is NOT
an all-clear.

Three things kept rather than simplified away:

The viewer's database filter. Dropping it would have been a smaller
signature and a different question: on a busy instance one database usually
owns the blocking, and a series that cannot be narrowed to it does not
answer what the viewer answers. The wait series stays server-wide, which the
test pins, because narrowing both would silently change what the left-hand
chart means.

Lite's probe reads v_waiting_tasks, not the base table, because every other
reader in that file goes through the view. A probe on a different source
could report a server as sampled for rows the trend cannot see -- picking
the wrong branch in precisely the case it exists to get right. Darling uses
the base table throughout, so its probe does too.

The web panels go through fanout(), not two line() calls. One read, two
panels: the tab must not fetch the same read twice, and here that rule
matches the payload's own reason for existing.

Ported to Lite in the same change rather than parked on the divergence
ratchet; census 102 -> 103 tools, 77 -> 78 shared, Darling-unique unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@claude

claude Bot commented Aug 22, 2026

Copy link
Copy Markdown

Review summary

Reviewed the get_current_waits_trend addition across both Lite and Darling (SQL readers, MCP tools, web dispatch/catalog, instructions, and the browser fanout() panel). No correctness, parity, or security issues found.

Specifics checked:

  • SQL correctness: WaitingTaskTrendSql in DarlingDataReader.cs is byte-for-byte identical to the existing WPF viewer's query (ViewerDataService.BlockingTrends.cs), confirming the "verbatim" claim in the doc comment. BlockedSessionTrendSql correctly adapts the viewer's array-based $4::text[] filter to a single $4::text parameter for the MCP tool's single-database-name argument — a deliberate, well-documented simplification, not a divergence bug. All queries are parameterized (no injection risk).
  • Empty/unavailable distinction: The never-sampled vs. genuinely-quiet-window split (unavailable vs empty) is implemented identically in Lite and Darling, reusing the same status vocabulary as get_collection_log. Both live/DuckDB test suites pin the wording ("NOT an all-clear" / "EVER" present only in the never-sampled case).
  • Lite/Darling parity: Tool registered in both MCP servers with matching name, description, and parameter contract; DarlingWebEndpoints.cs catalog + dispatch entries added; both instruction docs (DarlingMcpInstructions.cs, McpInstructions.cs) updated with matching table rows; the self-verifying CrossAppMcpToolInventoryPinTests census-sentence regex will hold the 103/78/25 tool counts to the real scanned inventory rather than relying on a static count that could drift.
  • v_waiting_tasks vs base table: Lite's HasAnyWaitingTaskSampleAsync correctly probes the same v_waiting_tasks view every other reader in that file uses (including archived parquet rows); Darling's probes the base waiting_tasks table, which is the only source available there (no passthrough view exists for it) — consistent with the file's existing convention, not a new divergence.
  • JS panel wiring (server-tabs.js): fanout() correctly surfaces the backend's distinguishing message for the whole-payload empty/unavailable case (both panels), while each individual series' emptyText is used for the finer-grained "one series has rows, the other doesn't" case via VIZ.line's own empty check in panels.js. Verified against the live rendering path, not just the wiring code.
  • Type asymmetry (pre-existing, not introduced here): Lite's BlockedSessionTrendPoint.BlockedCount is int while Darling's BlockedSessionTrendRow.BlockedCount is long; both serialize identically to JSON numbers, and the class predates this PR, so not flagging as a regression.

No SQL Server missing-index recommendations applicable (no T-SQL collector changes in this PR — all reads are against the Postgres/DuckDB store).

@erikdarlingdata
erikdarlingdata merged commit 82a9bfa into dev Aug 22, 2026
6 checks passed
@erikdarlingdata
erikdarlingdata deleted the feat/2484-blocking-stats branch August 22, 2026 06:29
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