Skip to content

Commit 82dc799

Browse files
Merge pull request #842 from erikdarlingdata/dev
Release v2.7.0
2 parents cb47ba2 + 980e9bd commit 82dc799

46 files changed

Lines changed: 2164 additions & 495 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ on:
66
pull_request:
77
branches: [main, dev]
88
release:
9-
types: [created, published]
9+
types: [published]
1010

1111
permissions:
1212
contents: write

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,6 @@ Lite/collection_schedule.json
5959

6060
# Plans directory
6161
plans/
62+
63+
# Community scripts (user-provided, not bundled)
64+
community/*.sql

CHANGELOG.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,48 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [2.7.0] - 2026-04-13
9+
10+
### Added
11+
12+
- **Host OS column** in Server Inventory for both Dashboard and Lite ([#748], [#823])
13+
- **Offline community script support** via `community/` directory for user-contributed scripts ([#814], [#822])
14+
- **MultiSubnetFailover connection option** in Dashboard and Lite for Always On availability groups ([#813], [#821])
15+
16+
### Changed
17+
18+
- **PlanAnalyzer and ShowPlanParser** synced from PerformanceStudio with latest improvements ([#816])
19+
- **MCP query tools** optimized for large databases ([#826])
20+
- **Add Server dialog UX** improved with inline connection status and full-height window
21+
- **"CPUs" renamed to "Logical CPUs"** for clarity in Lite ([#825])
22+
23+
### Fixed
24+
25+
- **Dashboard auto-refresh stalling under load** — replaced DispatcherTimer with async Task.Delay loop to prevent priority starvation during heavy chart rendering ([#833], [#834])
26+
- **Lite auto-refresh silently skipping** every tick ([#824])
27+
- **Deadlock count not resetting** between collections ([#803], [#820])
28+
- **Upgrade filter skipping patch versions** during version comparison ([#817], [#819])
29+
- **Upgrade script executing against master** instead of PerformanceMonitor database ([#828])
30+
- **Duplicate release builds** triggering on both created and published events
31+
32+
[#748]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/748
33+
[#803]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/803
34+
[#813]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/813
35+
[#814]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/814
36+
[#816]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/816
37+
[#817]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/817
38+
[#819]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/819
39+
[#820]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/820
40+
[#821]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/821
41+
[#822]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/822
42+
[#823]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/823
43+
[#824]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/824
44+
[#825]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/825
45+
[#826]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/826
46+
[#828]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/828
47+
[#833]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/833
48+
[#834]: https://github.com/erikdarlingdata/PerformanceMonitor/issues/834
49+
850
## [2.6.0] - 2026-04-08
951

1052
### Added

Dashboard/AddServerDialog.xaml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
33
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
44
Title="Add SQL Server"
5-
Height="750" Width="500"
6-
WindowStartupLocation="CenterOwner"
5+
Width="500"
6+
WindowStartupLocation="Manual"
77
ResizeMode="CanResizeWithGrip"
88
Background="{DynamicResource BackgroundBrush}"
99
Foreground="{DynamicResource ForegroundBrush}">
@@ -111,6 +111,12 @@
111111
Foreground="{DynamicResource ForegroundBrush}" Margin="0,0,0,6"
112112
ToolTip="Sets ApplicationIntent=ReadOnly. Required when connecting via an AG listener or Azure failover group endpoint to route to a readable secondary."/>
113113

114+
<!-- Multi-Subnet Failover -->
115+
<CheckBox x:Name="MultiSubnetFailoverCheckBox"
116+
Content="Multi-subnet failover (for AG listeners and FCIs)"
117+
Foreground="{DynamicResource ForegroundBrush}" Margin="0,0,0,6"
118+
ToolTip="Sets MultiSubnetFailover=true. Connects to all IPs in parallel during failover. Recommended for AG listeners and failover cluster instances spanning multiple subnets."/>
119+
114120
<!-- Is Favorite -->
115121
<CheckBox x:Name="IsFavoriteCheckBox" Content="Mark as favorite (appears at top of list)"
116122
Foreground="{DynamicResource ForegroundBrush}" Margin="0,0,0,6"/>
@@ -136,6 +142,9 @@
136142
Background="{DynamicResource BackgroundLightBrush}"
137143
CornerRadius="4" Padding="12" Margin="0,0,0,10">
138144
<StackPanel>
145+
<TextBlock x:Name="ConnectionInfoText" TextWrapping="Wrap" Margin="0,0,0,4"
146+
FontWeight="SemiBold"
147+
Foreground="{DynamicResource ForegroundBrush}"/>
139148
<TextBlock x:Name="DatabaseStatusText" TextWrapping="Wrap" Margin="0,0,0,8"
140149
Foreground="{DynamicResource ForegroundBrush}"/>
141150
<StackPanel Orientation="Horizontal" Margin="0,0,0,4">

Dashboard/AddServerDialog.xaml.cs

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,12 @@ private enum DialogState
4747
private InstallationResult? _installResult;
4848
private string? _reportPath;
4949
private DialogState _currentState = DialogState.Initial;
50+
private string? _serverVersion;
5051

5152
public AddServerDialog()
5253
{
5354
InitializeComponent();
55+
SizeToWorkArea();
5456
_isEditMode = false;
5557
ServerConnection = new ServerConnection();
5658
Title = "Add SQL Server";
@@ -59,6 +61,7 @@ public AddServerDialog()
5961
public AddServerDialog(ServerConnection existingServer)
6062
{
6163
InitializeComponent();
64+
SizeToWorkArea();
6265
_isEditMode = true;
6366
ServerConnection = existingServer;
6467
Title = "Edit SQL Server";
@@ -78,6 +81,7 @@ public AddServerDialog(ServerConnection existingServer)
7881
};
7982
TrustServerCertificateCheckBox.IsChecked = existingServer.TrustServerCertificate;
8083
ReadOnlyIntentCheckBox.IsChecked = existingServer.ReadOnlyIntent;
84+
MultiSubnetFailoverCheckBox.IsChecked = existingServer.MultiSubnetFailover;
8185

8286
if (existingServer.AuthenticationType == AuthenticationTypes.EntraMFA)
8387
{
@@ -108,6 +112,14 @@ public AddServerDialog(ServerConnection existingServer)
108112
}
109113
}
110114

115+
private void SizeToWorkArea()
116+
{
117+
var workArea = SystemParameters.WorkArea;
118+
Height = workArea.Height;
119+
Top = workArea.Top;
120+
Left = workArea.Left + (workArea.Width - Width) / 2;
121+
}
122+
111123
private void AuthType_Changed(object sender, RoutedEventArgs e)
112124
{
113125
if (SqlAuthPanel != null && EntraMfaPanel != null)
@@ -154,7 +166,8 @@ private SqlConnectionStringBuilder BuildConnectionBuilder()
154166
Encrypt = ParseEncryptOption(GetSelectedEncryptMode()),
155167
ApplicationIntent = ReadOnlyIntentCheckBox.IsChecked == true
156168
? ApplicationIntent.ReadOnly
157-
: ApplicationIntent.ReadWrite
169+
: ApplicationIntent.ReadWrite,
170+
MultiSubnetFailover = MultiSubnetFailoverCheckBox.IsChecked == true
158171
};
159172

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

340353
if (connected)
341354
{
342-
var message = serverVersion != null
343-
? $"Successfully connected to {ServerNameTextBox.Text}!\n\n{serverVersion}"
344-
: $"Successfully connected to {ServerNameTextBox.Text}!";
345-
MessageBox.Show(
346-
message,
347-
"Connection Successful",
348-
MessageBoxButton.OK,
349-
MessageBoxImage.Information
350-
);
355+
_serverVersion = serverVersion;
351356

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

401406
if (!_coreServerInfo.IsSupportedVersion)
402407
{
408+
string serverName = ServerNameTextBox.Text;
409+
ConnectionInfoText.Text = _serverVersion != null
410+
? $"Connected to {serverName} ({_serverVersion})"
411+
: $"Connected to {serverName}";
403412
DatabaseStatusText.Text = $"Warning: {_coreServerInfo.ProductMajorVersionName} is not supported. SQL Server 2016+ is required.";
404413
DatabaseStatusPanel.Visibility = Visibility.Visible;
405414
InstallUpgradeButton.Visibility = Visibility.Collapsed;
@@ -458,13 +467,22 @@ private void TransitionToState(DialogState newState)
458467
ViewReportButton.Visibility = Visibility.Collapsed;
459468
StatusText.Text = string.Empty;
460469
StatusText.Visibility = Visibility.Collapsed;
470+
ConnectionInfoText.Text = string.Empty;
461471
InstallUpgradeButton.Visibility = Visibility.Visible;
462472
SkipInstallText.Visibility = Visibility.Visible;
463473

474+
/* Build the connection header shown for all connected states */
475+
string serverName = ServerNameTextBox.Text;
476+
string connectionHeader = _serverVersion != null
477+
? $"Connected to {serverName} ({_serverVersion})"
478+
: $"Connected to {serverName}";
479+
464480
switch (newState)
465481
{
466482
case DialogState.Connected_NoDatabase:
467-
DatabaseStatusText.Text = $"No PerformanceMonitor database found on this server. Install v{appVersion}?";
483+
ConnectionInfoText.Text = connectionHeader;
484+
DatabaseStatusText.Text = "No PerformanceMonitor database found on this server. " +
485+
$"Click Install Now to create the monitoring database, collection jobs, and stored procedures (v{appVersion}).";
468486
InstallUpgradeButton.Content = "Install Now";
469487
DatabaseStatusPanel.Visibility = Visibility.Visible;
470488
InstallationPanel.Visibility = Visibility.Visible;
@@ -473,7 +491,9 @@ private void TransitionToState(DialogState newState)
473491

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

483503
case DialogState.Connected_Current:
484504
string normalizedCurrent = NormalizeVersion(_installedVersion!);
505+
ConnectionInfoText.Text = connectionHeader;
485506
DatabaseStatusText.Text = $"PerformanceMonitor v{normalizedCurrent} is up to date.";
486507
InstallUpgradeButton.Visibility = Visibility.Collapsed;
487508
SkipInstallText.Visibility = Visibility.Collapsed;
@@ -616,7 +637,8 @@ private async void InstallOrUpgrade_Click(object sender, RoutedEventArgs e)
616637
preValidationAction = async () =>
617638
{
618639
AppendInstallLog("Installing community dependencies...", "Info");
619-
using var depInstaller = new DependencyInstaller();
640+
string communityDir = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "community");
641+
using var depInstaller = new DependencyInstaller(communityDir);
620642
await depInstaller.InstallDependenciesAsync(installerConnStr, progress, cancellationToken);
621643
};
622644
}
@@ -821,6 +843,7 @@ private void SetFormEnabled(bool enabled)
821843
EncryptModeComboBox.IsEnabled = enabled;
822844
TrustServerCertificateCheckBox.IsEnabled = enabled;
823845
ReadOnlyIntentCheckBox.IsEnabled = enabled;
846+
MultiSubnetFailoverCheckBox.IsEnabled = enabled;
824847
IsFavoriteCheckBox.IsEnabled = enabled;
825848
MonthlyCostTextBox.IsEnabled = enabled;
826849
DescriptionTextBox.IsEnabled = enabled;
@@ -915,6 +938,7 @@ private async void Save_Click(object sender, RoutedEventArgs e)
915938
ServerConnection.EncryptMode = GetSelectedEncryptMode();
916939
ServerConnection.TrustServerCertificate = TrustServerCertificateCheckBox.IsChecked == true;
917940
ServerConnection.ReadOnlyIntent = ReadOnlyIntentCheckBox.IsChecked == true;
941+
ServerConnection.MultiSubnetFailover = MultiSubnetFailoverCheckBox.IsChecked == true;
918942
if (decimal.TryParse(MonthlyCostTextBox.Text, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out var editCost) && editCost >= 0)
919943
ServerConnection.MonthlyCostUsd = editCost;
920944
}
@@ -936,6 +960,7 @@ private async void Save_Click(object sender, RoutedEventArgs e)
936960
EncryptMode = GetSelectedEncryptMode(),
937961
TrustServerCertificate = TrustServerCertificateCheckBox.IsChecked == true,
938962
ReadOnlyIntent = ReadOnlyIntentCheckBox.IsChecked == true,
963+
MultiSubnetFailover = MultiSubnetFailoverCheckBox.IsChecked == true,
939964
MonthlyCostUsd = monthlyCost
940965
};
941966
}

0 commit comments

Comments
 (0)