Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
91b656b
Fix duplicate release builds: trigger on published only
erikdarlingdata Apr 8, 2026
f8033bf
Merge pull request #811 from erikdarlingdata/fix/release-duplicate-tr…
erikdarlingdata Apr 8, 2026
19430a0
Sync PlanAnalyzer + ShowPlanParser from PerformanceStudio (#816)
erikdarlingdata Apr 9, 2026
a25c90b
Fix upgrade filter skipping patch versions (#817) (#819)
erikdarlingdata Apr 9, 2026
b4fe866
Fix deadlock count not resetting between collections (#803) (#820)
erikdarlingdata Apr 9, 2026
f302205
Add MultiSubnetFailover connection option to Dashboard and Lite (#813…
erikdarlingdata Apr 9, 2026
f554d3b
Add offline community script support via community/ directory (#814) …
erikdarlingdata Apr 9, 2026
31f97ee
Add Host OS column to Server Inventory in Dashboard and Lite (#748) (…
erikdarlingdata Apr 9, 2026
f431846
Fix Lite auto-refresh silently skipping every tick (#824), rename CPU…
erikdarlingdata Apr 10, 2026
822cba0
Fix Lite auto-refresh silently skipping every tick (#824), rename CPU…
erikdarlingdata Apr 10, 2026
cfdc90f
Improve Add Server dialog UX: inline connection status, full-height w…
erikdarlingdata Apr 10, 2026
6fa4d05
Merge pull request #827 from erikdarlingdata/improve-add-server-dialo…
erikdarlingdata Apr 10, 2026
1ca4a57
Fix upgrade script executing against master instead of PerformanceMon…
erikdarlingdata Apr 11, 2026
0914b0e
Add upgrade script template and update README guidelines
erikdarlingdata Apr 11, 2026
bffd7a7
Merge pull request #829 from erikdarlingdata/fix/upgrade-script-missi…
erikdarlingdata Apr 11, 2026
d15e948
Optimize MCP query tools for large databases (#826)
erikdarlingdata Apr 11, 2026
564773a
Merge pull request #830 from erikdarlingdata/feature/mcp-query-optimi…
erikdarlingdata Apr 11, 2026
ef8435a
Fix Dashboard auto-refresh stall: move _isRefreshing guard into LoadD…
erikdarlingdata Apr 12, 2026
64a6247
Fix Dashboard auto-refresh stalling by using stop/start timer pattern…
erikdarlingdata Apr 12, 2026
e41992a
Fix Dashboard memory leaks: dispose chart helpers, unsubscribe events…
erikdarlingdata Apr 14, 2026
33ae14b
Merge pull request #836 from erikdarlingdata/fix/835-dashboard-memory…
erikdarlingdata Apr 14, 2026
87cdbaf
Release v2.7.0: version bumps, changelog, auto-refresh fix
erikdarlingdata Apr 14, 2026
a2c0b93
Merge pull request #838 from erikdarlingdata/release/v2.7.0
erikdarlingdata Apr 14, 2026
cb5bc9a
Fix Dashboard auto-refresh: async loop, survive tab close, prevent le…
erikdarlingdata Apr 14, 2026
58368c6
Merge pull request #839 from erikdarlingdata/fix/dashboard-autorefres…
erikdarlingdata Apr 14, 2026
1e40ef3
Fix legend duplication on tab switch: move cleanup from Unloaded to C…
erikdarlingdata Apr 15, 2026
980e9bd
Merge pull request #841 from erikdarlingdata/fix/dashboard-unloaded-c…
erikdarlingdata Apr 15, 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
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
pull_request:
branches: [main, dev]
release:
types: [created, published]
types: [published]

permissions:
contents: write
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,6 @@ Lite/collection_schedule.json

# Plans directory
plans/

# Community scripts (user-provided, not bundled)
community/*.sql
42 changes: 42 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,48 @@ 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.7.0] - 2026-04-13

### Added

- **Host OS column** in Server Inventory for both Dashboard and Lite ([#748], [#823])
- **Offline community script support** via `community/` directory for user-contributed scripts ([#814], [#822])
- **MultiSubnetFailover connection option** in Dashboard and Lite for Always On availability groups ([#813], [#821])

### Changed

- **PlanAnalyzer and ShowPlanParser** synced from PerformanceStudio with latest improvements ([#816])
- **MCP query tools** optimized for large databases ([#826])
- **Add Server dialog UX** improved with inline connection status and full-height window
- **"CPUs" renamed to "Logical CPUs"** for clarity in Lite ([#825])

### Fixed

- **Dashboard auto-refresh stalling under load** — replaced DispatcherTimer with async Task.Delay loop to prevent priority starvation during heavy chart rendering ([#833], [#834])
- **Lite auto-refresh silently skipping** every tick ([#824])
- **Deadlock count not resetting** between collections ([#803], [#820])
- **Upgrade filter skipping patch versions** during version comparison ([#817], [#819])
- **Upgrade script executing against master** instead of PerformanceMonitor database ([#828])
- **Duplicate release builds** triggering on both created and published events

[#748]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/748
[#803]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/803
[#813]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/813
[#814]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/814
[#816]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/816
[#817]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/817
[#819]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/819
[#820]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/820
[#821]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/821
[#822]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/822
[#823]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/823
[#824]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/824
[#825]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/825
[#826]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/826
[#828]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/828
[#833]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/833
[#834]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/834

## [2.6.0] - 2026-04-08

### Added
Expand Down
13 changes: 11 additions & 2 deletions Dashboard/AddServerDialog.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Add SQL Server"
Height="750" Width="500"
WindowStartupLocation="CenterOwner"
Width="500"
WindowStartupLocation="Manual"
ResizeMode="CanResizeWithGrip"
Background="{DynamicResource BackgroundBrush}"
Foreground="{DynamicResource ForegroundBrush}">
Expand Down Expand Up @@ -111,6 +111,12 @@
Foreground="{DynamicResource ForegroundBrush}" Margin="0,0,0,6"
ToolTip="Sets ApplicationIntent=ReadOnly. Required when connecting via an AG listener or Azure failover group endpoint to route to a readable secondary."/>

<!-- Multi-Subnet Failover -->
<CheckBox x:Name="MultiSubnetFailoverCheckBox"
Content="Multi-subnet failover (for AG listeners and FCIs)"
Foreground="{DynamicResource ForegroundBrush}" Margin="0,0,0,6"
ToolTip="Sets MultiSubnetFailover=true. Connects to all IPs in parallel during failover. Recommended for AG listeners and failover cluster instances spanning multiple subnets."/>

<!-- Is Favorite -->
<CheckBox x:Name="IsFavoriteCheckBox" Content="Mark as favorite (appears at top of list)"
Foreground="{DynamicResource ForegroundBrush}" Margin="0,0,0,6"/>
Expand All @@ -136,6 +142,9 @@
Background="{DynamicResource BackgroundLightBrush}"
CornerRadius="4" Padding="12" Margin="0,0,0,10">
<StackPanel>
<TextBlock x:Name="ConnectionInfoText" TextWrapping="Wrap" Margin="0,0,0,4"
FontWeight="SemiBold"
Foreground="{DynamicResource ForegroundBrush}"/>
<TextBlock x:Name="DatabaseStatusText" TextWrapping="Wrap" Margin="0,0,0,8"
Foreground="{DynamicResource ForegroundBrush}"/>
<StackPanel Orientation="Horizontal" Margin="0,0,0,4">
Expand Down
53 changes: 39 additions & 14 deletions Dashboard/AddServerDialog.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,12 @@ private enum DialogState
private InstallationResult? _installResult;
private string? _reportPath;
private DialogState _currentState = DialogState.Initial;
private string? _serverVersion;

public AddServerDialog()
{
InitializeComponent();
SizeToWorkArea();
_isEditMode = false;
ServerConnection = new ServerConnection();
Title = "Add SQL Server";
Expand All @@ -59,6 +61,7 @@ public AddServerDialog()
public AddServerDialog(ServerConnection existingServer)
{
InitializeComponent();
SizeToWorkArea();
_isEditMode = true;
ServerConnection = existingServer;
Title = "Edit SQL Server";
Expand All @@ -78,6 +81,7 @@ public AddServerDialog(ServerConnection existingServer)
};
TrustServerCertificateCheckBox.IsChecked = existingServer.TrustServerCertificate;
ReadOnlyIntentCheckBox.IsChecked = existingServer.ReadOnlyIntent;
MultiSubnetFailoverCheckBox.IsChecked = existingServer.MultiSubnetFailover;

if (existingServer.AuthenticationType == AuthenticationTypes.EntraMFA)
{
Expand Down Expand Up @@ -108,6 +112,14 @@ public AddServerDialog(ServerConnection existingServer)
}
}

private void SizeToWorkArea()
{
var workArea = SystemParameters.WorkArea;
Height = workArea.Height;
Top = workArea.Top;
Left = workArea.Left + (workArea.Width - Width) / 2;
}

private void AuthType_Changed(object sender, RoutedEventArgs e)
{
if (SqlAuthPanel != null && EntraMfaPanel != null)
Expand Down Expand Up @@ -154,7 +166,8 @@ private SqlConnectionStringBuilder BuildConnectionBuilder()
Encrypt = ParseEncryptOption(GetSelectedEncryptMode()),
ApplicationIntent = ReadOnlyIntentCheckBox.IsChecked == true
? ApplicationIntent.ReadOnly
: ApplicationIntent.ReadWrite
: ApplicationIntent.ReadWrite,
MultiSubnetFailover = MultiSubnetFailoverCheckBox.IsChecked == true
};

if (WindowsAuthRadio.IsChecked == true)
Expand Down Expand Up @@ -339,17 +352,9 @@ private async void TestConnection_Click(object sender, RoutedEventArgs e)

if (connected)
{
var message = serverVersion != null
? $"Successfully connected to {ServerNameTextBox.Text}!\n\n{serverVersion}"
: $"Successfully connected to {ServerNameTextBox.Text}!";
MessageBox.Show(
message,
"Connection Successful",
MessageBoxButton.OK,
MessageBoxImage.Information
);
_serverVersion = serverVersion;

/* After successful connection, check database status */
/* Show connection + database status inline instead of a popup */
await DetectDatabaseStatusAsync();
}
else if (mfaCancelled)
Expand Down Expand Up @@ -400,6 +405,10 @@ private async System.Threading.Tasks.Task DetectDatabaseStatusAsync()

if (!_coreServerInfo.IsSupportedVersion)
{
string serverName = ServerNameTextBox.Text;
ConnectionInfoText.Text = _serverVersion != null
? $"Connected to {serverName} ({_serverVersion})"
: $"Connected to {serverName}";
DatabaseStatusText.Text = $"Warning: {_coreServerInfo.ProductMajorVersionName} is not supported. SQL Server 2016+ is required.";
DatabaseStatusPanel.Visibility = Visibility.Visible;
InstallUpgradeButton.Visibility = Visibility.Collapsed;
Expand Down Expand Up @@ -458,13 +467,22 @@ private void TransitionToState(DialogState newState)
ViewReportButton.Visibility = Visibility.Collapsed;
StatusText.Text = string.Empty;
StatusText.Visibility = Visibility.Collapsed;
ConnectionInfoText.Text = string.Empty;
InstallUpgradeButton.Visibility = Visibility.Visible;
SkipInstallText.Visibility = Visibility.Visible;

/* Build the connection header shown for all connected states */
string serverName = ServerNameTextBox.Text;
string connectionHeader = _serverVersion != null
? $"Connected to {serverName} ({_serverVersion})"
: $"Connected to {serverName}";

switch (newState)
{
case DialogState.Connected_NoDatabase:
DatabaseStatusText.Text = $"No PerformanceMonitor database found on this server. Install v{appVersion}?";
ConnectionInfoText.Text = connectionHeader;
DatabaseStatusText.Text = "No PerformanceMonitor database found on this server. " +
$"Click Install Now to create the monitoring database, collection jobs, and stored procedures (v{appVersion}).";
InstallUpgradeButton.Content = "Install Now";
DatabaseStatusPanel.Visibility = Visibility.Visible;
InstallationPanel.Visibility = Visibility.Visible;
Expand All @@ -473,7 +491,9 @@ private void TransitionToState(DialogState newState)

case DialogState.Connected_NeedsUpgrade:
string normalizedInstalled = NormalizeVersion(_installedVersion!);
DatabaseStatusText.Text = $"v{normalizedInstalled} installed — v{appVersion} available";
ConnectionInfoText.Text = connectionHeader;
DatabaseStatusText.Text = $"PerformanceMonitor v{normalizedInstalled} is installed. " +
$"v{appVersion} is available — click Upgrade Now to apply the update.";
InstallUpgradeButton.Content = "Upgrade Now";
DatabaseStatusPanel.Visibility = Visibility.Visible;
InstallationPanel.Visibility = Visibility.Visible;
Expand All @@ -482,6 +502,7 @@ private void TransitionToState(DialogState newState)

case DialogState.Connected_Current:
string normalizedCurrent = NormalizeVersion(_installedVersion!);
ConnectionInfoText.Text = connectionHeader;
DatabaseStatusText.Text = $"PerformanceMonitor v{normalizedCurrent} is up to date.";
InstallUpgradeButton.Visibility = Visibility.Collapsed;
SkipInstallText.Visibility = Visibility.Collapsed;
Expand Down Expand Up @@ -616,7 +637,8 @@ private async void InstallOrUpgrade_Click(object sender, RoutedEventArgs e)
preValidationAction = async () =>
{
AppendInstallLog("Installing community dependencies...", "Info");
using var depInstaller = new DependencyInstaller();
string communityDir = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "community");
using var depInstaller = new DependencyInstaller(communityDir);
await depInstaller.InstallDependenciesAsync(installerConnStr, progress, cancellationToken);
};
}
Expand Down Expand Up @@ -821,6 +843,7 @@ private void SetFormEnabled(bool enabled)
EncryptModeComboBox.IsEnabled = enabled;
TrustServerCertificateCheckBox.IsEnabled = enabled;
ReadOnlyIntentCheckBox.IsEnabled = enabled;
MultiSubnetFailoverCheckBox.IsEnabled = enabled;
IsFavoriteCheckBox.IsEnabled = enabled;
MonthlyCostTextBox.IsEnabled = enabled;
DescriptionTextBox.IsEnabled = enabled;
Expand Down Expand Up @@ -915,6 +938,7 @@ private async void Save_Click(object sender, RoutedEventArgs e)
ServerConnection.EncryptMode = GetSelectedEncryptMode();
ServerConnection.TrustServerCertificate = TrustServerCertificateCheckBox.IsChecked == true;
ServerConnection.ReadOnlyIntent = ReadOnlyIntentCheckBox.IsChecked == true;
ServerConnection.MultiSubnetFailover = MultiSubnetFailoverCheckBox.IsChecked == true;
if (decimal.TryParse(MonthlyCostTextBox.Text, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out var editCost) && editCost >= 0)
ServerConnection.MonthlyCostUsd = editCost;
}
Expand All @@ -936,6 +960,7 @@ private async void Save_Click(object sender, RoutedEventArgs e)
EncryptMode = GetSelectedEncryptMode(),
TrustServerCertificate = TrustServerCertificateCheckBox.IsChecked == true,
ReadOnlyIntent = ReadOnlyIntentCheckBox.IsChecked == true,
MultiSubnetFailover = MultiSubnetFailoverCheckBox.IsChecked == true,
MonthlyCostUsd = monthlyCost
};
}
Expand Down
Loading
Loading