Skip to content

Release v2.7.0#842

Merged
erikdarlingdata merged 27 commits intomainfrom
dev
Apr 15, 2026
Merged

Release v2.7.0#842
erikdarlingdata merged 27 commits intomainfrom
dev

Conversation

@erikdarlingdata
Copy link
Copy Markdown
Owner

Summary

Added

Changed

Fixed

Test plan

  • Version bumps (4 csproj files)
  • CHANGELOG + README synced
  • Upgrade script audit (no schema changes)
  • Build: 0 errors | Installer.Tests: 46 passed | Lite.Tests: 257 passed
  • Fresh install: sql2016
  • Upgrade: sql2017 (v2.6.0→v2.7.0, data survived)
  • Upgrade: sql2022 (v2.6.0→v2.7.0, data survived, idempotency verified)
  • Multi-hop: v2.4.1→v2.7.0 and v2.5.0→v2.7.0 on sql2019
  • Uninstall: sql2016 (DB/jobs/XE removed, reinstall works)
  • RDS: SQL Server 2022 Standard, db.t3.xlarge — 3+ hours, 4,875 SUCCESS / 0 errors
  • Auto-refresh: ticking every 60s, survives tab switch/close, no legend duplication
  • Overnight soak: clean

🤖 Generated with Claude Code

erikdarlingdata and others added 27 commits April 8, 2026 10:26
gh release create fires both created and published events,
causing two identical builds that conflict on asset uploads.

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

Fix duplicate release builds: trigger on published only
PlanAnalyzer (Dashboard + Lite):
- Rules 3 & 20: SubTreeCost threshold 0.01 → 1.0 (CTFP is integer)
- Rule 3: Smarter MaxDOPSetToOne — 3-branch logic with query text check
- Rule 5: AllocatesResources gate on zero-rows warning (Hash/Sort/Spool)
- Rule 11: Enriched scan-with-predicate (cost %, elapsed %, selectivity)
- Rule 12: IsFunctionOnColumnSide — fix non-SARGable false positive
- Rules 25/31: GetWaitStatsAdvice for targeted parallelism advice
- New Rule 32: Scan cardinality misestimate (actual plans)
- New Rule 33: CE guess detection (estimated plans)

ShowPlanParser (Dashboard + Lite):
- Fix multi-statement batch parsing (iterate all <Statements> children)
- Add synthetic root nodes for DECLARE/ASSIGN statements

Closes #816. Syncs from PerformanceStudio PR #213.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
FilterUpgrades used `FromVersion >= current` which excluded upgrades
when the user was on a patch release (e.g., 2.4.1 skipped the
2.4.0-to-2.5.0 upgrade because 2.4.0 < 2.4.1). Changed to
`ToVersion > current` so any upgrade the user hasn't reached is
included. Adds regression test for the patch version scenario.

Fixes #817

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When no new deadlocks occurred for a database, the MERGE source was
empty and no row was created, leaving stale delta values in the last
row. Expanded the MERGE source with a combined_source CTE that carries
forward databases from the previous collection with zero counts, so
the delta calculation properly resets to 0.

Fixes #803

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
#821)

Adds an opt-in checkbox in both Dashboard and Lite Add/Edit Server
dialogs that sets MultiSubnetFailover=true on the connection string.
Recommended for AG listeners and FCIs spanning multiple subnets —
connects to all IPs in parallel during failover instead of sequentially.

Defaults to off; persists to servers.json; backward compatible with
existing configs.

Fixes #813

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…822)

The installer now checks for pre-downloaded SQL files in a community/
directory before downloading from GitHub. Users in air-gapped
environments can place the files there manually. If a file is missing,
it falls back to the normal GitHub download.

Expected files:
- community/sp_WhoIsActive.sql
- community/DarlingData.sql
- community/Install-All-Scripts.sql

Fixes #814

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…823)

Queries sys.dm_os_host_info (SQL 2017+) for the host_distribution
value. Falls back to parsing @@Version for SQL 2016 and Azure SQL DB.
No schema changes — the data is queried live alongside existing server
properties.

Fixes #748

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…s to Logical CPUs

The timer tick handler set _isRefreshing = true before calling RefreshAllDataAsync,
which also checked _isRefreshing and returned immediately — every auto-refresh was a no-op.
Removed the redundant guard from the tick handler so RefreshAllDataAsync owns the flag.

Renamed "CPUs" to "Logical CPUs" in the FinOps server inventory tab (both Dashboard and Lite)
to clarify the relationship with the Cores/Socket column, which shows physical cores.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…s to Logical CPUs (#825)

The timer tick handler set _isRefreshing = true before calling RefreshAllDataAsync,
which also checked _isRefreshing and returned immediately — every auto-refresh was a no-op.
Removed the redundant guard from the tick handler so RefreshAllDataAsync owns the flag.

Renamed "CPUs" to "Logical CPUs" in the FinOps server inventory tab (both Dashboard and Lite)
to clarify the relationship with the Cores/Socket column, which shows physical cores.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…indow

Replace the connection-success MessageBox popup with inline status in the
DatabaseStatusPanel so the entire flow (connection result, database detection,
install prompt) reads naturally in one place. Add guidance text explaining
what Install Now does for first-time users.

Size the dialog to the screen work area height and top-align it so the
post-test content is never hidden below the fold.

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

01_widen_version_columns.sql (PR #722) was missing USE PerformanceMonitor and
SET options that every other upgrade script has. The installer connects to master,
so the script's OBJECT_ID check resolved against master, found nothing, skipped
both ALTERs, and reported success while leaving columns at nvarchar(255).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds _template.sql with the required SET options and USE PerformanceMonitor
preamble so future upgrade scripts don't repeat the #828 mistake. Updates
the README example to match the actual pattern used by all upgrade scripts.

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

Fix upgrade script executing against master instead of PerformanceMonitor
The three heaviest MCP tools (get_top_queries_by_cpu, get_top_procedures_by_cpu,
get_query_store_top) timed out on large databases because they scanned every row,
decompressed text on every row, and returned all results for client-side filtering.

Dashboard: add new MCP-specific service methods using multi-phase temp tables —
aggregate numerics first, rank TOP N, then hydrate text via OUTER APPLY for only
the winners. DECOMPRESS now runs on ~N rows instead of the entire table.

Lite: rewrite queries with CTE-based phasing — inner CTE aggregates numeric-only
columns with LIMIT, outer query hydrates text via LEFT JOIN LATERAL for winners.

Both: push database_name filter and TOP N into SQL instead of client-side filtering.
Shared UI methods are untouched.

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

Optimize MCP query tools for large databases
…ataAsync (#833)

Same class of bug as Lite (#824) — the timer tick handlers each managed
the _isRefreshing flag redundantly. On a transient connection failure,
MessageBox.Show blocked the UI thread while _isRefreshing remained true,
causing every subsequent tick to skip silently.

- Move _isRefreshing ownership into LoadDataAsync (matches Lite pattern)
- Remove redundant guard from all 3 timer tick handlers
- Suppress MessageBox during auto-refresh (fullRefresh: false) — log
  errors and update status bar instead of blocking the UI thread

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…#834)

The _isRefreshing guard in LoadDataAsync could get permanently stuck if
any SQL query hung (connection drop, slow timeout), silently killing all
subsequent auto-refresh ticks. Replaced with a stop/start pattern: the
timer stops before each tick and restarts in finally, bypassing
LoadDataAsync entirely for auto-refresh. This guarantees the timer always
restarts regardless of what happens during the refresh.

Also removed the redundant _isRefreshing guard from
CorrelatedTimelineLanesControl.RefreshAsync which was independently
causing Server Trends charts to silently skip updates.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… on tab close (#835)

Dashboard had the same memory leak patterns fixed in Lite (commit 2b61592)
but never received those fixes. When server tabs were closed, 53+ event
handlers and hover helpers remained subscribed, preventing GC from
collecting the tab and its child controls.

- Add Dispose() to Dashboard ChartHoverHelper (matches Lite)
- Add DisposeChartHelpers() to ServerTab, MemoryContent, ResourceMetricsContent,
  and QueryPerformanceContent
- Store lambda event delegates as named fields so they can be unsubscribed
- Unsubscribe all child control events in ServerTab_Unloaded (slicers,
  CriticalIssuesTab, MemoryTab, PerformanceTab, ResourceMetricsContent)
- Store and unsubscribe AlertAcknowledged handler in MainWindow.CloseTab_Click
- Clear unfiltered data collections, filter dictionaries, legend panels,
  and baseline provider cache on tab close

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

Fix Dashboard memory leaks on tab close (#835)
- Bump version to 2.7.0 in all 4 csproj files
- Add CHANGELOG entry for v2.7.0 (features, changes, fixes)
- Update README: MultiSubnetFailover AG note, air-gapped community scripts
- Replace DispatcherTimer auto-refresh with async Task.Delay loop to
  prevent priority starvation under heavy UI load (chart rendering)

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

Replace DispatcherTimer with async Task.Delay loop to prevent priority
starvation under heavy UI load. Don't cancel the loop on Unloaded (WPF
fires Unloaded on tab switch/close, not just control destruction). Catch
OperationCanceledException from SQL queries without killing the loop.
Skip auto-refresh ticks while a full refresh is in progress to prevent
concurrent chart rendering that duplicates legends.

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

Fix Dashboard auto-refresh: async loop, tab close, legend duplication
…leanupOnClose

WPF fires Unloaded on tab switch, not just destruction. The old handler
tore down legend tracking, chart helpers, and event subscriptions on
every tab switch, causing the auto-refresh loop to lose track of legend
panels and create duplicates. Now Unloaded is a no-op; full cleanup
only runs via CleanupOnClose when a tab is actually closed/removed.

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

Fix legend duplication on tab switch
@erikdarlingdata erikdarlingdata merged commit 82dc799 into main Apr 15, 2026
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.

1 participant