Skip to content

Commit 9a63521

Browse files
Release v2.5.0: version bumps, changelog, bug fixes found during release testing (#766)
- Bump all project versions to 2.5.0 - Add v2.5.0 changelog entry and README sync (Overview tab, query heatmap, webhooks, chart drill-down) - Fix PERCENTILE_CONT missing OVER() in FinOps VM right-sizing query - Fix database_id column reference on AWS RDS sys.dm_db_persisted_sku_features - Fix FinOps right-click copy/export broken on all Dashboard grids (PlacementTarget was row, not grid) - Add server name to FinOps recommendation error logs - Add null guard for _dbSizesFilterMgr race condition Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent d10569b commit 9a63521

9 files changed

Lines changed: 161 additions & 45 deletions

File tree

CHANGELOG.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,92 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [2.5.0] - 2026-03-30
9+
10+
### Important
11+
12+
- **InstallerGui retired**: The standalone GUI installer has been removed. Installation, upgrade, and uninstall are now handled directly from the Dashboard's Add Server dialog, powered by the new Installer.Core shared library. The CLI installer continues to work as before. ([#755])
13+
14+
### Added
15+
16+
- **Dashboard integrated installer** — Add Server dialog now installs, upgrades, and uninstalls PerformanceMonitor directly, replacing the standalone InstallerGui ([#755])
17+
- **Installer.Core shared library** — shared installation logic used by both the CLI installer and Dashboard ([#755])
18+
- **Overview tab** for Lite with 2x2 resource chart grid (CPU, Memory, Wait Stats, TempDB) ([#689])
19+
- **Chart drill-down** on CPU, Memory, TempDB, Blocking, and Deadlock charts in both Dashboard and Lite — right-click any chart point to jump to Active Queries for that time window ([#682])
20+
- **Grid-to-slicer overlay** for Query Stats, Procedure Stats, and Query Store tabs — click a row to overlay its trend on the slicer chart ([#683])
21+
- **Query heatmap** tab in both Dashboard and Lite — visual heat map of query activity over time ([#739], [#743])
22+
- **Webhook notifications** for alerts — configurable webhook endpoint for alert delivery ([#725])
23+
- **Per-server collector schedule intervals** — customize collection frequency per server ([#703])
24+
- **Investigate button** in Critical Issues grid — jump directly to relevant tab from an alert ([#684])
25+
- **Dismiss Selected** context menu and View Log sidebar button for alert management ([#718], [#740])
26+
- **Alert archival awareness** — dismissed_archive_alerts sidecar table, source column for live vs archived alerts, stale-data indicator, structured telemetry ([#718])
27+
- **Dashboard read-only connection intent** — connections use `ApplicationIntent=ReadOnly` where supported ([#728])
28+
- FUNDING.yml for GitHub Sponsors ([#752])
29+
30+
### Changed
31+
32+
- **Installer architecture** refactored: CLI installer is now a thin wrapper over Installer.Core ([#755])
33+
- **DuckDB memory capped** at 2 GB during parquet compaction to prevent out-of-memory on large archives ([#758])
34+
- **Text rendering** improved with `TextOptions.TextFormattingMode="Display"` for sharper text ([#710])
35+
- **installation_history version columns** widened from nvarchar(255) to nvarchar(512) to handle long @@VERSION strings ([#712])
36+
37+
### Fixed
38+
39+
- **Memory leaks in Lite** — delta cache, event handlers, and chart helpers properly disposed ([#758])
40+
- **Doomed transaction errors** in delta framework and ensure_collection_table — ROLLBACK now occurs before error logging ([#756])
41+
- **XACT_STATE check** added after third-party stored procedure calls (sp_HumanEventsBlockViewer, sp_BlitzLock) to prevent doomed transaction errors ([#695])
42+
- **CREATE DATABASE failure** when model database has large default file sizes ([#676])
43+
- **CPU metrics mixed** for different Azure SQL databases on the same logical server ([#680])
44+
- **Azure SQL DB vCore** FinOps calculations incorrect for serverless/vCore tiers ([#736])
45+
- **Webhook alert recording** not persisting correctly ([#726])
46+
- **Drill-down timezone** misalignment between chart and detail view ([#747], [#750])
47+
- **Drill-down refresh** losing context on auto-refresh ([#744])
48+
- **Drill-down target** incorrectly routing Memory to Memory Grants instead of Active Queries ([#706])
49+
- **Heatmap colorbar stacking** when switching between servers ([#746])
50+
- **Display mode pickers** not reflecting current state on tab switch ([#751])
51+
- **Slicer custom range** handling and sub-hour display issues ([#704])
52+
- **Overlay selection** lost on Dashboard auto-refresh ([#683])
53+
- **Numeric values** in alert details treated as strings instead of numbers ([#732])
54+
- **FinOps VM right-sizing** query error — `PERCENTILE_CONT` missing required `OVER()` clause
55+
- **FinOps Enterprise features** query error on AWS RDS — `database_id` column not present in `sys.dm_db_persisted_sku_features` on RDS
56+
- **FinOps right-click copy** broken on all Dashboard FinOps grids — context menu walked to row instead of grid
57+
- **FinOps recommendation error logs** now include server name for easier troubleshooting
58+
59+
### Deprecated
60+
61+
- **InstallerGui** — removed from the solution and build pipeline. Use the Dashboard or CLI installer instead. ([#755])
62+
63+
[#676]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/676
64+
[#680]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/680
65+
[#682]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/682
66+
[#683]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/683
67+
[#684]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/684
68+
[#689]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/689
69+
[#695]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/695
70+
[#703]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/703
71+
[#704]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/704
72+
[#706]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/706
73+
[#710]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/710
74+
[#712]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/712
75+
[#718]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/718
76+
[#725]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/725
77+
[#726]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/726
78+
[#728]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/728
79+
[#732]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/732
80+
[#736]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/736
81+
[#739]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/739
82+
[#740]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/740
83+
[#743]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/743
84+
[#744]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/744
85+
[#746]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/746
86+
[#747]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/747
87+
[#750]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/750
88+
[#751]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/751
89+
[#752]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/752
90+
[#755]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/755
91+
[#756]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/756
92+
[#758]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/758
93+
894
## [2.4.0] - 2026-03-23
995

1096
### Important

Dashboard/Controls/FinOpsContent.xaml.cs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,8 @@ private async Task LoadDatabaseSizesAsync()
630630
}
631631
}
632632

633-
_dbSizesFilterMgr!.UpdateData(data);
633+
if (_dbSizesFilterMgr == null) return;
634+
_dbSizesFilterMgr.UpdateData(data);
634635
UpdateDbSizeCountUI();
635636
}
636637
catch (Exception ex)
@@ -901,11 +902,22 @@ private async void ServerInventoryRefresh_Click(object sender, RoutedEventArgs e
901902
// Copy / Export Context Menu Handlers
902903
// ============================================
903904

905+
private static DataGrid? FindParentDataGrid(DependencyObject? element)
906+
{
907+
while (element != null)
908+
{
909+
if (element is DataGrid dg) return dg;
910+
element = VisualTreeHelper.GetParent(element);
911+
}
912+
return null;
913+
}
914+
904915
private void CopyCell_Click(object sender, RoutedEventArgs e)
905916
{
906917
if (sender is MenuItem menuItem && menuItem.Parent is ContextMenu contextMenu)
907918
{
908-
if (contextMenu.PlacementTarget is DataGrid grid && grid.CurrentCell.Column != null)
919+
var grid = FindParentDataGrid(contextMenu.PlacementTarget);
920+
if (grid != null && grid.CurrentCell.Column != null)
909921
{
910922
var cellContent = TabHelpers.GetCellContent(grid, grid.CurrentCell);
911923
if (!string.IsNullOrEmpty(cellContent))
@@ -921,7 +933,8 @@ private void CopyRow_Click(object sender, RoutedEventArgs e)
921933
{
922934
if (sender is MenuItem menuItem && menuItem.Parent is ContextMenu contextMenu)
923935
{
924-
if (contextMenu.PlacementTarget is DataGrid grid && grid.SelectedItem != null)
936+
var grid = FindParentDataGrid(contextMenu.PlacementTarget);
937+
if (grid != null && grid.SelectedItem != null)
925938
{
926939
var rowText = TabHelpers.GetRowAsText(grid, grid.SelectedItem);
927940
if (!string.IsNullOrEmpty(rowText))
@@ -937,7 +950,8 @@ private void CopyAllRows_Click(object sender, RoutedEventArgs e)
937950
{
938951
if (sender is MenuItem menuItem && menuItem.Parent is ContextMenu contextMenu)
939952
{
940-
if (contextMenu.PlacementTarget is DataGrid grid)
953+
var grid = FindParentDataGrid(contextMenu.PlacementTarget);
954+
if (grid != null)
941955
{
942956
var sb = new StringBuilder();
943957

@@ -975,7 +989,8 @@ private void ExportToCsv_Click(object sender, RoutedEventArgs e)
975989
{
976990
if (sender is MenuItem menuItem && menuItem.Parent is ContextMenu contextMenu)
977991
{
978-
if (contextMenu.PlacementTarget is DataGrid grid)
992+
var grid = FindParentDataGrid(contextMenu.PlacementTarget);
993+
if (grid != null)
979994
{
980995
var dialog = new SaveFileDialog
981996
{

Dashboard/Dashboard.csproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
<StartupObject>PerformanceMonitorDashboard.Program</StartupObject>
88
<AssemblyName>PerformanceMonitorDashboard</AssemblyName>
99
<Product>SQL Server Performance Monitor Dashboard</Product>
10-
<Version>2.4.1</Version>
11-
<AssemblyVersion>2.4.1.0</AssemblyVersion>
12-
<FileVersion>2.4.1.0</FileVersion>
13-
<InformationalVersion>2.4.1</InformationalVersion>
10+
<Version>2.5.0</Version>
11+
<AssemblyVersion>2.5.0.0</AssemblyVersion>
12+
<FileVersion>2.5.0.0</FileVersion>
13+
<InformationalVersion>2.5.0</InformationalVersion>
1414
<Company>Darling Data, LLC</Company>
1515
<Copyright>Copyright © 2026 Darling Data, LLC</Copyright>
1616
<ApplicationIcon>EDD.ico</ApplicationIcon>

Dashboard/Services/DatabaseService.FinOps.cs

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1841,11 +1841,18 @@ public async Task<List<FinOpsRecommendation>> GetFinOpsRecommendationsAsync(deci
18411841

18421842
if (edition.Contains("Enterprise", StringComparison.OrdinalIgnoreCase))
18431843
{
1844-
using var featCmd = new SqlCommand(@"
1845-
SELECT
1846-
DB_NAME(database_id) AS database_name,
1847-
feature_name
1848-
FROM sys.dm_db_persisted_sku_features", connection);
1844+
var hasDatabaseId = false;
1845+
using (var colCheck = new SqlCommand(
1846+
"SELECT COL_LENGTH('sys.dm_db_persisted_sku_features', 'database_id')", connection))
1847+
{
1848+
colCheck.CommandTimeout = 10;
1849+
hasDatabaseId = await colCheck.ExecuteScalarAsync() is not null and not DBNull;
1850+
}
1851+
1852+
var featSql = hasDatabaseId
1853+
? "SELECT DB_NAME(database_id) AS database_name, feature_name FROM sys.dm_db_persisted_sku_features"
1854+
: "SELECT N'(unknown)' AS database_name, feature_name FROM sys.dm_db_persisted_sku_features";
1855+
using var featCmd = new SqlCommand(featSql, connection);
18491856
featCmd.CommandTimeout = 30;
18501857

18511858
var features = new List<string>();
@@ -1909,7 +1916,7 @@ public async Task<List<FinOpsRecommendation>> GetFinOpsRecommendationsAsync(deci
19091916
}
19101917
catch (Exception ex)
19111918
{
1912-
Logger.Error($"Recommendation check failed (Enterprise features): {ex.Message}", ex);
1919+
Logger.Error($"[{ServerLabel}] Recommendation check failed (Enterprise features): {ex.Message}", ex);
19131920
}
19141921

19151922
// 2. CPU right-sizing score
@@ -1953,7 +1960,7 @@ FROM report.finops_utilization_efficiency AS v
19531960
}
19541961
catch (Exception ex)
19551962
{
1956-
Logger.Error($"Recommendation check failed (CPU right-sizing): {ex.Message}", ex);
1963+
Logger.Error($"[{ServerLabel}] Recommendation check failed (CPU right-sizing): {ex.Message}", ex);
19571964
}
19581965

19591966
// 3. Memory right-sizing score
@@ -1997,7 +2004,7 @@ ORDER BY ms.collection_time DESC
19972004
}
19982005
catch (Exception ex)
19992006
{
2000-
Logger.Error($"Recommendation check failed (Memory right-sizing): {ex.Message}", ex);
2007+
Logger.Error($"[{ServerLabel}] Recommendation check failed (Memory right-sizing): {ex.Message}", ex);
20012008
}
20022009

20032010
// 4. Unused index cost quantification
@@ -2019,7 +2026,7 @@ ORDER BY ms.collection_time DESC
20192026
}
20202027
catch (Exception ex)
20212028
{
2022-
Logger.Error($"Recommendation check failed (Index analysis): {ex.Message}", ex);
2029+
Logger.Error($"[{ServerLabel}] Recommendation check failed (Index analysis): {ex.Message}", ex);
20232030
}
20242031

20252032
// 5. Compression savings estimator
@@ -2086,7 +2093,7 @@ size_mb DESC
20862093
}
20872094
catch (Exception ex)
20882095
{
2089-
Logger.Error($"Recommendation check failed (Compression): {ex.Message}", ex);
2096+
Logger.Error($"[{ServerLabel}] Recommendation check failed (Compression): {ex.Message}", ex);
20902097
}
20912098

20922099
// 6. Dormant database detection with cost impact
@@ -2122,7 +2129,7 @@ size_mb DESC
21222129
}
21232130
catch (Exception ex)
21242131
{
2125-
Logger.Error($"Recommendation check failed (Dormant databases): {ex.Message}", ex);
2132+
Logger.Error($"[{ServerLabel}] Recommendation check failed (Dormant databases): {ex.Message}", ex);
21262133
}
21272134

21282135
// 7. Dev/test workload detection
@@ -2159,7 +2166,7 @@ FROM sys.databases
21592166
}
21602167
catch (Exception ex)
21612168
{
2162-
Logger.Error($"Recommendation check failed (Dev/test detection): {ex.Message}", ex);
2169+
Logger.Error($"[{ServerLabel}] Recommendation check failed (Dev/test detection): {ex.Message}", ex);
21632170
}
21642171

21652172
// 11. Maintenance window efficiency — jobs running long
@@ -2206,7 +2213,7 @@ HAVING SUM(CAST(is_running_long AS int)) >= 3
22062213
}
22072214
catch (Exception ex)
22082215
{
2209-
Logger.Error($"Recommendation check failed (Maintenance window): {ex.Message}", ex);
2216+
Logger.Error($"[{ServerLabel}] Recommendation check failed (Maintenance window): {ex.Message}", ex);
22102217
}
22112218

22122219
// 12. VM right-sizing — prescriptive core/memory targets
@@ -2215,7 +2222,7 @@ HAVING SUM(CAST(is_running_long AS int)) >= 3
22152222
using var vmCmd = new SqlCommand(@"
22162223
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
22172224
SELECT
2218-
p95_cpu = (SELECT PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY cus.sqlserver_cpu_utilization)
2225+
p95_cpu = (SELECT TOP (1) PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY cus.sqlserver_cpu_utilization) OVER ()
22192226
FROM collect.cpu_utilization_stats AS cus
22202227
WHERE cus.collection_time >= DATEADD(DAY, -7, SYSDATETIME())),
22212228
cpu_count = (SELECT si.cpu_count FROM sys.dm_os_sys_info AS si),
@@ -2291,7 +2298,7 @@ FROM sys.dm_os_performance_counters AS pc
22912298
}
22922299
catch (Exception ex)
22932300
{
2294-
Logger.Error($"Recommendation check failed (VM right-sizing): {ex.Message}", ex);
2301+
Logger.Error($"[{ServerLabel}] Recommendation check failed (VM right-sizing): {ex.Message}", ex);
22952302
}
22962303

22972304
// 13. Storage tier optimization — flag databases with low IO latency
@@ -2353,7 +2360,7 @@ ORDER BY
23532360
}
23542361
catch (Exception ex)
23552362
{
2356-
Logger.Error($"Recommendation check failed (Storage tier): {ex.Message}", ex);
2363+
Logger.Error($"[{ServerLabel}] Recommendation check failed (Storage tier): {ex.Message}", ex);
23572364
}
23582365

23592366
// 14. Reserved capacity candidates — stable CPU utilization
@@ -2398,7 +2405,7 @@ HAVING COUNT(*) >= 24
23982405
}
23992406
catch (Exception ex)
24002407
{
2401-
Logger.Error($"Recommendation check failed (Reserved capacity): {ex.Message}", ex);
2408+
Logger.Error($"[{ServerLabel}] Recommendation check failed (Reserved capacity): {ex.Message}", ex);
24022409
}
24032410

24042411
return recommendations;

Dashboard/Services/DatabaseService.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ public DatabaseService(string connectionString)
3737
/// </summary>
3838
public string ConnectionString => _connectionString;
3939

40+
/// <summary>
41+
/// Server name extracted from the connection string, for use in log messages.
42+
/// </summary>
43+
private string ServerLabel =>
44+
new SqlConnectionStringBuilder(_connectionString).DataSource ?? "(unknown)";
45+
4046
/// <summary>
4147
/// Opens a throttled database connection. The semaphore is released when the connection is disposed.
4248
/// </summary>

Installer.Core/Installer.Core.csproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
<RootNamespace>Installer.Core</RootNamespace>
88
<AssemblyName>Installer.Core</AssemblyName>
99
<Product>SQL Server Performance Monitor Installer Core</Product>
10-
<Version>2.4.1</Version>
11-
<AssemblyVersion>2.4.1.0</AssemblyVersion>
12-
<FileVersion>2.4.1.0</FileVersion>
13-
<InformationalVersion>2.4.1</InformationalVersion>
10+
<Version>2.5.0</Version>
11+
<AssemblyVersion>2.5.0.0</AssemblyVersion>
12+
<FileVersion>2.5.0.0</FileVersion>
13+
<InformationalVersion>2.5.0</InformationalVersion>
1414
<Company>Darling Data, LLC</Company>
1515
<Copyright>Copyright (c) 2026 Darling Data, LLC</Copyright>
1616
<EnableNETAnalyzers>true</EnableNETAnalyzers>

Installer/PerformanceMonitorInstaller.csproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@
2020
<!-- Application metadata -->
2121
<AssemblyName>PerformanceMonitorInstaller</AssemblyName>
2222
<Product>SQL Server Performance Monitor Installer</Product>
23-
<Version>2.4.1</Version>
24-
<AssemblyVersion>2.4.1.0</AssemblyVersion>
25-
<FileVersion>2.4.1.0</FileVersion>
26-
<InformationalVersion>2.4.1</InformationalVersion>
23+
<Version>2.5.0</Version>
24+
<AssemblyVersion>2.5.0.0</AssemblyVersion>
25+
<FileVersion>2.5.0.0</FileVersion>
26+
<InformationalVersion>2.5.0</InformationalVersion>
2727
<Company>Darling Data, LLC</Company>
2828
<Copyright>Copyright © 2026 Darling Data, LLC</Copyright>
2929
<Description>Installation utility for SQL Server Performance Monitor - Supports SQL Server 2016-2025</Description>

Lite/PerformanceMonitorLite.csproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88
<AssemblyName>PerformanceMonitorLite</AssemblyName>
99
<RootNamespace>PerformanceMonitorLite</RootNamespace>
1010
<Product>SQL Server Performance Monitor Lite</Product>
11-
<Version>2.4.1</Version>
12-
<AssemblyVersion>2.4.1.0</AssemblyVersion>
13-
<FileVersion>2.4.1.0</FileVersion>
14-
<InformationalVersion>2.4.1</InformationalVersion>
11+
<Version>2.5.0</Version>
12+
<AssemblyVersion>2.5.0.0</AssemblyVersion>
13+
<FileVersion>2.5.0.0</FileVersion>
14+
<InformationalVersion>2.5.0</InformationalVersion>
1515
<Company>Darling Data, LLC</Company>
1616
<Copyright>Copyright © 2026 Darling Data, LLC</Copyright>
1717
<Description>Lightweight SQL Server performance monitoring - no installation required on target servers</Description>

0 commit comments

Comments
 (0)