Skip to content

Thread cancellation through 9 Health Parser web reads (#4203) - #4359

Merged
erikdarlingdata merged 2 commits into
devfrom
fix/4203-web-read-cancellation-5
Sep 26, 2026
Merged

erikdarlingdata merged 2 commits into
devfrom
fix/4203-web-read-cancellation-5

Conversation

@erikdarlingdata

@erikdarlingdata erikdarlingdata commented Sep 25, 2026 •

Copy link
Copy Markdown
Owner

Refs #4203.

Why

Web /api/read/* handlers must thread RequestAborted into the store query so an abandoned browser request stops its work instead of running to completion. The nine get_health_parser_* tools were still on CancellationAllowlist, meaning their store calls ran under CancellationToken.None regardless of the request.

What changes

  • DarlingMcpHealthParserTools.cs: every one of the nine health-parser tools (GetSystemHealth, GetSevereErrors, GetIOIssues, GetSchedulerIssues, GetMemoryConditions, GetCPUTasks, GetMemoryBroker, GetMemoryNodeOOM, GetSignificantWaits) now takes a trailing CancellationToken cancellationToken = default and threads it through every store call in its body: DarlingServerResolver.ResolveOrErrorAsync, the shared CollectAsync/EmptyAsync helpers, DarlingSystemHealthReader.ReadEventXmlAsync / GetLastCaptureAsync / GetLastCaptureOfTypeAsync / GetDatabaseNameMapAsync, and DarlingEngineCapability.NotCollectedStatusAsync. Each catch (Exception ex) narrows to when (ex is not OperationCanceledException) so a cancellation propagates instead of being swallowed into an error payload.
  • DarlingWebEndpoints.cs: the nine tools are removed from CancellationAllowlist; their dispatch-table entries now pass cancellationToken: c.RequestAborted.

All nine tools in scope for #4203's health-parser slice are done; nothing was left for a follow-up slice.

Test plan

  • dotnet build Darling/Darling.Tests/Darling.Tests.csproj -p:EnableWindowsTargeting=true - 0 Warning(s), 0 Error(s)
  • WebReadCancellationPinTests, PgTargetMcpSurfaceTests, McpServiceParameterDiSeatCensusTests, EngineCapabilityReadWiringTests, RuntimePreconditionReadWiringTests (net10.0-windows only, cannot run on macOS - CI decides these)
  • Full Windows test suite (CI)

For the coordinator

CancellationAllowlist shrinks by 9 in this PR (from the pre-#4203 baseline). This edits the same block of DarlingWebEndpoints.cs (CancellationAllowlist array and the read-dispatch table) that sibling #4203 slice PRs also touch, so this PR's diff will conflict textually with those siblings and needs manual resolution at merge time, not a rebase onto them per the brief.

CHANGELOG entry

SECTION: Fixed
ENTRY:

pm-pr lane report (dev merge + verification)

Merge: new head 58062b0a2cf61a287936b29f46928ce913bfb2d2, a plain merge of origin/dev (no rebase, no force push). Auto-merge was clean — git reported no conflicted files; DarlingWebEndpoints.cs's CancellationAllowlist and dispatch table merged automatically because dev's #4357 slice (trend tools) and this PR's #4203 slice (health-parser tools) touched disjoint entries in both the set literal and the dispatch table.

Allowlist check (110 -> 101): extracted the quoted names in CancellationAllowlist at origin/dev (110) and at the merged head (101). Diff shows exactly the 9 get_health_parser_* names removed (system_health, severe_errors, io_issues, scheduler_issues, memory_conditions, cpu_tasks, memory_broker, memory_node_oom, significant_waits) and nothing added. PASS.

Build: dotnet build Darling/Darling.Tests/Darling.Tests.csproj -p:EnableWindowsTargeting=true — Build succeeded, 0 Warning(s), 0 Error(s).

Verification

  1. Allowlist diff — PASS (above).
  2. CancellationToken threading — PASS. Read all 9 [McpServerTool] bodies in DarlingMcpHealthParserTools.cs. Every store call — DarlingServerResolver.ResolveOrErrorAsync, CollectAsync, EmptyAsync, DarlingSystemHealthReader.ReadEventXmlAsync/GetLastCaptureAsync/GetLastCaptureOfTypeAsync/GetDatabaseNameMapAsync, and DarlingEngineCapability.NotCollectedStatusAsync — takes and forwards cancellationToken. grep -n "CancellationToken.None" on the file returns nothing.
  3. Catch narrowing — PASS. All 9 catch blocks read catch (Exception ex) when (ex is not OperationCanceledException).
  4. Census/pin tests — read, not run (see Lite overview: show Online/Offline status #6):
    • WebReadCancellationPinTests: reflects [McpServerTool] methods via Assembly.GetTypes()/GetMethods, scoped to DarlingWebEndpoints.BuildReadDispatch().Keys. Asserts every store-reading tool method not on the allowlist takes a CancellationToken, and that no allowlist entry already has one (CancellationAllowlist_HasNoStaleEntry). New shapes pass: all 9 tools have the token and are off the allowlist.
    • PgTargetMcpSurfaceTests: builds RegisteredTools from attribute names across assemblies; unaffected by this change (no tool added/removed, just token param).
    • McpServiceParameterDiSeatCensusTests: checks DI seat registration by service-parameter type; unaffected (no new parameter types).
    • EngineCapabilityReadWiringTests / RuntimePreconditionReadWiringTests: regex-scan MCP source for NotCollectedStatusAsync(...)/RuntimePrecondition.StatusAsync(...) calls, with a documented negative-lookahead guard so a trailing cancellationToken token argument isn't mis-captured as the collector name (both regexes' comments cite Web /api/read/* handlers never thread RequestAborted into the underlying store query #4203 explicitly for this). EmptyAsync's NotCollectedStatusAsync(postgres, c.ServerId, c.ServerName, SystemHealthCollectorName, cancellationToken) call matches the guarded pattern correctly (collector name is SystemHealthCollectorName, token argument recognized and excluded).
  5. CHANGELOG entry — PASS. One line, true to the diff (9 health-parser reads thread RequestAborted). REF: link is https://github.com/erikdarlingdata/PerformanceMonitor/pull/4359 — correct PR. Body leads with Refs #4203., not Closes. No edit needed.
  6. Local test run — CAN'T run: Darling.Tests targets net10.0-windows and requires Microsoft.WindowsDesktop.App (confirmed via dotnet vstest failing with "No frameworks were found" on this macOS arm64 host). CI's Windows build job decides all 5 test classes above plus the full suite.

get_health_parser_cpu_tasks, _io_issues, _memory_broker, _memory_conditions,
_memory_node_oom, _scheduler_issues, _severe_errors, _significant_waits and
_system_health now take a CancellationToken and pass it down to every store
call (resolver, shared CollectAsync/EmptyAsync helpers, the significant-waits
direct path, the not-collected probe), so an abandoned web request stops its
query instead of running to completion. Removes all nine from
CancellationAllowlist.
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