From 9a673cfad26c324a2b84b391a3fba79172787781 Mon Sep 17 00:00:00 2001 From: Erik Darling <2136037+erikdarlingdata@users.noreply.github.com> Date: Wed, 18 Mar 2026 12:31:47 -0400 Subject: [PATCH 1/3] Add llms.txt and CITATION.cff for discoverability - llms.txt: structured project summary for LLM crawlers (llmstxt.org standard) - CITATION.cff: enables GitHub "Cite this repository" widget + structured metadata Co-Authored-By: Claude Opus 4.6 (1M context) --- CITATION.cff | 22 ++++++++++++++++++++++ llms.txt | 20 ++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 CITATION.cff create mode 100644 llms.txt diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 0000000..53f532f --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,22 @@ +cff-version: 1.2.0 +title: "SQL Server Performance Monitor" +message: "If you use this software, please cite it as below." +type: software +authors: + - given-names: Erik + family-names: Darling + affiliation: "Darling Data, LLC" + website: "https://erikdarling.com" +repository-code: "https://github.com/erikdarlingdata/PerformanceMonitor" +url: "https://erikdarling.com/free-sql-server-performance-monitoring/" +license: MIT +version: "2.3.0" +date-released: "2026-03-18" +keywords: + - sql-server + - performance-monitoring + - database-monitoring + - dba-tools + - mcp-server + - wait-stats + - execution-plans diff --git a/llms.txt b/llms.txt new file mode 100644 index 0000000..ee83844 --- /dev/null +++ b/llms.txt @@ -0,0 +1,20 @@ +# SQL Server Performance Monitor + +> Free, open-source SQL Server performance monitoring tool by Erik Darling (Darling Data, LLC). 32 T-SQL collectors, real-time alerts, graphical execution plan viewer, and a built-in MCP server with 51-63 tools for AI-powered analysis. Two editions: Full (server-installed with SQL Agent) and Lite (standalone desktop app with DuckDB storage). Replaces expensive commercial tools like SentryOne and Solarwinds DPA. MIT licensed. + +- Supports SQL Server 2016-2025, Azure SQL Managed Instance, AWS RDS for SQL Server, and Azure SQL Database (Lite only) +- Monitors wait stats, query performance, blocking chains, deadlocks, memory grants, file I/O, tempdb, CPU, perfmon counters, and more +- Real-time alerts via system tray notifications and styled HTML emails +- Built-in MCP server exposes monitoring data to Claude Code, Cursor, and other LLM clients +- 4,300+ downloads across releases + +## Documentation + +- [README](https://github.com/erikdarlingdata/PerformanceMonitor/blob/main/README.md): Complete documentation including quick start, permissions, collector reference, alert configuration, MCP server setup, and edition comparison +- [Releases](https://github.com/erikdarlingdata/PerformanceMonitor/releases): Download pre-built binaries +- [Product page](https://erikdarling.com/free-sql-server-performance-monitoring/): Overview and commercial support options + +## Optional + +- [Third-party notices](https://github.com/erikdarlingdata/PerformanceMonitor/blob/main/THIRD_PARTY_NOTICES.md): License information for bundled components +- [License](https://github.com/erikdarlingdata/PerformanceMonitor/blob/main/LICENSE): MIT License From 7e484946fbde09a5289c286f7a0cc41bf6439ca5 Mon Sep 17 00:00:00 2001 From: Erik Darling <2136037+erikdarlingdata@users.noreply.github.com> Date: Wed, 18 Mar 2026 21:29:53 -0400 Subject: [PATCH 2/3] Fix query_stats collector causing SQL dumps on passive mirrors (#632) Removed CROSS APPLY sys.dm_exec_plan_attributes and the direct sys.databases join. Both dm_exec_plan_attributes and dm_exec_sql_text can trigger severity 22 engine crashes when plan handles reference RESTORING databases on passive mirror servers. Now pre-builds a temp table of ONLINE, accessible database IDs (filtered by state = 0 + HAS_DBACCESS) before the main query runs. The staging INSERT joins to this temp table instead of sys.databases, preventing any DMV from being evaluated against RESTORING databases. Co-Authored-By: Claude Opus 4.6 (1M context) --- install/08_collect_query_stats.sql | 52 ++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/install/08_collect_query_stats.sql b/install/08_collect_query_stats.sql index f866749..e838e1b 100644 --- a/install/08_collect_query_stats.sql +++ b/install/08_collect_query_stats.sql @@ -222,6 +222,38 @@ BEGIN row_hash binary(32) NULL ); + /* + Pre-build list of ONLINE, accessible database IDs. + This prevents any downstream DMV calls (dm_exec_sql_text, + dm_exec_plan_attributes) from touching RESTORING databases + on passive mirror servers — which triggers severity 22 dumps. + */ + CREATE TABLE + #online_databases + ( + database_id integer NOT NULL PRIMARY KEY, + database_name sysname NOT NULL + ); + + INSERT INTO + #online_databases + ( + database_id, + database_name + ) + SELECT + d.database_id, + d.name + FROM sys.databases AS d + WHERE d.state = 0 + AND HAS_DBACCESS(d.name) = 1 + AND d.database_id NOT IN + ( + 1, 2, 3, 4, 32761, 32767, + DB_ID(N'PerformanceMonitor') + ) + AND d.database_id < 32761; + INSERT INTO #query_stats_staging ( @@ -274,7 +306,7 @@ BEGIN ) SELECT server_start_time = @server_start_time, - database_name = d.name, + database_name = od.database_name, sql_handle = qs.sql_handle, statement_start_offset = qs.statement_start_offset, statement_end_offset = qs.statement_end_offset, @@ -353,23 +385,9 @@ BEGIN qs.statement_start_offset, qs.statement_end_offset ) AS tqp - CROSS APPLY - ( - SELECT - dbid = CONVERT(integer, pa.value) - FROM sys.dm_exec_plan_attributes(qs.plan_handle) AS pa - WHERE pa.attribute = N'dbid' - ) AS pa - INNER JOIN sys.databases AS d - ON pa.dbid = d.database_id + INNER JOIN #online_databases AS od + ON st.dbid = od.database_id WHERE qs.last_execution_time >= @cutoff_time - AND d.state = 0 /*ONLINE only — skip RESTORING databases (mirroring/AG secondary)*/ - AND pa.dbid NOT IN - ( - 1, 2, 3, 4, 32761, 32767, - DB_ID(N'PerformanceMonitor') - ) - AND pa.dbid < 32761 /*exclude contained AG system databases*/ OPTION(RECOMPILE); /* From 07a4650e1a156cbba28d1999505a62fa236752fe Mon Sep 17 00:00:00 2001 From: Erik Darling <2136037+erikdarlingdata@users.noreply.github.com> Date: Thu, 19 Mar 2026 08:09:30 -0400 Subject: [PATCH 3/3] Fix File I/O chart showing 'Unknown' for unresolved file names (#633) When sys.master_files LEFT JOIN misses (NULL file_name), fall back to DB_{database_id} and File_{file_id} instead of generic 'Unknown'. Chart labels now show e.g. 'MyDatabase.File_2' instead of 'MyDatabase.Unknown'. Co-Authored-By: Claude Opus 4.6 (1M context) --- Lite/Services/RemoteCollectorService.FileIo.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lite/Services/RemoteCollectorService.FileIo.cs b/Lite/Services/RemoteCollectorService.FileIo.cs index 595bb9d..7ffa017 100644 --- a/Lite/Services/RemoteCollectorService.FileIo.cs +++ b/Lite/Services/RemoteCollectorService.FileIo.cs @@ -199,9 +199,9 @@ private static (string DatabaseName, string FileName, string FileType, string Ph int DatabaseId, int FileId) ReadFileIoRow(SqlDataReader reader) { return ( - DatabaseName: reader.IsDBNull(0) ? "Unknown" : reader.GetString(0), - FileName: reader.IsDBNull(1) ? "Unknown" : reader.GetString(1), - FileType: reader.IsDBNull(2) ? "Unknown" : reader.GetString(2), + DatabaseName: reader.IsDBNull(0) ? $"DB_{reader.GetValue(13)}" : reader.GetString(0), + FileName: reader.IsDBNull(1) ? $"File_{reader.GetValue(14)}" : reader.GetString(1), + FileType: reader.IsDBNull(2) ? "UNKNOWN" : reader.GetString(2), PhysicalName: reader.IsDBNull(3) ? "" : reader.GetString(3), SizeMb: reader.IsDBNull(4) ? 0m : reader.GetDecimal(4), NumOfReads: reader.IsDBNull(5) ? 0L : reader.GetInt64(5),