Repository navigation
Correct the force-plan scoping guidance to the call form that actually works; close #1914 on probe evidence - #1927
Conversation
…y works, and close #1914 on the probe evidence #1882 told operators to scope a force with @replica_group_id "as the fourth argument (they are extended stored procedure arguments, so the documented order matters)". That came from the reference page's syntax block and had never been run. It fails. sp_query_store_force_plan is an EXTENDED_STORED_PROCEDURE, so sys.system_parameters is empty for it and its argument surface can only be established by executing it. Probed on SQL Server 2022 (16.0.4255.1) and 2025 (17.0.4045.5), each form run from a freshly-unforced plan in a throwaway database: @query_id, @plan_id, @disable_optimized_plan_forcing = 0, @replica_group_id = 1 -> ERROR 12463 "Role id should be between (including) 1 and 4" on BOTH, for a role id of 1, which is in that range. Succeeds if that argument is 1. @query_id, @plan_id, @replica_group_id = 1 -> OK on both. Named arguments are honored and may even be reversed, so the doc's ordering warning does not describe named-argument behavior at all. So the documented form is the failing one and the form #1882 declined to emit is the working one. The guidance now names the three-argument form and says not to add @disable_optimized_plan_forcing to reach it. Two further probe findings changed what the text tells an operator to DO: - SQL Server 2025 does not validate the group id. @replica_group_id = 99 -- not a role at all -- SUCCEEDED on a standalone server and wrote a row into sys.query_store_plan_forcing_locations naming group 99. 2022 rejects it. So on the version where the feature is GA, a wrong id fails silently. - Nothing protects a typo: a misspelled @replica_groupid still reached the replica-group logic, and an invented parameter name was accepted with no error. Those same findings close #1914's proposal without building it. Collecting replica_group_id so we could emit a scoped call would mean persisting a value that sys.query_store_replicas re-points across failovers (its own documented remark), against a 2025 proc that accepts a wrong id without error -- a stale id would not fail, it would force against a replica that does not exist. No collector column, no storage migration, no StorageVersion or schema bump. The operator reading current ids off the live server at the moment they act is the only reliable version, and the text now says so. Both probe databases were dropped and verified GONE on both instances. Closes #1914 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
One conflict, CHANGELOG only -- adjacent [Unreleased] entries from parallel work (including #1913, which landed while this was in flight). Both sides kept; no code conflicts. Verified on the merged tree: -t:Rebuild clean (0 warnings), 6494 tests passing across all three suites. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ReviewWhat this does: Corrects the force-plan replica-scoping guidance emitted by The reasoning and live-probe evidence in the PR description and doc comments are solid, and the corrected guidance for the preview path is thorough. Parity/correctness gap (not caught by CI)
This is arguably the higher-stakes surface to leave stale, not the lower one: the MCP tools hand this string to an LLM/agent as `remediation_command` with no human reading a "preview" first. The existing test for this exact block (`Lite.Tests/ForcePlanReplicaScopeTests.cs:369`, `CopyPasteSql_SecondaryTarget_CarriesTheDisclosureWithTheStatement`) states the principle this now violates in its own comment: "a warning that only exists in the preview is a warning the person running the statement never sees." That test only asserts the old generic text is present, so nothing in CI catches the drift between the two disclosures. Suggest bringing the copy-paste disclosure in sync -- even kept compact, e.g. adding "Do not add `@disable_optimized_plan_forcing` (error 12463 on both versions). Check the id against `sys.query_store_replicas` first -- 2025 accepts an invalid id without error." (Since `RenderCopyPasteCommand` is the genuinely shared source for both apps, this isn't a Lite-vs-Darling drift -- it's a drift between two renderers within the shared library that both apps now inherit identically. Flagging it here because the PR's stated goal is specifically "guidance that fails when followed," and this second surface still ships the pre-fix framing.) Everything else
|
Closes #1914
The headline: #1882 shipped guidance that fails when followed, and this fixes it
#1882 told operators how to scope a force-plan recommendation to a replica:
That came from the reference page's syntax block and had never been run. It fails.
sp_query_store_force_planis anEXTENDED_STORED_PROCEDURE, sosys.system_parametersreturns zero rows for it — its argument surface cannot be read, only executed. #1914 executed it, on SQL Server 2022 (16.0.4255.1) and SQL Server 2025 (17.0.4045.5), each form run from a freshly-unforced plan in a throwaway database.@query_id, @plan_id@query_id, @plan_id, @replica_group_id = 1@query_id, @plan_id, @disable_optimized_plan_forcing = 0, @replica_group_id = 1(the documented order)@disable_optimized_plan_forcing = 1@plan_idbefore@query_id@replica_group_id = 99@replica_groupid = 2(misspelled)@there_is_no_such_parameter = 1Error 12463 is "Role id should be between (including) 1 and 4" — for a role id of 1, which is in that range. The form the reference page documents is the one that errors; the form #1882 declined to emit is the one that works. And named arguments can be reversed, so the doc's "documented order matters" warning does not describe named-argument behavior at all.
The rendered guidance now names the three-argument form and says explicitly not to add
@disable_optimized_plan_forcingto reach it.Two findings that changed what the text tells an operator to do
SQL Server 2025 does not validate the group id.
@replica_group_id = 99— not a role at all — succeeded on a standalone server and wrote a row intosys.query_store_plan_forcing_locationsnaming replica group 99. SQL Server 2022 rejects the same call. So on the version where the feature is GA, a wrong id does not fail loudly; it records a forcing against a replica that does not exist. The text now says to check the id againstsys.query_store_replicasfirst.Nothing protects a typo. A misspelled
@replica_groupidstill reached the replica-group logic, and an entirely invented parameter name was accepted with no error at all.Also observed, and consistent with the settled Azure facts:
sys.query_store_replicasholds 0 rows on a standalone 2022 but a static 4-row roles enumeration on a standalone 2025 (Primary / Secondary / Geo Secondary / Geo HA Secondary) — on a box with no Availability Group. Same shape already recorded for Azure SQL DB.Why the issue's own proposal is closed rather than built
#1914 asked whether to collect
replica_group_idso the recommendation could emit a scoped call instead of describing one. The probes answer no:sys.query_store_replicasaccumulates a row per (replica, role) across failovers — the docs' own remark — so a persisted id can silently re-point at a different replica.An operator reading the current ids off the live server at the moment they act is the only reliable version of this, and the guidance now tells them to do exactly that.
No collector column, no storage migration, no
StorageVersionbump, no Darling migration number claimed. (The store-shape instructions in the assignment are therefore moot — nothing was added.)Watched-red
Reverted the guidance text to its pre-#1914 wording: 2 of 21 failed —
TheScopedFormItSuggests_IsTheOneThatActuallyWorksandItTellsTheOperatorToVerifyTheGroupId_BecauseSql2025DoesNotValidateIt. The other 19 are #1882's behavioral pins, which must hold unchanged.One pre-existing assertion needed tightening rather than deleting:
Assert.DoesNotContain("@replica_group_id =", sql)pinned "we never emit a scoped call" as a text absence over the whole blob, which broke the moment the disclosure showed the operator the form in a comment. It now asserts over executable lines only (non---), which is the invariant that actually matters — nothing SQL Server would run carries a replica group id.Testing
-t:Rebuildclean: 0 errors, 0 warnings.6,483 passing. Live SQL Server 2022 and 2025 used for the probes; both throwaway databases dropped and verified
GONEon both instances. No Azure provisioning — the questions were box-SQL-Server questions, and the Azure half was already settled in the validation-runs reference (static 4-row roles enumeration, all statsreplica_group_id = 1,replica_roleconstantPrimary).Deferrals
None. Nothing downscoped; no new issue needed. The proposal is answered on evidence rather than deferred, and the shipped-guidance bug it uncovered is fixed here rather than filed.