From 0a51706e04fe5554b215d685f992c58b321bc9e6 Mon Sep 17 00:00:00 2001 From: Erik Darling <2136037+erikdarlingdata@users.noreply.github.com> Date: Wed, 25 Feb 2026 19:40:52 -0500 Subject: [PATCH 1/3] Fix release workflow to trigger on draft publish The release event type 'created' doesn't fire when a draft release is published. Add 'published' so draft-then-publish workflow also builds and attaches artifacts. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e7ba32d7..ef240d0c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,7 +6,7 @@ on: pull_request: branches: [main, dev] release: - types: [created] + types: [created, published] permissions: contents: write From f5a61ca27839f8a5407894dacf1a6e8441349d23 Mon Sep 17 00:00:00 2001 From: Erik Darling <2136037+erikdarlingdata@users.noreply.github.com> Date: Wed, 25 Feb 2026 21:04:54 -0500 Subject: [PATCH 2/3] Update README for v2.0.0: plan viewer, perfmon packs, new charts, upgrade support Highlight major v2.0.0 additions: graphical plan viewer with analysis rules, perfmon auto-select packs, interactive Run Now buttons, CPU scheduler pressure, file I/O throughput, current waits charts, DuckDB 512MB auto-archive, GUI installer as first-class single exe, upgrade detection, --reset-schedule flag. Co-Authored-By: Claude Opus 4.6 --- README.md | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index d3ddb75e..4f9f8c04 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,7 @@ A single WPF desktop application that monitors SQL Server instances remotely. Qu | Agent job monitoring | Duration vs historical avg/p95 | Duration vs historical avg/p95 | | Data storage | SQL Server (on target) | DuckDB + Parquet (local) | | Execution plans | Collected and stored | Download on demand | +| Graphical plan viewer | Built-in with analysis rules | Built-in with analysis rules | | Community tools (sp_WhoIsActive, sp_BlitzLock) | Installed automatically | Not needed | | Alerts (tray + email) | Blocking, deadlocks, CPU | Blocking, deadlocks, CPU | | Dashboard | Separate app | Built-in | @@ -130,7 +131,9 @@ PerformanceMonitorInstaller.exe YourServerName --reinstall PerformanceMonitorInstaller.exe YourServerName sa YourPassword --reinstall ``` -The installer automatically tests the connection, executes SQL scripts, downloads community dependencies, creates SQL Agent jobs, and runs initial data collection. A GUI installer (`InstallerGui/`) is also available with the same functionality. +The installer automatically tests the connection, detects existing installations and applies upgrade scripts if needed, executes SQL scripts, downloads community dependencies, creates SQL Agent jobs, and runs initial data collection. + +A GUI installer (`PerformanceMonitorInstallerGui.exe`) is also included in the release with the same functionality — same upgrade detection, same scripts, same results, with a graphical interface. ### CLI Installer Options @@ -139,6 +142,7 @@ The installer automatically tests the connection, executes SQL scripts, download | `SERVER` | SQL Server instance name (positional, required) | | `USERNAME PASSWORD` | SQL Authentication credentials (positional, optional) | | `--reinstall` | Drop existing database and perform clean install | +| `--reset-schedule` | Reset collection schedule to recommended defaults during upgrade | | `--encrypt=optional\|mandatory\|strict` | Connection encryption level (default: mandatory) | | `--trust-cert` | Trust server certificate without validation (default: require valid cert) | @@ -273,6 +277,7 @@ Data starts flowing within 1-5 minutes depending on collector schedules. ### Lite Data Storage - **Hot data** in DuckDB (7-90 days, configurable) +- **Automatic maintenance** — when DuckDB reaches 512 MB, all data is archived to Parquet and the database reinitializes, keeping inserts fast and preventing bloat - **Archive** to Parquet with ZSTD compression (~10x reduction, 30-180 days configurable) - Typical size: ~50-200 MB per server per week @@ -296,10 +301,10 @@ All configuration lives in the `config/` folder: | Tab | Contents | |-----|----------| | **Overview** | Resource overview, daily summary, critical issues, server config changes, database config changes, trace flag changes, collection health | -| **Performance** | Performance trends, expensive queries, active queries, query stats, procedure stats, Query Store, Query Store regressions, query trace patterns | -| **Resource Metrics** | Server trends, wait stats, TempDB, file I/O latency, perfmon counters, default trace events, trace analysis, session stats, latch stats, spinlock stats | +| **Performance** | Performance trends, expensive queries, active queries (with interactive "Run Now"), query stats, procedure stats, Query Store, Query Store regressions, query trace patterns, graphical plan viewer with analysis rules | +| **Resource Metrics** | Server trends, wait stats, TempDB, file I/O latency & throughput, perfmon counters (with auto-select packs), CPU scheduler pressure, default trace events, trace analysis, session stats, latch stats, spinlock stats | | **Memory** | Memory overview, grants, clerks, plan cache, memory pressure events | -| **Locking** | Blocking chains, deadlocks, blocking/deadlock trends | +| **Locking** | Blocking chains, deadlocks, blocking/deadlock trends, current waits | | **System Events** | Corruption events, contention, errors, I/O issues, scheduler issues, memory conditions | Plus a NOC-style landing page with server health cards (green/yellow/red severity indicators). @@ -308,15 +313,15 @@ Plus a NOC-style landing page with server health cards (green/yellow/red severit | Tab | Contents | |-----|----------| -| **Active Queries** | Running queries with session details, wait types, blocking, DOP, memory grants | +| **Active Queries** | Running queries with session details, wait types, blocking, DOP, memory grants, interactive "Run Now" button, graphical plan viewer with analysis rules | | **Wait Stats** | Filterable wait statistics chart with delta calculations | -| **CPU** | SQL Server CPU vs Other Processes over time | -| **Memory** | Physical memory overview, SQL Server memory trend, memory clerk breakdown | +| **CPU** | SQL Server CPU vs Other Processes over time, CPU scheduler pressure | +| **Memory** | Physical memory overview, SQL Server memory trend, memory clerk breakdown, memory grant details | | **Queries** | Performance trends, top queries and procedures by duration, Query Store integration | -| **File I/O** | Read/write I/O trends per database file | +| **File I/O** | Read/write I/O trends, file-level latency & throughput per database file | | **TempDB** | Space usage breakdown and TempDB file I/O | -| **Blocking** | Blocking/deadlock trends, blocked process reports, deadlock history | -| **Perfmon** | Selectable SQL Server performance counters over time | +| **Blocking** | Blocking/deadlock trends, blocked process reports, deadlock history, current waits | +| **Perfmon** | Selectable SQL Server performance counters with auto-select packs (General Throughput, Memory Pressure, CPU/Compilation, I/O Pressure, TempDB Pressure, Lock/Blocking) | | **Configuration** | Server configuration, database configuration, scoped configuration, trace flags | Both editions feature auto-refresh, configurable time ranges, right-click CSV export, system tray integration, and dark theme. @@ -518,8 +523,8 @@ dotnet build Lite/PerformanceMonitorLite.csproj # CLI Installer (self-contained) dotnet publish Installer/PerformanceMonitorInstaller.csproj -c Release -# GUI Installer -dotnet publish InstallerGui/InstallerGui.csproj -c Release -r win-x64 --self-contained +# GUI Installer (self-contained) +dotnet publish InstallerGui/InstallerGui.csproj -c Release ``` --- From 3bf988e36bdb14b9b1894b00db702d0aa3ebb910 Mon Sep 17 00:00:00 2001 From: Erik Darling <2136037+erikdarlingdata@users.noreply.github.com> Date: Wed, 25 Feb 2026 21:58:05 -0500 Subject: [PATCH 3/3] Fix live snapshot missing bulk insert queries and decimal cast error Use sql_handle instead of plan_handle for dm_exec_sql_text so bulk insert operations (which have no plan_handle) appear in live snapshots. Fix System.Single to System.Decimal cast errors for percent_complete and granted_query_memory_gb in Dashboard's active query reader. Co-Authored-By: Claude Opus 4.6 --- Dashboard/Services/DatabaseService.QueryPerformance.cs | 6 +++--- Lite/Services/RemoteCollectorService.QuerySnapshots.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Dashboard/Services/DatabaseService.QueryPerformance.cs b/Dashboard/Services/DatabaseService.QueryPerformance.cs index 11e987a9..561992e1 100644 --- a/Dashboard/Services/DatabaseService.QueryPerformance.cs +++ b/Dashboard/Services/DatabaseService.QueryPerformance.cs @@ -2882,7 +2882,7 @@ WHEN 5 THEN 'Snapshot' FROM sys.dm_exec_requests AS der JOIN sys.dm_exec_sessions AS des ON des.session_id = der.session_id -OUTER APPLY sys.dm_exec_sql_text(der.plan_handle) AS dest +OUTER APPLY sys.dm_exec_sql_text(COALESCE(der.sql_handle, der.plan_handle)) AS dest OUTER APPLY sys.dm_exec_text_query_plan(der.plan_handle, der.statement_start_offset, der.statement_end_offset) AS deqp OUTER APPLY sys.dm_exec_query_statistics_xml(der.session_id) AS deqs WHERE der.session_id <> @@SPID @@ -2919,7 +2919,7 @@ AND dest.text IS NOT NULL Reads = reader.IsDBNull(13) ? 0 : Convert.ToInt64(reader.GetValue(13)), Writes = reader.IsDBNull(14) ? 0 : Convert.ToInt64(reader.GetValue(14)), LogicalReads = reader.IsDBNull(15) ? 0 : Convert.ToInt64(reader.GetValue(15)), - GrantedQueryMemoryGb = reader.IsDBNull(16) ? 0m : reader.GetDecimal(16), + GrantedQueryMemoryGb = reader.IsDBNull(16) ? 0m : Convert.ToDecimal(reader.GetValue(16)), TransactionIsolationLevel = reader.IsDBNull(17) ? null : reader.GetString(17), Dop = reader.IsDBNull(18) ? 0 : Convert.ToInt32(reader.GetValue(18)), ParallelWorkerCount = reader.IsDBNull(19) ? 0 : Convert.ToInt32(reader.GetValue(19)), @@ -2927,7 +2927,7 @@ AND dest.text IS NOT NULL HostName = reader.IsDBNull(21) ? null : reader.GetString(21), ProgramName = reader.IsDBNull(22) ? null : reader.GetString(22), OpenTransactionCount = reader.IsDBNull(23) ? 0 : Convert.ToInt32(reader.GetValue(23)), - PercentComplete = reader.IsDBNull(24) ? 0m : reader.GetDecimal(24) + PercentComplete = reader.IsDBNull(24) ? 0m : Convert.ToDecimal(reader.GetValue(24)) }); } diff --git a/Lite/Services/RemoteCollectorService.QuerySnapshots.cs b/Lite/Services/RemoteCollectorService.QuerySnapshots.cs index b2cb66de..9fc2b2a2 100644 --- a/Lite/Services/RemoteCollectorService.QuerySnapshots.cs +++ b/Lite/Services/RemoteCollectorService.QuerySnapshots.cs @@ -70,7 +70,7 @@ WHEN 5 THEN 'Snapshot' FROM sys.dm_exec_requests AS der JOIN sys.dm_exec_sessions AS des ON des.session_id = der.session_id -OUTER APPLY sys.dm_exec_sql_text(der.plan_handle) AS dest +OUTER APPLY sys.dm_exec_sql_text(COALESCE(der.sql_handle, der.plan_handle)) AS dest OUTER APPLY sys.dm_exec_text_query_plan(der.plan_handle, der.statement_start_offset, der.statement_end_offset) AS deqp {1} WHERE der.session_id <> @@SPID