Repository navigation
Thread cancellation through 8 object-stats and config-history web reads (#4203) - #4372
Merged
Merged
Conversation
…ds (#4203) get_database_sizes, get_index_usage, get_object_locking, get_table_index_sizes (DarlingMcpObjectStatsTools) and get_database_config_changes, get_database_scoped_config, get_server_config_changes, get_trace_flag_changes (DarlingMcpConfigHistoryTools) now take a CancellationToken and pass it to every store call, so an abandoned web request stops its query instead of running to completion. Removes all eight from CancellationAllowlist.
…ellation-7 # Conflicts: # Darling/PerformanceMonitor.Darling.Service/DarlingWebEndpoints.cs
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.
Refs #4203.
Why
/api/read/{tool}requests for 8 object-stats and config-history reads ran their storequery to completion even after the caller abandoned the request.
CancellationAllowlistlisted all eight because their MCP tool methods had no
CancellationTokenparameter toreceive
HttpContext.RequestAborted.What changes
DarlingMcpObjectStatsTools.cs:GetTableIndexSizes,GetIndexUsage,GetObjectLocking,GetDatabaseSizeseach gain a trailingCancellationToken cancellationToken = default,threaded into every
DarlingObjectStatsReader/DarlingServerResolver/DarlingEngineCapabilitycall in the body, with
catch (Exception ex) when (ex is not OperationCanceledException).DarlingMcpConfigHistoryTools.cs:GetServerConfigChanges,GetDatabaseConfigChanges,GetTraceFlagChanges,GetDatabaseScopedConfigget the same treatment (GetQueryStoreHealthwas already converted in a prior lane and is unchanged here).
DarlingWebEndpoints.cs: all 8 names removed fromCancellationAllowlist; their dispatchentries now pass
cancellationToken: c.RequestAborted.No shared-helper signature changed (the reader methods already took a trailing
CancellationToken cancellationToken = default); only the tool methods and dispatch entrieswere touched.
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.WebReadCancellationPinTests(Windows-only, CI runs it) is the generic ratchet for thiswhole surface, not tool-specific, so no new pin was needed:
AConvertedReadEndpoint_ObservesCancellation_InsteadOfDialingTheStoreis a[Theory]over every dispatch entry not on
CancellationAllowlist. Against the pre-fix code these8 names were still on the allowlist, so the theory never ran a case for them (RED case:
if this PR's dispatch-entry edit were dropped for any of the 8 while the name stayed off
the allowlist, that tool's case would throw
NpgsqlExceptionagainst the dead-port storeinstead of
OperationCanceledException, failing the assert).EveryStoreReadingToolMethod_TakesACancellationToken_UnlessAllowlistedreflects every[McpServerTool]method on the read surface not on the allowlist and asserts it has aCancellationTokenparameter. Before this PR all 8 were allowlisted so this test wassilent about them; after this PR, if any of the 8 methods lost its
CancellationTokenparameter, this test fails naming it.
CancellationAllowlist_HasNoStaleEntryasserts no allowlist entry's tool method alreadyhas a
CancellationTokenparameter. Before this PR the 8 entries were consistent (notoken, on the allowlist); this PR's removal keeps that invariant, and if any of the 8
were left on the allowlist after gaining a token, this test would fail naming it.
them, listed unchecked in this environment for that reason only.
PgTargetMcpSurfaceTestsandMcpServiceParameterDiSeatCensusTestswere checked byinspection: neither references any of the 8 tool names or asserts a specific parameter
list, so neither is affected by this change.
EngineCapabilityReadWiringTestsandRuntimePreconditionReadWiringTests(named in the brief) were not found in the tree underthose names; the closest matches (
EngineCapabilityMissTests, others) do not referencethese 8 tools either.
For the coordinator
CancellationAllowlistcount: down by 8 from this PR (unioned with the sibling #4203 slicesrunning in parallel on the same file — the tender resolves any textual conflict on
DarlingWebEndpoints.cs, not this lane). No shared-helper signature changed.CHANGELOG entry
SECTION: Fixed
ENTRY:
REF:
[Thread cancellation through 8 object-stats and config-history web reads (#4203) #4372]: Thread cancellation through 8 object-stats and config-history web reads (#4203) #4372
pm-pr lane report (dev merge + verification)
New head SHA
f457cccf(merge commitf457cccf...onfix/4203-web-read-cancellation-7, parent PR head62cb3b7bfa5ed869e8b5840b0af54836af110e15+origin/devate12cdc96). Pushed toorigin/fix/4203-web-read-cancellation-7.Confirmed the PR head was still
62cb3b7b(viagh-pm.sh pr view 4372 --json headRefOid) immediately before merging and before pushing.Conflicts and resolution
Exactly one conflicted file:
Darling/PerformanceMonitor.Darling.Service/DarlingWebEndpoints.cs, one hunk, insideCancellationAllowlist's literal set.get_database_config_changes,get_database_scoped_config,get_database_sizes,get_index_usage,get_object_locking,get_server_config_changes,get_table_index_sizes,get_trace_flag_changes— but the diff context still carried the 7 blocking/deadlock names verbatim (untouched by this PR).get_blocked_process_xml,get_blocking,get_blocking_trend,get_deadlock_detail,get_deadlock_trend,get_deadlocks,get_lock_wait_trend— but its diff context still carried the 4 config-change names this PR removes.Resolution: dropped both removed groups (this PR's 8 + the siblings' 7), kept every other line as-is (both "keep the other side's stuff" halves are automatic since neither side actually re-added anything — the conflict was purely "each side's own already-removed lines reappearing in the other side's context"). No
c.RequestAborteddispatch-table lines conflicted; those merged cleanly (git resolved that half automatically, confirmed no<<<<<<<markers remain anywhere in the file after resolution).Two other files (
DarlingMcpBlockingTools.cs,DarlingServerResolver.cs) auto-merged clean, no manual edits.Allowlist diff: 94 → 86
Verified by diffing the quoted-name set at
origin/dev(94 entries) against the merged working tree (86 entries), against the 3-way base (101 entries atgit merge-base9ba47523).base (101) − this-PR's-own-removals (8) − dev-siblings'-removals (7) = 86— computed set matched the actual merged-file set byte-for-byte (diffclean).origin/dev(94) and absent from the merged result (86):get_database_config_changes,get_database_scoped_config,get_database_sizes,get_index_usage,get_object_locking,get_server_config_changes,get_table_index_sizes,get_trace_flag_changes.comm -13(no name in the merged file that isn't in the 3-way base).Build
dotnet build Darling/Darling.Tests/Darling.Tests.csproj -c Debug→ Build succeeded. 0 Warning(s). 0 Error(s).Checks
1. Allowlist (94→86, exactly the 8 named tools, nothing added): PASS. See diff above.
2. All 8 tool methods take
CancellationTokenand pass it to every store call, noCancellationToken.None:PASS.
grep -c CancellationToken.Noneon both files returned 0. Read every method body inDarlingMcpObjectStatsTools.cs(GetTableIndexSizes,GetIndexUsage,GetObjectLocking,GetDatabaseSizes) andDarlingMcpConfigHistoryTools.cs(GetServerConfigChanges,GetDatabaseConfigChanges,GetTraceFlagChanges,GetDatabaseScopedConfig) — everyawait(ResolveOrErrorAsync, the reader call,NotCollectedStatusAsync, match-count helpers) passescancellationToken. No remainingCancellationToken.Noneanywhere in either path.3. Catch blocks narrowed to
when (ex is not OperationCanceledException): PASS. All 8 methods' single catch block readscatch (Exception ex) when (ex is not OperationCanceledException).4. Census/pin tests:
PgTargetMcpSurfaceTests/McpServiceParameterDiSeatCensusTests: reflection-driven, discover tool classes/registrations generically — no reference to specific tool names in this PR's scope, unaffected by the merge. PASS (by inspection; not name-coupled).EngineCapabilityReadWiringTests(inEngineCapabilityMissTests.cs): regex-scansNotCollectedStatusAsync(...)calls in every MCP source file, tolerant of a trailingcancellationTokenargument (comment explicitly cites Web /api/read/* handlers never thread RequestAborted into the underlying store query #4203). All 8 tools'NotCollectedStatusAsynccalls end withcancellationToken. PASS.RuntimePreconditionReadWiringTests(inRuntimePreconditionMissTests.cs): regex-scansRuntimePrecondition.StatusAsync(...)calls — none of these 8 tools use that helper (they useNotCollectedStatusAsync), so this test is unaffected either way. PASS (not exercised by this PR's tools).WebReadCancellationPinTests: read in full. Half 1 drivesDarlingWebEndpoints.BuildReadDispatch().Keys.Except(CancellationAllowlist)against a dead-port store + already-cancelled context, assertingOperationCanceledException; half 2 reflects every[McpServerTool]method with anNpgsqlDataSourceparam on the read surface and asserts aCancellationTokenparam unless allowlisted; a third fact (CancellationAllowlist_HasNoStaleEntry) asserts no allowlist entry already has a converted method. All three are driven off the code, not a fixed list — with the 8 tools removed from the allowlist and each now carrying aCancellationTokenparameter wired toRequestAbortedin the dispatch table (verified:c.RequestAbortedpassed for all 8, e.g.get_database_sizes,get_index_usage, etc.), all three should now pass. PASS (by code inspection; could not execute —dotnet teston this macOS box refuses net10.0-windows test target: "Testing with VSTest target is no longer supported... .NET 10 SDK", and the project itself targetsnet10.0-windows).5. CHANGELOG entry: PASS-with-note. The
## CHANGELOG entryblock is oneFixedbullet, true to the diff, labelled[#4372]linkingpull/4372correctly, listing the same 8 tool names this merge preserved.Refs #4203.is NOT inside the CHANGELOG bullet itself — it's the PR body's opening line (line 1), outside the CHANGELOG section. If the brief means "Refs #4203" must be inside the entry text, it currently isn't; the body was not edited per instructions. Correct text, if it needs to move inside the entry: appendRefs #4203.to the ENTRY line before theREF:line.READY