Skip to content

Release v2.4.0#673

Merged
erikdarlingdata merged 61 commits into
mainfrom
dev
Mar 23, 2026
Merged

Release v2.4.0#673
erikdarlingdata merged 61 commits into
mainfrom
dev

Conversation

@erikdarlingdata
Copy link
Copy Markdown
Owner

Release v2.4.0

See CHANGELOG.md for full details.

Highlights

  • Auto-update: Velopack integration for Dashboard and Lite (Setup.exe + delta updates)
  • Lite data directory: moved to %LOCALAPPDATA% for auto-update compatibility
  • Per-tab time slicers: Dashboard and Lite query tabs
  • Time display picker: Local/UTC/Server toggle
  • Alert muting improvements: context pre-fill, default expiration
  • Import Settings: expanded from Import Connections to cover all config files
  • DataGrid sort preservation across auto-refresh
  • Installer fixes: no more false SUCCESS on failure, colored CLI output, version check

Testing completed

  • Fresh install: sql2016 (CLI)
  • Upgrade: sql2017 (CLI), sql2019 + sql2025 (GUI), sql2022 (CLI + idempotency)
  • Multi-hop: 2.1.0 → 2.2.0 → 2.3.0 → 2.4.0 on sql2019
  • Uninstall + reinstall: sql2016
  • Cloud: Azure SQL DB (Lite), AWS RDS (Lite + Dashboard + installer)
  • Automated: 38 installer adversarial tests passed
  • Data survival verified on all upgrade paths
  • Nightly build clean

erikdarlingdata and others added 30 commits March 18, 2026 11:33
Imports server connections from a previous Lite install's
config/servers.json. Upserts by server name — existing servers are
skipped, new ones are added with their original GUIDs so Credential
Manager entries still resolve without re-entering passwords.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…nnections

Add Import Connections to Lite sidebar
Triggers connection check, server list refresh, and status bar update
after a successful data import so historical data is visible
immediately without manual refresh.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…resh

Auto-refresh after Import Data completes
- 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) <noreply@anthropic.com>
- 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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
…ls-633

Fix File I/O chart 'Unknown' labels (#633)
…own-labels-633"

This reverts commit 9c2a77f, reversing
changes made to eb3e4c2.
Revert File I/O label change (premature)
The maxGapSeconds + collectionTime gap detection was only on the
perfmon collector. All other cumulative-counter collectors (file I/O,
wait stats, query stats, procedure stats, memory grants) produced
inflated deltas after app restart because the cached baseline could
be hours old.

Now all CalculateDelta calls pass collectionTime and maxGapSeconds=300
(5 minutes). If the gap since the last cached value exceeds 5 minutes,
the delta is treated as a new baseline (returns 0) instead of
computing against the stale value.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Colored console output: green for success, red for errors, yellow
  for warnings. Matches the GUI installer's visual pattern.
- Version check on startup: calls GitHub Releases API and shows a
  prominent yellow banner if a newer version is available. Silent
  if current or if GitHub is unreachable (5-second timeout).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…pdate-check

Add colored output and version check to CLI installer
When the login lacks VIEW ANY DEFINITION, sys.master_files returns
NULLs via the LEFT JOIN. Updated ISNULL fallbacks in both the Lite
collector and the Full Edition install script:

- database_name: falls back to DB_NAME(vfs.database_id)
- file_name: falls back to 'File_{file_id}'
- physical_name: falls back to empty string

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…iles-633

Fix File I/O NULL fallbacks for restricted permissions (#633)
Checks GitHub Releases API on load and logs a warning if a newer
version is available. 5-second timeout, best-effort — won't block
the installer if GitHub is unreachable.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…date-check

Add version check to GUI installer
Adds VelopackApp.Build().Run() early in OnStartup to handle
install/uninstall/update lifecycle hooks. This is the prerequisite
for auto-update — the actual download/apply logic and build pipeline
(vpk pack) will follow in subsequent PRs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add Velopack package and startup hook to Dashboard (#635)
Startup check tries Velopack first (can download + apply), falls
back to the existing GitHub API check (notification only) when
Velopack packages aren't available yet.

About > Check for Updates now supports the full Velopack flow:
check → download → restart to apply. Falls back to opening the
releases page in browser when no Velopack packages exist.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ck-updater

Wire Velopack update manager into Dashboard (#635)
Both apps now have a "Times: [Server Time | Local Time | UTC]" ComboBox
on the global toolbar. Changing the mode:
- Updates ServerTimeHelper.CurrentDisplayMode
- Refreshes all DataGrid timestamp columns via Items.Refresh()
- Dashboard persists the preference via UserPreferencesService

The existing ServerTimeConverter already reads CurrentDisplayMode, so
all timestamp columns update instantly without re-querying.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rGui.csproj' is a self-contained executable. A self-contained executable cannot be referenced by a non self-contained executable. For more information, see https://aka.ms/netsdk1151 Installer.Tests C:\Program Files\dotnet\sdk\10.0.104\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.Sdk.targets 1330
fix warnings and upgrade only the minor versions of NuGet packages
erikdarlingdata and others added 29 commits March 20, 2026 17:11
App-side:
- Custom Main() in Program.cs with VelopackApp.Build().Run() before
  WPF starts (proper entry point, resolves Velopack warning)
- Removed StartupUri from App.xaml, MainWindow created in OnStartup
- StartupObject set in csproj
- About window: 3-step update flow (check → download → confirm → restart)
  with MessageBox confirmation before restart
- Startup check: 5-second delay to avoid slowing launch
- Link text shortened to "click to install"

Pipeline:
- Self-contained win-x64 publish for Velopack (separate from existing)
- vpk download for delta generation
- vpk pack producing Setup.exe + full/delta nupkg
- vpk upload with --token to attach artifacts to release
- Existing zip distribution unchanged

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ck-full

Full Velopack auto-update for Dashboard (#635)
Now imports the full config folder from a previous install:
- servers.json (upsert by server name, as before)
- settings.json (alert thresholds, SMTP, theme, MCP settings)
- collection_schedule.json (custom schedules)
- ignored_wait_types.json (custom wait exclusions)
- alert_state.json (dismissed alerts)

Config files are only copied if they don't already exist in the
current install — won't overwrite a user's current settings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rename Import Connections to Import Settings, copy config files
Data directory:
- All data (config, DuckDB, archives, logs, alert state) now stored
  in %LOCALAPPDATA%\PerformanceMonitorLite\ instead of alongside exe
- Fixed hardcoded AppContext.BaseDirectory in AlertStateService and
  ServerTab log viewer
- Enables Velopack to replace app directory without losing data

Velopack integration:
- Custom Main() with VelopackApp.Build().Run() before WPF
- About window: 3-step update flow with confirmation dialog
- Startup check: 5-second delay, title bar notification
- Falls back to browser-based check when no packages exist

Pipeline:
- Self-contained win-x64 publish for Lite
- Separate channels: dashboard + lite (different package IDs)
- vpk download/pack/upload for both apps

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add HAS_DBACCESS('msdb') check to the connectivity probe in ServerManager
- Store HasMsdbAccess on ServerConnectionStatus (defaults to true)
- Gate the running_jobs collector on HasMsdbAccess in IsCollectorSupported()
- Show a warning message on the Running Jobs tab when msdb access is denied

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Active Queries, Query Stats, Procedure Stats, Query Store each get their own slicer:
- Slicer data aggregated by hourly collection_time buckets
- fromSlicer parameter filters by collection_time (not execution time) when slicer is active
- Metric label updates on grid sort (total/avg CPU, duration, reads, writes)
- Selection preserved on auto-refresh
- Grid respects slicer dates during auto-refresh (HasNarrowedSelection check)
- Time display mode (Local/UTC/Server) updates slicer labels
- Proportional time positioning for accurate data-gap representation
- Hour-boundary snapping for accurate filtering
- WAITFOR queries excluded from Active Queries slicer
- TRY_CAST to money for sp_WhoIsActive varchar metric columns

Also bumps Lite slicer height to 130px, Dashboard to 150px.
Fixes proportional time positioning and label spacing in both Lite and Dashboard slicers.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Skip running jobs collector when login lacks msdb access
Audit of Query Trace Patterns tab found CollectionTime was fetched but
not displayed, and nt_user_name was not exposed at all in the drill-down
history window.

Changes:
- Add CollectionTime column to TracePatternHistoryWindow DataGrid
- Add NtUserName (nt_user_name) to SQL query, model, and DataGrid
- Reorder date columns to front: End Time, Collection Time, Start Time
- NT User column placed after Login for logical grouping

Main patterns grid unchanged — nt_user_name excluded there to avoid
breaking the GROUP BY aggregation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…n-audit

Add CollectionTime and NtUserName to trace pattern drill-down (#663)
Velopack auto-update for Lite + data directory migration (#635)
Save SortDescriptions before setting ItemsSource and restore them after,
so user-applied sort order survives refresh cycles in both Dashboard and Lite.

Dashboard: Replace SetInitialSort with SetItemsSourcePreservingSort across
all 7 QueryPerformanceContent DataGrids.

Lite: Add sort preservation to DataGridFilterManager (covers refresh and
filter paths), rename SetInitialSort to SetDefaultSortIfNone which skips
when user sort is already active.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…-refresh

Fix #659: Preserve DataGrid sort order across auto-refresh
CHARINDEX(N' ON ', st.text) searched from the start of the text,
so a comment like "Update Current LoadIndex Id on Machine record"
before CREATE TRIGGER would match first, producing a negative
SUBSTRING length.

Now passes the start position after CREATE TRIGGER as the third
argument to CHARINDEX so it only finds the ON clause after the
trigger name.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix trigger name extraction with ON in comment (#666)
1. FinOps expensive queries query referenced statement_start_offset
   and statement_end_offset columns that don't exist in the DuckDB
   query_stats schema. Removed from GROUP BY — sql_handle + query_text
   is sufficient for grouping.

2. Archive compaction didn't recognize imported_ prefixed parquet
   files from the data import feature. Added regex patterns for
   imported_YYYYMM_tablename and imported_YYYYMMDD_HHMM_tablename.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ries-and-imported-parquet

Fix FinOps expensive queries error + imported parquet warnings
- Parse detail_text to extract Database, Query Text, and Wait Type
  when using 'Mute This Alert' from alert history (both editions)
- Add PopulateFromDetailText() to AlertMuteContext for structured
  field extraction from the label: value format
- Add 'Default expiration for new mute rules' dropdown to Settings
  in both editions (1 hour, 24 hours, 7 days, Never; default 24h)
- MuteRuleDialog now selects the configured default expiration
  instead of always defaulting to 'Never'
- Persist setting as mute_rule_default_expiration in settings.json
  (Lite) and preferences.json (Dashboard)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The XML doc claimed Job Name extraction but the parser did not
implement it. Add the missing branch in both Dashboard and Lite
editions so the behavior matches the documentation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Collapse newlines in Truncate/TruncateText so detail_text fields
  stay single-line in the label: value format
- Handle multi-line query values in PopulateFromDetailText by
  accumulating continuation lines until the next indented field
- Recognize variant query labels (Blocked Query, Blocking Query,
  Victim SQL) in addition to Query

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Explain that the field is a case-insensitive substring match and
suggest entering a distinctive fragment like a table or procedure name.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Servers that upgraded to 2.3.0 before PR #625 shipped don't have
the is_percent_growth, growth_pct, and vlf_count columns. This
re-applies the idempotent ALTER TABLE from the 2.2.0-to-2.3.0
upgrade for those servers.
CLI: treated 1 failure as success in automated mode (intended for
query_snapshots but applied to any failure). Now any failure = not
success.

GUI: had a similar workaround checking for query_snapshots
specifically. Removed — failures should be reported honestly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Release prep: upgrade script + installer success fix
- Changelog for 2.4.0
- README: updated upgrade instructions, data storage location
- Upgrade script 2.3.0-to-2.4.0: re-apply growth/VLF columns
- Fix: installers no longer write SUCCESS when files fail

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@erikdarlingdata erikdarlingdata merged commit 3122122 into main Mar 23, 2026
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants