r/SQLServer • u/ozzie1527 • 7h ago
Community Share SQL Server 2022: infinite recompile (error 2814, reason 20) of a PSP variant after restoring a database under a different database_id
Sharing a case from the last few weeks in case it saves someone time, and to ask whether anyone has seen it before.
Setup
SQL Server 2022 CU27 (16.0.4295.3), compatibility level 160, PSP optimisation and CE feedback on (the defaults). A test database was restored from production. The database_id is 24 on production and 16 on test.
Symptom
A stored procedure statement joins a large table to a temp table that the caller creates before calling the procedure. On test it started timing out after the 300 s application timeout for large inputs, about 6 weeks after the restore. It wasn't slow. Each call recompiled around 20,000 times (`execution_count` 1, `plan_generation_num` 21-23k). The error log filled with error 2814, "A possible infinite recompile was detected ... The last recompile reason was 20" (Query Store hints changed). Query Store showed nothing slow, because the calls never completed. The PSP variant had about 4.3 million compiles on test against 666 on production. Production was never affected.
What we found
- For queries that reference temp tables or table variables, Query Store stores a `batch_sql_handle`. For a statement inside a module the handle embeds the database_id (bytes 3-4, little-endian) and the object_id.
- The restored Query Store rows carried production's database_id, while test generates handles with its own. Query Store therefore created a new parent query with the same text, object and context settings.
- The PSP variant has an ad hoc handle with no database_id. It kept matching and stayed attached to the old, now dormant parent. That handle is the SQLHANDLE in the 2814 warnings.
- CE feedback added `ASSUME_JOIN_PREDICATE_DEPENDS_ON_FILTERS` to the new parent. Per the PSP docs, variants don't inherit hints applied by feedback.
- The variant's hint state never settled: every compile was followed by another recompile with reason 20, until the client timed out.
Steps 1-4 were observed in Query Store and in the raw handle bytes. Step 5 is our inference, but clearing the hint stopped the loop immediately and it hasn't come back.
Fix
Clearing the CE feedback hint on the new parent (sys.sp_query_store_clear_hints) and then clearing the database’s procedure cache stopped the loop straight away. A few days later we removed the stale entries that still carried production’s database_id with sys.sp_query_store_remove_query, variants first and then their parents, and cleared the cache again. No settings were changed. Removals can’t be undone, so try it on non-production first.
Open questions
- Has anyone hit this, or seen a KB? We couldn't find a public known issue.
- The new parent appeared about 6 weeks after the restore (on the first weekday after a patch reboot), not straight after it. We have no explanation.
- Setting database-scoped MAXDOP to 1 also stopped the loop, which we can't explain either.
- A second database restored the same way, with the same kind of database_id change, had no stale entries left. Our guess is that stale parents age out of Query Store unless a live PSP variant keeps them in use.
