Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
61f5a3b
Implements #843 in Lite
ClaudioESSilva Apr 15, 2026
d6b49a5
Implements #843 for Full Dashboard
ClaudioESSilva Apr 15, 2026
3fcbc61
Merge pull request #844 from ClaudioESSilva/feature/auto-scrolling
erikdarlingdata Apr 15, 2026
214b64e
Add trailing newlines to ScrollPanBehavior files
erikdarlingdata Apr 15, 2026
3c98ccb
Merge pull request #845 from erikdarlingdata/fix/trailing-newlines
erikdarlingdata Apr 15, 2026
ccf82aa
Harden DuckDB queries: parameterize values, escape paths, fix IsArchi…
erikdarlingdata Apr 15, 2026
f9be94c
Merge pull request #850 from erikdarlingdata/fix/duckdb-security-hard…
erikdarlingdata Apr 15, 2026
c11608d
Encrypt webhook URLs with DPAPI via Windows Credential Manager
erikdarlingdata Apr 15, 2026
32f0154
Merge pull request #851 from erikdarlingdata/fix/webhook-url-encryption
erikdarlingdata Apr 15, 2026
523a91c
Lazy-load server tabs: only load visible tab on open, full-load on fi…
erikdarlingdata Apr 16, 2026
3db895c
Cap query/procedure/query store grid results to TOP 500
erikdarlingdata Apr 16, 2026
a88572c
Merge pull request #852 from erikdarlingdata/fix/lazy-load-server-tabs
erikdarlingdata Apr 16, 2026
50d10f5
Remove pointless WAITFOR DECOMPRESS filters from stats/store queries
erikdarlingdata Apr 16, 2026
160921f
Refactor query/procedure/query store stats to phased DECOMPRESS approach
erikdarlingdata Apr 16, 2026
29e561c
Merge pull request #853 from erikdarlingdata/fix/remove-waitfor-decom…
erikdarlingdata Apr 16, 2026
4235f50
Fix FinOps TDE recommendation on SQL Server 2019+ (#854)
erikdarlingdata Apr 16, 2026
2b67f09
Merge pull request #855 from erikdarlingdata/fix/854-finops-tde-versi…
erikdarlingdata Apr 16, 2026
e668b76
Sync PlanAnalyzer and BenefitScorer from PerformanceStudio (Apr 9-16)
erikdarlingdata Apr 16, 2026
41f182f
Merge pull request #856 from erikdarlingdata/sync/planalyzer-from-ps-…
erikdarlingdata Apr 16, 2026
3e950a5
Fall back to single-database mode when Azure master is inaccessible (…
erikdarlingdata Apr 17, 2026
d5f586c
Merge pull request #858 from erikdarlingdata/fix/857-azure-master-fal…
erikdarlingdata Apr 17, 2026
694ead6
Add nonclustered indexes for query/procedure/query store lookups
erikdarlingdata Apr 17, 2026
a484e6e
Merge pull request #859 from erikdarlingdata/fix/835-query-lookup-ind…
erikdarlingdata Apr 17, 2026
a5829ba
Scope query snapshots to current database on Azure SQL DB (#857)
erikdarlingdata Apr 18, 2026
e41c960
Merge pull request #861 from erikdarlingdata/fix/857-live-snapshot-az…
erikdarlingdata Apr 18, 2026
b815472
Polish Lite chart axes and sub-tab styling
erikdarlingdata Apr 18, 2026
333a8f7
Merge pull request #862 from erikdarlingdata/ui/lite-chart-and-tab-po…
erikdarlingdata Apr 18, 2026
64ddd01
Port Lite chart/tab polish to Dashboard + LSP diagnostics cleanup
erikdarlingdata Apr 19, 2026
02db6b5
Merge pull request #863 from erikdarlingdata/chore/lsp-diagnostics-cl…
erikdarlingdata Apr 19, 2026
f1c8160
Fix Overview crosshair disappearing after tab switches / layout passes
erikdarlingdata Apr 19, 2026
d319039
Merge pull request #864 from erikdarlingdata/fix/overview-crosshair-s…
erikdarlingdata Apr 19, 2026
f014bf8
Fix Memory Pressure Events chart filter; add MCP interpretation (#865)
erikdarlingdata Apr 19, 2026
b86250f
Port Memory Pressure Events feature to Lite (#865)
erikdarlingdata Apr 19, 2026
04d2e24
Bump schema table count test to 30 for memory_pressure_events
erikdarlingdata Apr 19, 2026
641e59f
Merge pull request #866 from erikdarlingdata/fix/865-memory-pressure-…
erikdarlingdata Apr 19, 2026
985da4a
Fix blocked process report plan lookup (#867) (#868)
erikdarlingdata Apr 20, 2026
2958c0c
Pre-filter query snapshot requests into #temp on Azure SQL DB (#857) …
erikdarlingdata Apr 20, 2026
4cebae2
Stop retrying collectors after non-transient permission denial (#857)…
erikdarlingdata Apr 20, 2026
3327c91
Skip live query plans on Azure SQL DB (#857) (#871)
erikdarlingdata Apr 21, 2026
2c1ccbe
Fix FinOps recommendation severity sort order (#872)
erikdarlingdata Apr 21, 2026
08c68c7
Merge pull request #874 from erikdarlingdata/fix/872-finops-severity-…
erikdarlingdata Apr 21, 2026
eb5d326
Fix FinOps severity sort order in Dashboard (#872)
erikdarlingdata Apr 21, 2026
12d4e34
Merge pull request #875 from erikdarlingdata/fix/872-dashboard-finops…
erikdarlingdata Apr 21, 2026
95bca55
Drop sys.dm_os_schedulers from memory_stats on Azure SQL DB (#857) (#…
erikdarlingdata Apr 21, 2026
791f107
Release v2.8.0: version bumps and changelog (#877)
erikdarlingdata Apr 22, 2026
46a1285
Scope v2.8.0 webhook DPAPI note to Dashboard (#879)
erikdarlingdata Apr 22, 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
43 changes: 43 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,49 @@ 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.8.0] - TBD

### Important

- **New nonclustered indexes** on `collect.query_stats`, `collect.procedure_stats`, and `collect.query_store_data` to eliminate Eager Index Spools in Dashboard grid queries. On large installations these indexes may take several minutes to build; the upgrade script uses `ONLINE = ON` on Enterprise/Developer/Azure editions and falls back to offline on Standard/Web ([#835])

### Added

- **Memory Pressure Events in Lite** — the collector, chart, and `get_memory_pressure_events` MCP tool previously only in the Full Edition are now available in Lite ([#865])
- **Grid auto-scrolling** in Lite and Dashboard ([#843]) — thanks [@ClaudioESSilva](https://github.com/ClaudioESSilva)

### Changed

- **PlanAnalyzer and BenefitScorer** synced with PerformanceStudio's Apr 9–16 improvements
- **Query/Procedure/Query Store stats** refactored to a phased DECOMPRESS approach; removed unhelpful `WAITFOR DECOMPRESS` filters
- **Query/Procedure/Query Store grids** capped to TOP 500 to prevent UI freezes on large datasets
- **Server tabs lazy-load** — only the visible server tab loads on startup; remaining tabs load on first visit
- **Webhook URLs (Dashboard)** encrypted with DPAPI via Windows Credential Manager — Lite webhook URLs remain in plaintext settings for now
- **DuckDB queries hardened** — parameterized values, escaped paths, fixed `IsArchiving` race
- **Lite chart axes and sub-tab styling** polished, then ported to Dashboard

### Fixed

- **Memory Pressure Events chart filter** was dropping valid rows; added MCP interpretation guidance ([#865])
- **FinOps recommendation severity sort order** in Lite and Dashboard ([#872])
- **Overview crosshair** disappearing after tab switches or layout passes
- **Blocked process report plan lookup** returning the wrong plan ([#867])
- **FinOps TDE recommendation** flagging Standard edition on SQL Server 2019+ where TDE is free ([#854])
- **Azure SQL DB collector** falls back to single-database mode when `master` is inaccessible ([#857])
- **Azure SQL DB query snapshots** scoped to the current database ([#857])
- **Azure SQL DB query snapshot prefilter** — request set is narrowed into `#temp` before joining DMVs to avoid Azure-specific execution plan issues ([#857])
- **Azure SQL DB live query plans** — now skipped gracefully instead of erroring ([#857])
- **Azure SQL DB memory_stats collector** — dropped `sys.dm_os_schedulers` which is blocked on elastic-pool contained users regardless of DB-scoped grants ([#857])
- **Non-transient permission denials** now stop collector retries instead of looping forever ([#857])

[#835]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/835
[#843]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/843
[#854]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/854
[#857]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/857
[#865]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/865
[#867]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/867
[#872]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/872

## [2.7.0] - 2026-04-13

### Added
Expand Down
2 changes: 1 addition & 1 deletion Dashboard/Analysis/SqlServerBaselineProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ private static double PoolVariance(List<BaselineBucket> buckets, double grandMea
return totalSumSq / (totalSamples - 1);
}

private class CachedBaseline
private sealed class CachedBaseline
{
public DateTime ComputedAt { get; init; }
public DateTime RealTime { get; init; }
Expand Down
2 changes: 1 addition & 1 deletion Dashboard/Controls/ConfigChangesContent.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<MenuItem Header="Export to CSV..." Click="ExportToCsv_Click"/>
</ContextMenu>
</UserControl.Resources>
<TabControl>
<TabControl ItemContainerStyle="{DynamicResource SubTabItemStyle}">
<!-- Server Configuration Changes Sub-Tab -->
<TabItem Header="Server Config Changes">
<Grid>
Expand Down
24 changes: 21 additions & 3 deletions Dashboard/Controls/CorrelatedTimelineLanesControl.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@ public partial class CorrelatedTimelineLanesControl : UserControl
public CorrelatedTimelineLanesControl()
{
InitializeComponent();
Unloaded += (_, _) => _crosshairManager?.Dispose();
/* No Unloaded → Dispose() handler: WPF fires Unloaded for transient
reasons (tab virtualization, layout rebuilds) and Dispose() clears
the crosshair manager's lane list, permanently breaking the crosshair
until the ServerTab is rebuilt. The manager holds only managed state
(a Popup + lane references) — letting GC clean it up with the control
is fine. */
}

/// <summary>
Expand Down Expand Up @@ -69,6 +74,9 @@ public async Task RefreshAsync(int hoursBack, DateTime? fromDate, DateTime? toDa

_crosshairManager?.PrepareForRefresh();

try
{

var cpuTask = _dataService.GetCpuUtilizationAsync(hoursBack, fromDate, toDate);
var waitTask = _dataService.GetTotalWaitStatsTrendAsync(hoursBack, fromDate, toDate);
var blockingTask = _dataService.GetBlockedSessionTrendAsync(hoursBack, fromDate, toDate);
Expand Down Expand Up @@ -225,8 +233,18 @@ public async Task RefreshAsync(int hoursBack, DateTime? fromDate, DateTime? toDa
_crosshairManager?.SetComparisonLabel(ComparisonLabel(comparisonRange.Value, fromDate, hoursBack));
}

/* VLines must be re-attached before SyncXAxes so they're part of
the render set when the chart refreshes. */
_crosshairManager?.ReattachVLines();
SyncXAxes(hoursBack, fromDate, toDate);
}
finally
{
/* Safety net: if something threw between PrepareForRefresh() and the
ReattachVLines() call above, VLines are still null. EnsureVLinesAttached
creates them only for lanes where VLine is null, so it's idempotent. */
_crosshairManager?.EnsureVLinesAttached();
}
}

/// <summary>
Expand Down Expand Up @@ -320,7 +338,7 @@ private void UpdateBlockingLane(List<(double Time, double Value)> blockingData,
}
}

BlockingChart.Plot.Axes.DateTimeTicksBottom();
BlockingChart.Plot.Axes.DateTimeTicksBottomDateChange();
BlockingChart.Plot.Axes.Bottom.TickLabelStyle.IsVisible = false;
TabHelpers.ReapplyAxisColors(BlockingChart);

Expand Down Expand Up @@ -394,7 +412,7 @@ private void UpdateLane(ScottPlot.WPF.WpfPlot chart, string title,

_crosshairManager?.SetLaneData(chart, times, values);

chart.Plot.Axes.DateTimeTicksBottom();
chart.Plot.Axes.DateTimeTicksBottomDateChange();
if (chart != FileIoChart)
chart.Plot.Axes.Bottom.TickLabelStyle.IsVisible = false;

Expand Down
2 changes: 1 addition & 1 deletion Dashboard/Controls/CurrentConfigContent.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<MenuItem Header="Export to CSV..." Click="ExportToCsv_Click"/>
</ContextMenu>
</UserControl.Resources>
<TabControl>
<TabControl ItemContainerStyle="{DynamicResource SubTabItemStyle}">
<!-- Current Server Configuration Sub-Tab -->
<TabItem Header="Server Configuration">
<Grid>
Expand Down
4 changes: 2 additions & 2 deletions Dashboard/Controls/FinOpsContent.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
SelectionChanged="ServerSelector_SelectionChanged"/>
</StackPanel>

<TabControl Grid.Row="1" Background="Transparent" BorderThickness="0">
<TabControl Grid.Row="1" Background="Transparent" BorderThickness="0" ItemContainerStyle="{DynamicResource SubTabItemStyle}">

<!-- Recommendations Sub-Tab -->
<TabItem Header="Recommendations">
Expand Down Expand Up @@ -90,7 +90,7 @@
</StackPanel>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Severity}" MinWidth="80" Width="Auto">
<DataGridTextColumn Binding="{Binding Severity}" SortMemberPath="SeveritySort" MinWidth="80" Width="Auto">
<DataGridTextColumn.Header>
<StackPanel Orientation="Horizontal">
<Button Style="{DynamicResource ColumnFilterButtonStyle}" Tag="Severity" Click="FinOpsFilter_Click" Margin="0,0,4,0"/>
Expand Down
2 changes: 1 addition & 1 deletion Dashboard/Controls/MemoryContent.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
</ResourceDictionary>
</UserControl.Resources>

<TabControl x:Name="SubTabControl" TabStripPlacement="Top" Margin="0,5,0,0">
<TabControl x:Name="SubTabControl" TabStripPlacement="Top" Margin="0,5,0,0" ItemContainerStyle="{DynamicResource SubTabItemStyle}">
<!-- Memory Overview Sub-Tab -->
<TabItem Header="Memory Overview">
<!-- Chart + Summary Panel (grid removed) -->
Expand Down
Loading
Loading