Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
e4b905f
Add Import Connections button to Lite sidebar
erikdarlingdata Mar 18, 2026
601b039
Merge pull request #628 from erikdarlingdata/feature/import-server-co…
erikdarlingdata Mar 18, 2026
3187622
Auto-refresh after Import Data completes
erikdarlingdata Mar 18, 2026
18d7c66
Merge pull request #629 from erikdarlingdata/fix/import-data-auto-ref…
erikdarlingdata Mar 18, 2026
9a673cf
Add llms.txt and CITATION.cff for discoverability
erikdarlingdata Mar 18, 2026
eb3e4c2
Add llms.txt and CITATION.cff for discoverability (#630)
erikdarlingdata Mar 18, 2026
7e48494
Fix query_stats collector causing SQL dumps on passive mirrors (#632)
erikdarlingdata Mar 19, 2026
07a4650
Fix File I/O chart showing 'Unknown' for unresolved file names (#633)
erikdarlingdata Mar 19, 2026
9c2a77f
Merge pull request #636 from erikdarlingdata/fix/file-io-unknown-labe…
erikdarlingdata Mar 19, 2026
fb84cb6
Revert "Merge pull request #636 from erikdarlingdata/fix/file-io-unkn…
erikdarlingdata Mar 19, 2026
5c542ec
Merge pull request #637 from erikdarlingdata/revert/file-io-labels
erikdarlingdata Mar 19, 2026
a657c70
Add gap detection to all delta collectors (#633)
erikdarlingdata Mar 19, 2026
366dced
Merge pull request #638 from erikdarlingdata/fix/delta-gap-detection-633
erikdarlingdata Mar 19, 2026
1129ddf
Add colored output and version check to CLI installer
erikdarlingdata Mar 19, 2026
fd7f1e1
Merge pull request #639 from erikdarlingdata/feature/cli-colors-and-u…
erikdarlingdata Mar 19, 2026
2d122ab
Fix File I/O NULL fallbacks when sys.master_files is inaccessible (#633)
erikdarlingdata Mar 19, 2026
e6515d3
Merge pull request #640 from erikdarlingdata/fix/file-io-null-masterf…
erikdarlingdata Mar 19, 2026
ba49218
Add version check on startup to GUI installer
erikdarlingdata Mar 19, 2026
ff43c28
Merge pull request #641 from erikdarlingdata/feature/gui-installer-up…
erikdarlingdata Mar 19, 2026
2737dab
Add Velopack package and startup hook to Dashboard
erikdarlingdata Mar 19, 2026
f115693
Merge pull request #643 from erikdarlingdata/feature/dashboard-velopack
erikdarlingdata Mar 19, 2026
2f6fe86
Wire Velopack UpdateManager into Dashboard update flow (#635)
erikdarlingdata Mar 19, 2026
85201a6
Merge pull request #644 from erikdarlingdata/feature/dashboard-velopa…
erikdarlingdata Mar 19, 2026
1756c62
Add time display picker to Dashboard and Lite toolbars (#646)
erikdarlingdata Mar 20, 2026
65cd901
Delete .coderabbit.yaml (#647)
erikdarlingdata Mar 20, 2026
8d29ef3
fix warnings
OZTPR Mar 20, 2026
a565e63
minor version Nuget updates
OZTPR Mar 20, 2026
1b3a3b4
fix error NETSDK1151 The referenced project '..\InstallerGui\Installe…
OZTPR Mar 20, 2026
17c4c29
Merge branch 'dev' of https://github.com/MisterZeus/PerformanceMonito…
OZTPR Mar 20, 2026
afa348e
Merge pull request #653 from MisterZeus/dev
erikdarlingdata Mar 20, 2026
cb48df3
Add missing date columns to Query Stats and Procedure Stats tabs (#64…
erikdarlingdata Mar 20, 2026
600ac70
Add per-tab time range slicers to Lite query tabs (#655)
erikdarlingdata Mar 20, 2026
b845bcf
Full Velopack integration for Dashboard (#635)
erikdarlingdata Mar 20, 2026
55c5033
Merge pull request #657 from erikdarlingdata/feature/dashboard-velopa…
erikdarlingdata Mar 20, 2026
e283c70
Rename Import Connections to Import Settings, copy config files
erikdarlingdata Mar 20, 2026
b9a5049
Merge pull request #658 from erikdarlingdata/feature/import-settings
erikdarlingdata Mar 20, 2026
cb47b80
Velopack integration for Lite + data directory migration (#635)
erikdarlingdata Mar 20, 2026
1fae828
Skip running jobs collector when login lacks msdb access (#656)
erikdarlingdata Mar 20, 2026
79b0868
Add per-tab time range slicers to Dashboard query tabs (#662)
erikdarlingdata Mar 20, 2026
d493203
Merge pull request #661 from erikdarlingdata/fix/656-msdb-access
erikdarlingdata Mar 20, 2026
dd57067
Add CollectionTime and NtUserName to trace pattern drill-down (#663)
erikdarlingdata Mar 20, 2026
0d162d1
Merge pull request #664 from erikdarlingdata/feature/663-trace-patter…
erikdarlingdata Mar 20, 2026
b36ff7e
Merge pull request #660 from erikdarlingdata/feature/lite-velopack
erikdarlingdata Mar 20, 2026
b6c5efc
Fix #659: Preserve DataGrid sort order across auto-refresh
erikdarlingdata Mar 20, 2026
21696ee
Merge pull request #665 from erikdarlingdata/fix/659-preserve-sort-on…
erikdarlingdata Mar 23, 2026
726294c
Fix trigger name extraction when comment contains ' ON ' (#666)
erikdarlingdata Mar 23, 2026
4757a4a
Merge pull request #668 from erikdarlingdata/fix/trigger-comment-on-666
erikdarlingdata Mar 23, 2026
3e39b15
Fix FinOps expensive queries error + imported parquet warnings
erikdarlingdata Mar 23, 2026
b107453
Merge pull request #669 from erikdarlingdata/fix/finops-expensive-que…
erikdarlingdata Mar 23, 2026
6d14403
Pre-fill context fields and add default expiration setting
HannahVernon Mar 10, 2026
130438a
Add missing Job Name parsing to PopulateFromDetailText
HannahVernon Mar 19, 2026
8f44811
Fix mute dialog showing truncated query text for blocking alerts
HannahVernon Mar 19, 2026
b711025
Add tooltip to Query Text field in mute rule dialog
HannahVernon Mar 19, 2026
f228e53
Version bump to 2.4.0
erikdarlingdata Mar 23, 2026
913fe2a
Merge pull request #670 from erikdarlingdata/release-prep/v2.4.0
erikdarlingdata Mar 23, 2026
827b078
Merge pull request #642 from HannahVernon/feature/alert-muting-part-2
erikdarlingdata Mar 23, 2026
4d004ed
Add 2.3.0-to-2.4.0 upgrade: re-apply growth/VLF columns
erikdarlingdata Mar 23, 2026
a3edcfb
Fix installers reporting SUCCESS when files fail
erikdarlingdata Mar 23, 2026
ade185e
Merge pull request #671 from erikdarlingdata/fix/release-prep-commits
erikdarlingdata Mar 23, 2026
69c42aa
Release prep for v2.4.0
erikdarlingdata Mar 23, 2026
c3e49bf
Merge pull request #672 from erikdarlingdata/release-prep/v2.4.0-final
erikdarlingdata Mar 23, 2026
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
67 changes: 0 additions & 67 deletions .coderabbit.yaml

This file was deleted.

37 changes: 37 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,17 @@ jobs:
- name: Publish Dashboard
run: dotnet publish Dashboard/Dashboard.csproj -c Release -o publish/Dashboard

- name: Publish Dashboard (self-contained for Velopack)
if: github.event_name == 'release'
run: dotnet publish Dashboard/Dashboard.csproj -c Release -r win-x64 --self-contained -o publish/Dashboard-velopack

- name: Publish Lite
run: dotnet publish Lite/PerformanceMonitorLite.csproj -c Release -o publish/Lite

- name: Publish Lite (self-contained for Velopack)
if: github.event_name == 'release'
run: dotnet publish Lite/PerformanceMonitorLite.csproj -c Release -r win-x64 --self-contained -o publish/Lite-velopack

- name: Publish CLI Installer
run: dotnet publish Installer/PerformanceMonitorInstaller.csproj -c Release

Expand Down Expand Up @@ -156,6 +164,25 @@ jobs:
Remove-Item "releases/PerformanceMonitorInstaller-$version.zip" -ErrorAction SilentlyContinue
Compress-Archive -Path 'signed/Installer/*' -DestinationPath "releases/PerformanceMonitorInstaller-$version.zip" -Force

- name: Create Velopack release (Dashboard)
if: github.event_name == 'release'
shell: pwsh
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ steps.version.outputs.VERSION }}
run: |
dotnet tool install -g vpk
New-Item -ItemType Directory -Force -Path releases/velopack-dashboard
New-Item -ItemType Directory -Force -Path releases/velopack-lite

# Dashboard: download previous + pack
vpk download github --repoUrl https://github.com/${{ github.repository }} --channel dashboard -o releases/velopack-dashboard --token $env:GH_TOKEN
vpk pack -u PerformanceMonitorDashboard -v $env:VERSION -p publish/Dashboard-velopack -e PerformanceMonitorDashboard.exe -o releases/velopack-dashboard --channel dashboard

# Lite: download previous + pack
vpk download github --repoUrl https://github.com/${{ github.repository }} --channel lite -o releases/velopack-lite --token $env:GH_TOKEN
vpk pack -u PerformanceMonitorLite -v $env:VERSION -p publish/Lite-velopack -e PerformanceMonitorLite.exe -o releases/velopack-lite --channel lite

- name: Generate checksums
if: github.event_name == 'release'
shell: pwsh
Expand All @@ -174,3 +201,13 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release upload ${{ github.event.release.tag_name }} releases/*.zip releases/SHA256SUMS.txt --clobber

- name: Upload Velopack artifacts
if: github.event_name == 'release'
shell: pwsh
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ steps.version.outputs.VERSION }}
run: |
vpk upload github --repoUrl https://github.com/${{ github.repository }} --channel dashboard -o releases/velopack-dashboard --releaseName "v$env:VERSION" --tag "v$env:VERSION" --merge --token $env:GH_TOKEN
vpk upload github --repoUrl https://github.com/${{ github.repository }} --channel lite -o releases/velopack-lite --releaseName "v$env:VERSION" --tag "v$env:VERSION" --merge --token $env:GH_TOKEN
58 changes: 58 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,64 @@ 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.4.0] - 2026-03-23

### Important

- **Lite data directory moved**: Lite now stores all data (config, DuckDB, archives, logs) in `%LOCALAPPDATA%\PerformanceMonitorLite\` instead of alongside the executable. This enables auto-update support. Existing users upgrading from the zip should use **Import Settings** and **Import Data** to bring over their configuration and historical data from the old install folder.
- **Auto-update (Windows)**: Both Dashboard and Lite now include Velopack auto-update. Users who install via the new Setup.exe will receive update notifications and can download + apply updates from within the app. Existing zip distribution continues to work as before.

### Added

- **Velopack auto-update** for Dashboard and Lite — check on startup, download + apply from About window with confirmation dialog before restart ([#635])
- **Per-tab time range slicers** on Dashboard and Lite query tabs — filter data directly on each tab without changing global time range ([#655], [#662])
- **Time display picker** (Local/UTC/Server) in Dashboard and Lite toolbars ([#646])
- **Import Settings** — renamed from "Import Connections", now also copies `settings.json`, `collection_schedule.json`, `ignored_wait_types.json`, and `alert_state.json` from a previous install
- **Alert muting improvements** — pre-fill context fields (database, query, wait type, job name) from alert detail text, configurable default expiration for new mute rules, tooltip on query text field ([#642])
- **Missing date columns** on Query Stats and Procedure Stats tabs (`creation_time`, `last_execution_time`) ([#649], [#651], [#654])
- **Trace pattern drill-down** now includes `CollectionTime` and `NtUserName` columns ([#663])
- **DataGrid sort preservation** across auto-refresh — sort order no longer resets when data refreshes ([#659])
- **CLI installer**: colored output (green/red/yellow) and version check on startup ([#639])
- **GUI installer**: version check on startup
- **Growth rate and VLF count** columns in Database Sizes (from v2.3.0 nightly, now in upgrade path) ([#567])
- `llms.txt` and `CITATION.cff` for project discoverability ([#630])

### Changed

- **Lite data directory** moved to `%LOCALAPPDATA%\PerformanceMonitorLite\` for Velopack compatibility
- **Delta gap detection** added to all cumulative-counter collectors (file I/O, wait stats, query stats, procedure stats, memory grants) — prevents inflated spikes after app restart ([#633])
- **File I/O NULL fallbacks** improved when `sys.master_files` is inaccessible — falls back to `DB_NAME()` and `File_{id}` instead of generic "Unknown" ([#633])
- **Running jobs collector** skipped gracefully when login lacks msdb access ([#656])
- NuGet packages updated to latest minor versions ([#653])

### Fixed

- **Installer writing SUCCESS when files fail** — CLI tolerated 1 failure in automated mode, GUI had a similar workaround. Now any failure = not success.
- **Query stats collector causing SQL dumps** on passive mirror servers — removed `dm_exec_plan_attributes` CROSS APPLY, uses temp table of ONLINE database IDs instead ([#632])
- **Trigger name extraction** fails when comment before `CREATE TRIGGER` contains " ON " ([#666])
- **FinOps expensive queries** DuckDB error — query referenced `statement_start_offset` column that doesn't exist in schema
- **Imported parquet files** not recognized by archive compaction — added regex patterns for `imported_` prefix
- **Auto-refresh after Import Data** — views now refresh immediately after import completes

[#630]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/630
[#632]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/632
[#633]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/633
[#635]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/635
[#639]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/639
[#642]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/642
[#646]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/646
[#649]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/649
[#651]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/651
[#653]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/653
[#654]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/654
[#655]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/655
[#656]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/656
[#659]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/659
[#662]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/662
[#663]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/663
[#666]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/666
[#567]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/567

## [2.3.0] - 2026-03-18

### Important
Expand Down
22 changes: 22 additions & 0 deletions CITATION.cff
Original file line number Diff line number Diff line change
@@ -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
77 changes: 76 additions & 1 deletion Dashboard/AboutWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
using System.Reflection;
using System.Windows;
using PerformanceMonitorDashboard.Services;
using Velopack;
using System;

namespace PerformanceMonitorDashboard
{
Expand All @@ -19,6 +21,8 @@ public partial class AboutWindow : Window
private const string DarlingDataUrl = "https://www.erikdarling.com";

private string? _updateReleaseUrl;
private UpdateManager? _velopackMgr;
private Velopack.UpdateInfo? _pendingUpdate;

public AboutWindow()
{
Expand Down Expand Up @@ -47,6 +51,32 @@ private async void CheckUpdatesLink_Click(object sender, RoutedEventArgs e)
UpdateStatusText.Text = "Checking for updates...";
UpdateStatusText.Visibility = Visibility.Visible;

// Try Velopack first (supports download + apply)
try
{
_velopackMgr = new UpdateManager(
new Velopack.Sources.GithubSource("https://github.com/erikdarlingdata/PerformanceMonitor", null, false));

var updateInfo = await _velopackMgr.CheckForUpdatesAsync();
if (updateInfo != null)
{
_pendingUpdate = updateInfo;
UpdateStatusText.Text = $"Update available: v{updateInfo.TargetFullRelease.Version} — click to install";
UpdateStatusText.Cursor = System.Windows.Input.Cursors.Hand;
UpdateStatusText.MouseLeftButtonUp -= UpdateStatusText_Click;
UpdateStatusText.MouseLeftButtonUp += VelopackDownload_Click;
UpdateStatusText.TextDecorations = System.Windows.TextDecorations.Underline;
UpdateStatusText.Foreground = FindResource("AccentBrush") as System.Windows.Media.Brush
?? System.Windows.Media.Brushes.DodgerBlue;
return;
}
}
catch
{
// Velopack packages may not exist yet — fall through to legacy check
}

// Fallback: GitHub Releases API check (opens browser)
var result = await UpdateCheckService.CheckForUpdateAsync(bypassCache: true);

if (result == null)
Expand All @@ -56,8 +86,9 @@ private async void CheckUpdatesLink_Click(object sender, RoutedEventArgs e)
else if (result.IsUpdateAvailable)
{
_updateReleaseUrl = result.ReleaseUrl;
UpdateStatusText.Text = $"Update available: {result.LatestVersion} (you have {result.CurrentVersion})";
UpdateStatusText.Text = $"Update available: {result.LatestVersion} (you have {result.CurrentVersion}) — click to open releases";
UpdateStatusText.Cursor = System.Windows.Input.Cursors.Hand;
UpdateStatusText.MouseLeftButtonUp -= VelopackDownload_Click;
UpdateStatusText.MouseLeftButtonUp += UpdateStatusText_Click;
UpdateStatusText.TextDecorations = System.Windows.TextDecorations.Underline;
UpdateStatusText.Foreground = FindResource("AccentBrush") as System.Windows.Media.Brush
Expand All @@ -69,6 +100,50 @@ private async void CheckUpdatesLink_Click(object sender, RoutedEventArgs e)
}
}

private bool _updateDownloaded;

private async void VelopackDownload_Click(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (_velopackMgr == null || _pendingUpdate == null) return;

// Step 3: restart with confirmation
if (_updateDownloaded)
{
var result = MessageBox.Show(this,
"The application will close and restart with the new version. Continue?",
"Update Ready",
MessageBoxButton.OKCancel,
MessageBoxImage.Question);

if (result == MessageBoxResult.OK)
{
_velopackMgr.ApplyUpdatesAndRestart(_pendingUpdate.TargetFullRelease);
}
return;
}

// Step 2: download
try
{
UpdateStatusText.MouseLeftButtonUp -= VelopackDownload_Click;
UpdateStatusText.TextDecorations = null;
UpdateStatusText.Cursor = System.Windows.Input.Cursors.Arrow;
UpdateStatusText.Text = "Downloading update...";

await _velopackMgr.DownloadUpdatesAsync(_pendingUpdate);

_updateDownloaded = true;
UpdateStatusText.Text = "Update downloaded.";
UpdateStatusText.Cursor = System.Windows.Input.Cursors.Hand;
UpdateStatusText.TextDecorations = System.Windows.TextDecorations.Underline;
UpdateStatusText.MouseLeftButtonUp += VelopackDownload_Click;
}
catch (Exception ex)
{
UpdateStatusText.Text = $"Download failed: {ex.Message}";
}
}

private void UpdateStatusText_Click(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (!string.IsNullOrEmpty(_updateReleaseUrl))
Expand Down
14 changes: 9 additions & 5 deletions Dashboard/Analysis/FactScorer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -308,8 +308,10 @@ private static double ScoreBadActorFact(Fact fact)
/// </summary>
private static double ScoreAnomalyFact(Fact fact)
{
if (fact.Key.StartsWith("ANOMALY_CPU_SPIKE") || fact.Key.StartsWith("ANOMALY_READ_LATENCY")
|| fact.Key.StartsWith("ANOMALY_WRITE_LATENCY"))
if ( fact.Key.StartsWith("ANOMALY_CPU_SPIKE" , StringComparison.OrdinalIgnoreCase)
|| fact.Key.StartsWith("ANOMALY_READ_LATENCY" , StringComparison.OrdinalIgnoreCase)
|| fact.Key.StartsWith("ANOMALY_WRITE_LATENCY", StringComparison.OrdinalIgnoreCase)
)
{
// Deviation-based scoring: 2σ = 0.5, 4σ = 1.0
var deviation = fact.Metadata.GetValueOrDefault("deviation_sigma");
Expand All @@ -319,15 +321,17 @@ private static double ScoreAnomalyFact(Fact fact)
return base_score * confidence;
}

if (fact.Key.StartsWith("ANOMALY_WAIT_"))
if (fact.Key.StartsWith("ANOMALY_WAIT_", StringComparison.OrdinalIgnoreCase))
{
// Ratio-based scoring: 5x = 0.5, 20x = 1.0
var ratio = fact.Metadata.GetValueOrDefault("ratio");
if (ratio < 5) return 0.0;
return 0.5 + 0.5 * Math.Min((ratio - 5.0) / 15.0, 1.0);
}

if (fact.Key.StartsWith("ANOMALY_BLOCKING_SPIKE") || fact.Key.StartsWith("ANOMALY_DEADLOCK_SPIKE"))
if ( fact.Key.StartsWith("ANOMALY_BLOCKING_SPIKE", StringComparison.OrdinalIgnoreCase)
|| fact.Key.StartsWith("ANOMALY_DEADLOCK_SPIKE", StringComparison.OrdinalIgnoreCase)
)
{
// Ratio-based: 3x = 0.5, 10x = 1.0
var ratio = fact.Metadata.GetValueOrDefault("ratio");
Expand Down Expand Up @@ -859,7 +863,7 @@ private static (double concerning, double? critical)? GetWaitThresholds(string w
/// <summary>
/// An amplifier definition: a named predicate that boosts severity when matched.
/// </summary>
internal class AmplifierDefinition
internal sealed class AmplifierDefinition
{
public string Description { get; set; } = string.Empty;
public double Boost { get; set; }
Expand Down
Loading
Loading