You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Docs: per-database grants for FinOps Index Analysis (#915)
* Docs: per-database grants for FinOps Index Analysis (#915)
Adds a Permissions subsection covering the per-user-database mapping +
VIEW DATABASE STATE + VIEW DEFINITION grants that sp_IndexCleanup needs
to avoid hanging at 100% CPU when the dashboard/Lite login lacks access
to a target database. Includes a single-DB block and an sp_MSforeachdb
helper, plus the engine-bug explanation (infinite recompile loop, not
sys.dm_db_partition_stats per se).
Adds a matching troubleshooting entry naming the hang symptom and
linking to the new section.
* Docs: add SELECT on sys.sql_expression_dependencies grant (#915)
Earlier permissions block was incomplete. VIEW DATABASE STATE +
VIEW DEFINITION are necessary but not sufficient: sp_IndexCleanup
also queries sys.sql_expression_dependencies via three-part name
when scanning for computed columns / check constraints with UDF
references, and SELECT on that catalog view defaults to db_owner
only. VIEW DEFINITION does not include it.
OP confirmed in the issue thread that the previous grant set still
yielded Msg 229 on a real workload database. Reproduced locally on
SQL2019 with a database containing a UDF-bound computed column and
check constraint; adding GRANT SELECT ON sys.sql_expression_dependencies
clears it. Run completes in <1s and returns the expected UDF rows.
Updates:
- Single-DB and sp_MSforeachdb blocks now include the third grant.
- Symptoms section split into the two distinct failure modes (hang
vs. Msg 229) so readers can identify which grant they are missing.
- Troubleshooting bullet covers both symptoms.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: README.md
+39Lines changed: 39 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -539,6 +539,7 @@ Common issues:
539
539
2.**Query Store tab empty** — Query Store must be enabled on the target database (`ALTER DATABASE [YourDB] SET QUERY_STORE = ON`).
540
540
3.**Blocked process reports empty** — Both editions attempt to auto-configure the blocked process threshold to 5 seconds via `sp_configure`. On **AWS RDS**, `sp_configure` is not available — you must set `blocked process threshold (s)` through an RDS Parameter Group (see "AWS RDS Parameter Group Configuration" above). On **Azure SQL Database**, the threshold is fixed at 20 seconds and cannot be changed. If you still see no data on other platforms, verify the login has `ALTER SETTINGS` permission.
541
541
4.**Connection failures** — Verify network connectivity, firewall rules, and that the login has the required [permissions](#permissions). For Azure SQL Database, use a contained database user with `VIEW DATABASE STATE`.
542
+
5.**FinOps Index Analysis hangs, times out, or returns `Msg 229` on `sql_expression_dependencies`** — `sp_IndexCleanup` runs against each user database under your dashboard/Lite login. If that login has no user mapping in a target database, the procedure can hang at 100% CPU instead of erroring; if it is mapped but missing `SELECT` on `sys.sql_expression_dependencies`, it errors immediately on databases that have UDF-bound computed columns or check constraints. See [FinOps Index Analysis](#finops-index-analysis-per-database-grants) below for the full per-database grant set that fixes both.
542
543
543
544
---
544
545
@@ -589,6 +590,44 @@ CREATE USER [YourLogin] FOR LOGIN [YourLogin];
589
590
ALTER ROLE [SQLAgentReaderRole] ADD MEMBER [YourLogin];
590
591
```
591
592
593
+
### FinOps Index Analysis (per-database grants)
594
+
595
+
Applies to **both editions**. The FinOps Index Analysis tab runs `sp_IndexCleanup` against each user database you ask it to inspect, executing as your dashboard/Lite login. The grants above (`VIEW SERVER STATE`, `db_owner` on `PerformanceMonitor`, `SQLAgentReaderRole` on `msdb`) are *not* sufficient on their own — the login also needs a user mapping in every user database it will analyze, plus `VIEW DATABASE STATE`, `VIEW DEFINITION`, and `SELECT` on `sys.sql_expression_dependencies` in each.
596
+
597
+
The third grant is the easy one to miss: by default only members of `db_owner` have `SELECT` on `sys.sql_expression_dependencies`, and `VIEW DEFINITION` does not include it. `sp_IndexCleanup` queries that catalog view (via three-part name to the target database) when checking for computed columns and check constraints that reference UDFs, so the failure only surfaces on databases that actually have those — which is why a smoke-test database may pass and a real workload database fails with `Msg 229`.
598
+
599
+
For each target user database:
600
+
601
+
```sql
602
+
USE [YourTargetDatabase];
603
+
CREATE USER [SQLServerPerfMon] FOR LOGIN [SQLServerPerfMon];
604
+
GRANT VIEW DATABASE STATE TO [SQLServerPerfMon];
605
+
GRANT VIEW DEFINITION TO [SQLServerPerfMon];
606
+
GRANTSELECTONsys.sql_expression_dependencies TO [SQLServerPerfMon];
607
+
```
608
+
609
+
Or apply broadly with `sp_MSforeachdb`:
610
+
611
+
```sql
612
+
EXEC sp_MSforeachdb N'
613
+
USE [?];
614
+
IF DB_ID() > 4 AND DATABASEPROPERTYEX(DB_NAME(), ''Updateability'') = ''READ_WRITE''
615
+
BEGIN
616
+
IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = ''SQLServerPerfMon'')
617
+
CREATE USER [SQLServerPerfMon] FOR LOGIN [SQLServerPerfMon];
618
+
GRANT VIEW DATABASE STATE TO [SQLServerPerfMon];
619
+
GRANT VIEW DEFINITION TO [SQLServerPerfMon];
620
+
GRANT SELECT ON sys.sql_expression_dependencies TO [SQLServerPerfMon];
621
+
END';
622
+
```
623
+
624
+
**Symptoms if missing.** There are two distinct failure modes depending on which grant is absent:
625
+
626
+
-*No user mapping in the target database* — `sp_IndexCleanup` can hang at 100% CPU with no waits and never return, instead of failing fast with `Msg 916` like every other catalog DMV. The hang isn't a deadlock or a long-running scan; it's a SQL Server engine bug where a permission check at execute time gets misclassified as "this plan needs to be recompiled," producing an infinite recompile loop. Reproduces on SQL Server 2016 SP3 through 2025 CU4.
627
+
-*User is mapped with `VIEW DATABASE STATE` + `VIEW DEFINITION` but no `SELECT` on `sys.sql_expression_dependencies`* — fails fast with `Msg 229, Level 14, State 5: The SELECT permission was denied on the object 'sql_expression_dependencies', database 'mssqlsystemresource', schema 'sys'` the moment a database with a UDF-bound computed column or check constraint is reached.
628
+
629
+
Adding all three grants above eliminates both. See issue [#915](https://github.com/erikdarlingdata/PerformanceMonitor/issues/915) for the full diagnosis.
630
+
592
631
### Azure SQL Database (Lite Only)
593
632
594
633
Azure SQL Database doesn't support server-level logins. Create a **contained database user** directly on the target database:
0 commit comments