Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,92 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2.5.0] - 2026-03-30

### Important

- **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])

### Added

- **Dashboard integrated installer** — Add Server dialog now installs, upgrades, and uninstalls PerformanceMonitor directly, replacing the standalone InstallerGui ([#755])
- **Installer.Core shared library** — shared installation logic used by both the CLI installer and Dashboard ([#755])
- **Overview tab** for Lite with 2x2 resource chart grid (CPU, Memory, Wait Stats, TempDB) ([#689])
- **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])
- **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])
- **Query heatmap** tab in both Dashboard and Lite — visual heat map of query activity over time ([#739], [#743])
- **Webhook notifications** for alerts — configurable webhook endpoint for alert delivery ([#725])
- **Per-server collector schedule intervals** — customize collection frequency per server ([#703])
- **Investigate button** in Critical Issues grid — jump directly to relevant tab from an alert ([#684])
- **Dismiss Selected** context menu and View Log sidebar button for alert management ([#718], [#740])
- **Alert archival awareness** — dismissed_archive_alerts sidecar table, source column for live vs archived alerts, stale-data indicator, structured telemetry ([#718])
- **Dashboard read-only connection intent** — connections use `ApplicationIntent=ReadOnly` where supported ([#728])
- FUNDING.yml for GitHub Sponsors ([#752])

### Changed

- **Installer architecture** refactored: CLI installer is now a thin wrapper over Installer.Core ([#755])
- **DuckDB memory capped** at 2 GB during parquet compaction to prevent out-of-memory on large archives ([#758])
- **Text rendering** improved with `TextOptions.TextFormattingMode="Display"` for sharper text ([#710])
- **installation_history version columns** widened from nvarchar(255) to nvarchar(512) to handle long @@VERSION strings ([#712])

### Fixed

- **Memory leaks in Lite** — delta cache, event handlers, and chart helpers properly disposed ([#758])
- **Doomed transaction errors** in delta framework and ensure_collection_table — ROLLBACK now occurs before error logging ([#756])
- **XACT_STATE check** added after third-party stored procedure calls (sp_HumanEventsBlockViewer, sp_BlitzLock) to prevent doomed transaction errors ([#695])
- **CREATE DATABASE failure** when model database has large default file sizes ([#676])
- **CPU metrics mixed** for different Azure SQL databases on the same logical server ([#680])
- **Azure SQL DB vCore** FinOps calculations incorrect for serverless/vCore tiers ([#736])
- **Webhook alert recording** not persisting correctly ([#726])
- **Drill-down timezone** misalignment between chart and detail view ([#747], [#750])
- **Drill-down refresh** losing context on auto-refresh ([#744])
- **Drill-down target** incorrectly routing Memory to Memory Grants instead of Active Queries ([#706])
- **Heatmap colorbar stacking** when switching between servers ([#746])
- **Display mode pickers** not reflecting current state on tab switch ([#751])
- **Slicer custom range** handling and sub-hour display issues ([#704])
- **Overlay selection** lost on Dashboard auto-refresh ([#683])
- **Numeric values** in alert details treated as strings instead of numbers ([#732])
- **FinOps VM right-sizing** query error — `PERCENTILE_CONT` missing required `OVER()` clause
- **FinOps Enterprise features** query error on AWS RDS — `database_id` column not present in `sys.dm_db_persisted_sku_features` on RDS
- **FinOps right-click copy** broken on all Dashboard FinOps grids — context menu walked to row instead of grid
- **FinOps recommendation error logs** now include server name for easier troubleshooting

### Deprecated

- **InstallerGui** — removed from the solution and build pipeline. Use the Dashboard or CLI installer instead. ([#755])

[#676]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/676
[#680]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/680
[#682]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/682
[#683]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/683
[#684]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/684
[#689]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/689
[#695]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/695
[#703]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/703
[#704]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/704
[#706]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/706
[#710]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/710
[#712]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/712
[#718]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/718
[#725]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/725
[#726]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/726
[#728]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/728
[#732]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/732
[#736]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/736
[#739]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/739
[#740]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/740
[#743]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/743
[#744]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/744
[#746]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/746
[#747]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/747
[#750]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/750
[#751]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/751
[#752]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/752
[#755]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/755
[#756]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/756
[#758]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/758

## [2.4.0] - 2026-03-23

### Important
Expand Down
25 changes: 20 additions & 5 deletions Dashboard/Controls/FinOpsContent.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -630,7 +630,8 @@ private async Task LoadDatabaseSizesAsync()
}
}

_dbSizesFilterMgr!.UpdateData(data);
if (_dbSizesFilterMgr == null) return;
_dbSizesFilterMgr.UpdateData(data);
UpdateDbSizeCountUI();
}
catch (Exception ex)
Expand Down Expand Up @@ -901,11 +902,22 @@ private async void ServerInventoryRefresh_Click(object sender, RoutedEventArgs e
// Copy / Export Context Menu Handlers
// ============================================

private static DataGrid? FindParentDataGrid(DependencyObject? element)
{
while (element != null)
{
if (element is DataGrid dg) return dg;
element = VisualTreeHelper.GetParent(element);
}
return null;
}

private void CopyCell_Click(object sender, RoutedEventArgs e)
{
if (sender is MenuItem menuItem && menuItem.Parent is ContextMenu contextMenu)
{
if (contextMenu.PlacementTarget is DataGrid grid && grid.CurrentCell.Column != null)
var grid = FindParentDataGrid(contextMenu.PlacementTarget);
if (grid != null && grid.CurrentCell.Column != null)
{
var cellContent = TabHelpers.GetCellContent(grid, grid.CurrentCell);
if (!string.IsNullOrEmpty(cellContent))
Expand All @@ -921,7 +933,8 @@ private void CopyRow_Click(object sender, RoutedEventArgs e)
{
if (sender is MenuItem menuItem && menuItem.Parent is ContextMenu contextMenu)
{
if (contextMenu.PlacementTarget is DataGrid grid && grid.SelectedItem != null)
var grid = FindParentDataGrid(contextMenu.PlacementTarget);
if (grid != null && grid.SelectedItem != null)
{
var rowText = TabHelpers.GetRowAsText(grid, grid.SelectedItem);
if (!string.IsNullOrEmpty(rowText))
Expand All @@ -937,7 +950,8 @@ private void CopyAllRows_Click(object sender, RoutedEventArgs e)
{
if (sender is MenuItem menuItem && menuItem.Parent is ContextMenu contextMenu)
{
if (contextMenu.PlacementTarget is DataGrid grid)
var grid = FindParentDataGrid(contextMenu.PlacementTarget);
if (grid != null)
{
var sb = new StringBuilder();

Expand Down Expand Up @@ -975,7 +989,8 @@ private void ExportToCsv_Click(object sender, RoutedEventArgs e)
{
if (sender is MenuItem menuItem && menuItem.Parent is ContextMenu contextMenu)
{
if (contextMenu.PlacementTarget is DataGrid grid)
var grid = FindParentDataGrid(contextMenu.PlacementTarget);
if (grid != null)
{
var dialog = new SaveFileDialog
{
Expand Down
8 changes: 4 additions & 4 deletions Dashboard/Dashboard.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
<StartupObject>PerformanceMonitorDashboard.Program</StartupObject>
<AssemblyName>PerformanceMonitorDashboard</AssemblyName>
<Product>SQL Server Performance Monitor Dashboard</Product>
<Version>2.4.1</Version>
<AssemblyVersion>2.4.1.0</AssemblyVersion>
<FileVersion>2.4.1.0</FileVersion>
<InformationalVersion>2.4.1</InformationalVersion>
<Version>2.5.0</Version>
<AssemblyVersion>2.5.0.0</AssemblyVersion>
<FileVersion>2.5.0.0</FileVersion>
<InformationalVersion>2.5.0</InformationalVersion>
<Company>Darling Data, LLC</Company>
<Copyright>Copyright © 2026 Darling Data, LLC</Copyright>
<ApplicationIcon>EDD.ico</ApplicationIcon>
Expand Down
41 changes: 24 additions & 17 deletions Dashboard/Services/DatabaseService.FinOps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1841,11 +1841,18 @@ public async Task<List<FinOpsRecommendation>> GetFinOpsRecommendationsAsync(deci

if (edition.Contains("Enterprise", StringComparison.OrdinalIgnoreCase))
{
using var featCmd = new SqlCommand(@"
SELECT
DB_NAME(database_id) AS database_name,
feature_name
FROM sys.dm_db_persisted_sku_features", connection);
var hasDatabaseId = false;
using (var colCheck = new SqlCommand(
"SELECT COL_LENGTH('sys.dm_db_persisted_sku_features', 'database_id')", connection))
{
colCheck.CommandTimeout = 10;
hasDatabaseId = await colCheck.ExecuteScalarAsync() is not null and not DBNull;
}

var featSql = hasDatabaseId
? "SELECT DB_NAME(database_id) AS database_name, feature_name FROM sys.dm_db_persisted_sku_features"
: "SELECT N'(unknown)' AS database_name, feature_name FROM sys.dm_db_persisted_sku_features";
using var featCmd = new SqlCommand(featSql, connection);
featCmd.CommandTimeout = 30;

var features = new List<string>();
Expand Down Expand Up @@ -1909,7 +1916,7 @@ public async Task<List<FinOpsRecommendation>> GetFinOpsRecommendationsAsync(deci
}
catch (Exception ex)
{
Logger.Error($"Recommendation check failed (Enterprise features): {ex.Message}", ex);
Logger.Error($"[{ServerLabel}] Recommendation check failed (Enterprise features): {ex.Message}", ex);
}

// 2. CPU right-sizing score
Expand Down Expand Up @@ -1953,7 +1960,7 @@ FROM report.finops_utilization_efficiency AS v
}
catch (Exception ex)
{
Logger.Error($"Recommendation check failed (CPU right-sizing): {ex.Message}", ex);
Logger.Error($"[{ServerLabel}] Recommendation check failed (CPU right-sizing): {ex.Message}", ex);
}

// 3. Memory right-sizing score
Expand Down Expand Up @@ -1997,7 +2004,7 @@ ORDER BY ms.collection_time DESC
}
catch (Exception ex)
{
Logger.Error($"Recommendation check failed (Memory right-sizing): {ex.Message}", ex);
Logger.Error($"[{ServerLabel}] Recommendation check failed (Memory right-sizing): {ex.Message}", ex);
}

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

// 5. Compression savings estimator
Expand Down Expand Up @@ -2086,7 +2093,7 @@ size_mb DESC
}
catch (Exception ex)
{
Logger.Error($"Recommendation check failed (Compression): {ex.Message}", ex);
Logger.Error($"[{ServerLabel}] Recommendation check failed (Compression): {ex.Message}", ex);
}

// 6. Dormant database detection with cost impact
Expand Down Expand Up @@ -2122,7 +2129,7 @@ size_mb DESC
}
catch (Exception ex)
{
Logger.Error($"Recommendation check failed (Dormant databases): {ex.Message}", ex);
Logger.Error($"[{ServerLabel}] Recommendation check failed (Dormant databases): {ex.Message}", ex);
}

// 7. Dev/test workload detection
Expand Down Expand Up @@ -2159,7 +2166,7 @@ FROM sys.databases
}
catch (Exception ex)
{
Logger.Error($"Recommendation check failed (Dev/test detection): {ex.Message}", ex);
Logger.Error($"[{ServerLabel}] Recommendation check failed (Dev/test detection): {ex.Message}", ex);
}

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

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

// 13. Storage tier optimization — flag databases with low IO latency
Expand Down Expand Up @@ -2353,7 +2360,7 @@ ORDER BY
}
catch (Exception ex)
{
Logger.Error($"Recommendation check failed (Storage tier): {ex.Message}", ex);
Logger.Error($"[{ServerLabel}] Recommendation check failed (Storage tier): {ex.Message}", ex);
}

// 14. Reserved capacity candidates — stable CPU utilization
Expand Down Expand Up @@ -2398,7 +2405,7 @@ HAVING COUNT(*) >= 24
}
catch (Exception ex)
{
Logger.Error($"Recommendation check failed (Reserved capacity): {ex.Message}", ex);
Logger.Error($"[{ServerLabel}] Recommendation check failed (Reserved capacity): {ex.Message}", ex);
}

return recommendations;
Expand Down
6 changes: 6 additions & 0 deletions Dashboard/Services/DatabaseService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ public DatabaseService(string connectionString)
/// </summary>
public string ConnectionString => _connectionString;

/// <summary>
/// Server name extracted from the connection string, for use in log messages.
/// </summary>
private string ServerLabel =>
new SqlConnectionStringBuilder(_connectionString).DataSource ?? "(unknown)";

/// <summary>
/// Opens a throttled database connection. The semaphore is released when the connection is disposed.
/// </summary>
Expand Down
8 changes: 4 additions & 4 deletions Installer.Core/Installer.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
<RootNamespace>Installer.Core</RootNamespace>
<AssemblyName>Installer.Core</AssemblyName>
<Product>SQL Server Performance Monitor Installer Core</Product>
<Version>2.4.1</Version>
<AssemblyVersion>2.4.1.0</AssemblyVersion>
<FileVersion>2.4.1.0</FileVersion>
<InformationalVersion>2.4.1</InformationalVersion>
<Version>2.5.0</Version>
<AssemblyVersion>2.5.0.0</AssemblyVersion>
<FileVersion>2.5.0.0</FileVersion>
<InformationalVersion>2.5.0</InformationalVersion>
<Company>Darling Data, LLC</Company>
<Copyright>Copyright (c) 2026 Darling Data, LLC</Copyright>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
Expand Down
8 changes: 4 additions & 4 deletions Installer/PerformanceMonitorInstaller.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@
<!-- Application metadata -->
<AssemblyName>PerformanceMonitorInstaller</AssemblyName>
<Product>SQL Server Performance Monitor Installer</Product>
<Version>2.4.1</Version>
<AssemblyVersion>2.4.1.0</AssemblyVersion>
<FileVersion>2.4.1.0</FileVersion>
<InformationalVersion>2.4.1</InformationalVersion>
<Version>2.5.0</Version>
<AssemblyVersion>2.5.0.0</AssemblyVersion>
<FileVersion>2.5.0.0</FileVersion>
<InformationalVersion>2.5.0</InformationalVersion>
<Company>Darling Data, LLC</Company>
<Copyright>Copyright © 2026 Darling Data, LLC</Copyright>
<Description>Installation utility for SQL Server Performance Monitor - Supports SQL Server 2016-2025</Description>
Expand Down
8 changes: 4 additions & 4 deletions Lite/PerformanceMonitorLite.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
<AssemblyName>PerformanceMonitorLite</AssemblyName>
<RootNamespace>PerformanceMonitorLite</RootNamespace>
<Product>SQL Server Performance Monitor Lite</Product>
<Version>2.4.1</Version>
<AssemblyVersion>2.4.1.0</AssemblyVersion>
<FileVersion>2.4.1.0</FileVersion>
<InformationalVersion>2.4.1</InformationalVersion>
<Version>2.5.0</Version>
<AssemblyVersion>2.5.0.0</AssemblyVersion>
<FileVersion>2.5.0.0</FileVersion>
<InformationalVersion>2.5.0</InformationalVersion>
<Company>Darling Data, LLC</Company>
<Copyright>Copyright © 2026 Darling Data, LLC</Copyright>
<Description>Lightweight SQL Server performance monitoring - no installation required on target servers</Description>
Expand Down
Loading
Loading