Skip to content

Thread cancellation through 10 store/fleet web reads (#4203) - #4402

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

erikdarlingdata merged 4 commits into
devfrom
fix/4203-web-read-cancellation-13

Conversation

@erikdarlingdata

@erikdarlingdata erikdarlingdata commented Sep 26, 2026 •

Copy link
Copy Markdown
Owner

Refs #4203.

Why

/api/read/{tool} requests that abandon (client disconnect, timeout) should stop their store query instead of running it to completion. #4357/#4359 established the pattern; this converts 10 of the 11 tools in this batch.

What changes

get_store_query_stats, get_store_metrics, get_store_log, get_collector_stall_probes, get_plan_xml, get_oversized_plan_backlog, get_fleet_overview, get_sweep_reports, get_collector_cost, get_pg_blocking — each tool method now takes a trailing CancellationToken cancellationToken = default, passes it to every store call (resolver, reader helpers, not-collected probes), and narrows its catch block to when (ex is not OperationCanceledException). All ten are removed from DarlingWebEndpoints.CancellationAllowlist, and their dispatch entries in BuildReadDispatch now pass c.RequestAborted (or cancellationToken: c.RequestAborted where named).

get_store_host (the 11th tool in scope) is left unconverted and still allowlisted: its read runs through StoreHostProfileCache.GetOrGatherAsync, whose gather delegate is wrapped in its own fixed-deadline CancellationTokenSource. Linking the caller's token in required either a CreateLinkedTokenSource inside the delegate (tried, but it makes WebReadCancellationPinTests.CancellationAllowlist_HasNoStaleEntry fail — that test asserts an allowlisted tool's method must NOT already carry a CancellationToken parameter, so a method converted but kept on the allowlist is itself a violation) or removing it from the allowlist plus building a PostgresConfig-carrying harness case (the test's default BuildReadDispatch() call passes no PostgresConfig, so get_store_host short-circuits to "unavailable" before it ever reaches the store, and the cancellation assertion can never observe anything on it). Neither fit this change's scope. DarlingMcpStoreHostTools.cs was reverted to dev's shape. A follow-up should either extend the pin harness with a populated PostgresConfig case, or give the cache's own deadline logic a way to observe the caller's token without changing the test's shape.

Test plan

  • dotnet build Darling/Darling.Tests/Darling.Tests.csproj -p:EnableWindowsTargeting=true (both Debug and Release): 0 Warning(s), 0 Error(s).
  • dotnet build Lite.Tests/Lite.Tests.csproj -p:EnableWindowsTargeting=true: 0 Warning(s), 0 Error(s).
  • RED case (pre-fix): before this change, all 10 tool dispatch entries were on CancellationAllowlist, so WebReadCancellationPinTests.ConvertedDispatchEntries() never generated a theory case for any of them — the cancellation-observed assertion never ran against these ten. Removing the allowlist entries makes the reflection ratchet (EveryStoreReadingToolMethod_TakesACancellationToken_UnlessAllowlisted and CancellationAllowlist_HasNoStaleEntry) enforce them now that they're off it.
  • Ran the real Darling.Tests.dll in-process (stripped Microsoft.WindowsDesktop.App from the Release runtimeconfig, no rig/database needed for this class): dotnet Darling.Tests.dll -class Darling.Tests.WebReadCancellationPinTests → Total: 101, Errors: 0, Failed: 0, Skipped: 0.
  • Also ran PgTargetMcpSurfaceTests, McpServiceParameterDiSeatCensusTests, EngineCapabilityMissTests, RuntimePreconditionMissTests (all touching this area): Total: 4, Errors: 0, Failed: 0.
  • Darling.Tests and Lite.Tests target net10.0-windows: they BUILD here (0/0 above) but CANNOT RUN on macOS beyond the one in-process class above; the full Windows suite is unchecked — CI decides it.

CHANGELOG entry

SECTION: Fixed
ENTRY:

Known limits

  • CancellationAllowlist count on dev: 42; after this PR: 32 (10 removed; get_store_host stays, unconverted — see "What changes" above for why).
  • No shared-helper signature changes beyond passing an already-existing cancellationToken parameter through (every reader method touched here already had one defaulted to default); no other caller's compile surface changed.
  • Branch is based on dev directly (not on Thread cancellation through 10 PostgreSQL web reads (#4203) #4390/Thread cancellation through 11 more SQL Server web reads (#4203) #4393); both of those touch DarlingWebEndpoints.cs too — expect a textual merge conflict at merge time.
  • One tool short of the 11 in scope (get_store_host deferred) — a small follow-up, not a separate issue (it's a few hours at most, tied to this same allowlist/pin mechanism).

get_store_query_stats, get_store_metrics, get_store_log, get_collector_stall_probes,
get_plan_xml, get_oversized_plan_backlog, get_fleet_overview, get_sweep_reports,
get_collector_cost and get_pg_blocking now take a CancellationToken and pass it down
to every store call (resolver, rollup availability, not-collected status, direct
reads), so an abandoned web request stops its query instead of running to
completion. Removes all ten from CancellationAllowlist. get_store_host stays on the
allowlist: its GetOrGatherAsync signature makes threading the caller's token in a
larger change than this slice, so it is left for a follow-up.
@erikdarlingdata
erikdarlingdata marked this pull request as ready for review September 26, 2026 11:32
@erikdarlingdata
erikdarlingdata merged commit f16d519 into dev Sep 26, 2026
15 of 16 checks passed
@erikdarlingdata
erikdarlingdata deleted the fix/4203-web-read-cancellation-13 branch September 26, 2026 11:32
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