Skip to content

procedure_stats runs per database on Azure SQL Database (#1833) - #1838

Merged
erikdarlingdata merged 1 commit into
devfrom
fix/1833-procedure-stats-azure-per-db
Jul 30, 2026
Merged

erikdarlingdata merged 1 commit into
devfrom
fix/1833-procedure-stats-azure-per-db

Conversation

@erikdarlingdata

Copy link
Copy Markdown
Owner

Summary

Half of #1833 (the Top Procedures grid; Query Store is the design-sized half, filed as #1836). On Azure SQL DB, procedure_stats was one of only two database-scoped collectors that never opted into the per-database connection path — verified by enumerating every RunsPerDatabase override in the collectors project. It ran once on the server entry's own connection (catalog defaults to master with a blank Database field), where its Azure variant's WHERE s.database_id = DB_ID() matched only master's procedures: zero user rows, SUCCESS logged, an empty grid that looked healthy — while Top Queries, which does run per database, worked right next to it. That asymmetry is what fingerprinted the bug in triage.

The fix is the same one-line override its six database-scoped siblings already carry (RunsPerDatabase => target.IsAzureSqlDb). The Azure query needs no change — the per-database connection makes DB_ID() mean each user database in turn, which is exactly what that predicate was written for. Shared definition: Lite and Darling both get it. Follow-ups filed: #1836 (Query Store on Azure needs an enumeration→per-database rework), #1837 (zero-items-enumerated logs SUCCESS — the Collection Health gap that hid both defects).

Test plan

  • New RunsPerDatabase_OnAzureOnly pin: true on Azure SQL DB, false on-prem and on Managed Instance
  • ProcedureStatsCollectorDefinitionTests: 11/11 pass; build 0 warnings
  • Live Azure smoke rides the next cloud-testing pass (the collector's Azure query itself is unchanged and already pinned)

🤖 Generated with Claude Code

On Azure, procedure_stats was one of two database-scoped collectors that
never opted into the per-database connection path (RunsPerDatabase). It
ran once on the server entry's own connection - whose catalog defaults to
master when the entry's Database field is blank - and the Azure variant's
WHERE s.database_id = DB_ID() then matched only master's procedures: zero
user rows, logged SUCCESS, an empty Top Procedures grid that looked
healthy, while Top Queries (per-database) kept working next to it.

The override mirrors query_stats and the five other database-scoped
collectors (=> target.IsAzureSqlDb). The Azure query needs no change: the
per-database connection makes DB_ID() each user database in turn, which is
exactly what that predicate was written for. Shared definition, so Lite
and Darling both get it.

Query Store has the same gap but needs a per-database rework on Azure
(enumeration + per-item shape) - filed as #1836. The Collection Health
gap that hid both defects (zero items enumerated logs SUCCESS) is #1837.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@claude

claude Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review

Small, well-scoped fix — verified against the codebase, not just the diff.

Correctness: Confirmed. ProcedureStatsCollector was the only database-scoped collector still missing RunsPerDatabase => target.IsAzureSqlDb. Grepped every RunsPerDatabase override in PerformanceMonitor.Collectors/ — the six siblings cited in the PR description (QueryStatsCollector, IndexObjectStatsCollector, LongQueryCompletionsCollector, BlockedProcessReportCollector, DeadlocksCollector, FileIoStatsCollector) all use the identical one-line pattern, and CollectorTargetInfo.IsAzureSqlDb defaults to false, so on-prem/MI targets are unaffected. The Azure query's existing WHERE s.database_id = DB_ID() needed no change, since it becomes correct once the connection is per-database — same reasoning as QueryStatsCollector.

Test coverage: The new RunsPerDatabase_OnAzureOnly test mirrors the exact pattern used in DeadlocksCollectorDefinitionTests, LongQueryCompletionsCollectorDefinitionTests, and BlockedProcessReportCollectorDefinitionTests (Azure=true, default=false, MI=false) — good consistency with existing conventions.

Watermark: ProcedureStatsCollector has no WatermarkColumn/PerDatabaseWatermarkColumn (it deltas client-side on plan_handle, unlike the XE-based collectors that need PerDatabaseWatermarkColumn), so no additional wiring was needed there — correctly omitted.

Lite/Darling parity: No drift risk. PerformanceMonitor.Collectors is a single shared project referenced by both Lite/PerformanceMonitorLite.csproj and the Darling projects, and both hosts' runners (RemoteCollectorService.DefinitionRunner.cs for Lite, DarlingCollectorRunner.cs for Darling) already share the same RunsPerDatabase enumeration path exercised by the six existing siblings — this fix rides already-tested infrastructure rather than adding new logic.

CHANGELOG: Entry follows the established format and correctly cross-references the two follow-up issues (#1836, #1837) filed for the related gaps.

No issues found. LGTM.

@erikdarlingdata
erikdarlingdata merged commit 9905a5d into dev Jul 30, 2026
4 checks passed
@erikdarlingdata
erikdarlingdata deleted the fix/1833-procedure-stats-azure-per-db branch July 30, 2026 13:35
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