Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 Dashboard/Controls/MemoryContent.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
</ResourceDictionary>
</UserControl.Resources>

<TabControl TabStripPlacement="Top" Margin="0,5,0,0">
<TabControl x:Name="SubTabControl" TabStripPlacement="Top" Margin="0,5,0,0">
<!-- Memory Overview Sub-Tab -->
<TabItem Header="Memory Overview">
<!-- Chart + Summary Panel (grid removed) -->
Expand Down
35 changes: 25 additions & 10 deletions Dashboard/Controls/MemoryContent.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -172,22 +172,37 @@ public void SetTimeRange(int hoursBack, DateTime? fromDate = null, DateTime? toD
}

/// <summary>
/// Refreshes all memory data. Can be called from parent control.
/// Refreshes memory data. When fullRefresh is false, only the visible sub-tab is refreshed.
/// </summary>
public async Task RefreshAllDataAsync()
public async Task RefreshAllDataAsync(bool fullRefresh = true)
{
try
{
using var _ = Helpers.MethodProfiler.StartTiming("Memory");

// Run all independent refreshes in parallel for better performance
await Task.WhenAll(
RefreshMemoryStatsAsync(),
RefreshMemoryGrantsAsync(),
RefreshMemoryClerksAsync(),
RefreshPlanCacheAsync(),
RefreshMemoryPressureEventsAsync()
);
if (fullRefresh)
{
// Run all independent refreshes in parallel for initial load / manual refresh
await Task.WhenAll(
RefreshMemoryStatsAsync(),
RefreshMemoryGrantsAsync(),
RefreshMemoryClerksAsync(),
RefreshPlanCacheAsync(),
RefreshMemoryPressureEventsAsync()
);
}
else
{
// Only refresh the visible sub-tab
switch (SubTabControl.SelectedIndex)
{
case 0: await RefreshMemoryStatsAsync(); break;
case 1: await RefreshMemoryGrantsAsync(); break;
case 2: await RefreshMemoryClerksAsync(); break;
case 3: await RefreshPlanCacheAsync(); break;
case 4: await RefreshMemoryPressureEventsAsync(); break;
}
}
}
catch (Exception ex)
{
Expand Down
2 changes: 1 addition & 1 deletion Dashboard/Controls/QueryPerformanceContent.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
</ResourceDictionary>
</UserControl.Resources>

<TabControl TabStripPlacement="Top" Margin="0,5,0,0">
<TabControl x:Name="SubTabControl" TabStripPlacement="Top" Margin="0,5,0,0">
<!-- Performance Trends Sub-Tab -->
<TabItem Header="Performance Trends">
<Grid>
Expand Down
99 changes: 83 additions & 16 deletions Dashboard/Controls/QueryPerformanceContent.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -262,16 +262,35 @@ public void SetTimeRange(int hoursBack, DateTime? fromDate = null, DateTime? toD
}

/// <summary>
/// Refreshes all data for all sub-tabs.
/// Refreshes query performance data. When fullRefresh is false, only the visible sub-tab is refreshed.
/// </summary>
public async Task RefreshAllDataAsync()
public async Task RefreshAllDataAsync(bool fullRefresh = true)
{
try
{
using var _ = Helpers.MethodProfiler.StartTiming("QueryPerformance");

if (_databaseService == null) return;

if (!fullRefresh)
{
// Only refresh the visible sub-tab
switch (SubTabControl.SelectedIndex)
{
case 0: await RefreshPerformanceTrendsAsync(); break;
case 1: await RefreshActiveQueriesAsync(); break;
case 2: break; // Current Active Queries — manual refresh only
case 3: await RefreshQueryStatsGridAsync(); break;
case 4: await RefreshProcStatsGridAsync(); break;
case 5: await RefreshQueryStoreGridAsync(); break;
case 6: await RefreshQueryStoreRegressionsAsync(); break;
case 7: await RefreshLongRunningPatternsAsync(); break;
}
return;
}

// Full refresh — all sub-tabs in parallel

// Only show loading overlay on initial load (no existing data)
if (QueryStatsDataGrid.ItemsSource == null)
{
Expand Down Expand Up @@ -303,20 +322,9 @@ await Task.WhenAll(
);

// Populate grids from summary data
var queryStats = await queryStatsTask;
QueryStatsDataGrid.ItemsSource = queryStats;
QueryStatsNoDataMessage.Visibility = queryStats.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
SetInitialSort(QueryStatsDataGrid, "AvgCpuTimeMs", ListSortDirection.Descending);

var procStats = await procStatsTask;
ProcStatsDataGrid.ItemsSource = procStats;
ProcStatsNoDataMessage.Visibility = procStats.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
SetInitialSort(ProcStatsDataGrid, "AvgCpuTimeMs", ListSortDirection.Descending);

var queryStore = await queryStoreTask;
QueryStoreDataGrid.ItemsSource = queryStore;
QueryStoreNoDataMessage.Visibility = queryStore.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
SetInitialSort(QueryStoreDataGrid, "AvgCpuTimeMs", ListSortDirection.Descending);
PopulateQueryStatsGrid(await queryStatsTask);
PopulateProcStatsGrid(await procStatsTask);
PopulateQueryStoreGrid(await queryStoreTask);

// Populate charts from time-series data
LoadDurationChart(QueryPerfTrendsQueryChart, await queryDurationTrendsTask, _perfTrendsHoursBack, _perfTrendsFromDate, _perfTrendsToDate, "Duration (ms/sec)", TabHelpers.ChartColors[0], _queryDurationHover);
Expand All @@ -334,6 +342,65 @@ await Task.WhenAll(
}
}

private async Task RefreshPerformanceTrendsAsync()
{
if (_databaseService == null) return;

var queryDurationTrendsTask = _databaseService.GetQueryDurationTrendsAsync(_perfTrendsHoursBack, _perfTrendsFromDate, _perfTrendsToDate);
var procDurationTrendsTask = _databaseService.GetProcedureDurationTrendsAsync(_perfTrendsHoursBack, _perfTrendsFromDate, _perfTrendsToDate);
var qsDurationTrendsTask = _databaseService.GetQueryStoreDurationTrendsAsync(_perfTrendsHoursBack, _perfTrendsFromDate, _perfTrendsToDate);
var execTrendsTask = _databaseService.GetExecutionTrendsAsync(_perfTrendsHoursBack, _perfTrendsFromDate, _perfTrendsToDate);

await Task.WhenAll(queryDurationTrendsTask, procDurationTrendsTask, qsDurationTrendsTask, execTrendsTask);

LoadDurationChart(QueryPerfTrendsQueryChart, await queryDurationTrendsTask, _perfTrendsHoursBack, _perfTrendsFromDate, _perfTrendsToDate, "Duration (ms/sec)", TabHelpers.ChartColors[0], _queryDurationHover);
LoadDurationChart(QueryPerfTrendsProcChart, await procDurationTrendsTask, _perfTrendsHoursBack, _perfTrendsFromDate, _perfTrendsToDate, "Duration (ms/sec)", TabHelpers.ChartColors[1], _procDurationHover);
LoadDurationChart(QueryPerfTrendsQsChart, await qsDurationTrendsTask, _perfTrendsHoursBack, _perfTrendsFromDate, _perfTrendsToDate, "Duration (ms/sec)", TabHelpers.ChartColors[4], _qsDurationHover);
LoadExecChart(await execTrendsTask, _perfTrendsHoursBack, _perfTrendsFromDate, _perfTrendsToDate);
}

private async Task RefreshQueryStatsGridAsync()
{
if (_databaseService == null) return;
var data = await _databaseService.GetQueryStatsAsync(_queryStatsHoursBack, _queryStatsFromDate, _queryStatsToDate);
PopulateQueryStatsGrid(data);
}

private async Task RefreshProcStatsGridAsync()
{
if (_databaseService == null) return;
var data = await _databaseService.GetProcedureStatsAsync(_procStatsHoursBack, _procStatsFromDate, _procStatsToDate);
PopulateProcStatsGrid(data);
}

private async Task RefreshQueryStoreGridAsync()
{
if (_databaseService == null) return;
var data = await _databaseService.GetQueryStoreDataAsync(_queryStoreHoursBack, _queryStoreFromDate, _queryStoreToDate);
PopulateQueryStoreGrid(data);
}

private void PopulateQueryStatsGrid(List<QueryStatsItem> data)
{
QueryStatsDataGrid.ItemsSource = data;
QueryStatsNoDataMessage.Visibility = data.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
SetInitialSort(QueryStatsDataGrid, "AvgCpuTimeMs", ListSortDirection.Descending);
}

private void PopulateProcStatsGrid(List<ProcedureStatsItem> data)
{
ProcStatsDataGrid.ItemsSource = data;
ProcStatsNoDataMessage.Visibility = data.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
SetInitialSort(ProcStatsDataGrid, "AvgCpuTimeMs", ListSortDirection.Descending);
}

private void PopulateQueryStoreGrid(List<QueryStoreItem> data)
{
QueryStoreDataGrid.ItemsSource = data;
QueryStoreNoDataMessage.Visibility = data.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
SetInitialSort(QueryStoreDataGrid, "AvgCpuTimeMs", ListSortDirection.Descending);
}

private void SetStatus(string message)
{
_statusCallback?.Invoke(message);
Expand Down
2 changes: 1 addition & 1 deletion Dashboard/Controls/ResourceMetricsContent.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
</ContextMenu>
</UserControl.Resources>

<TabControl TabStripPlacement="Top" Margin="0,5,0,0">
<TabControl x:Name="SubTabControl" TabStripPlacement="Top" Margin="0,5,0,0">
<!-- Server Trends Sub-Tab -->
<TabItem Header="Server Trends">
<!-- 2x2 Chart Grid -->
Expand Down
46 changes: 32 additions & 14 deletions Dashboard/Controls/ResourceMetricsContent.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -251,27 +251,45 @@ public void SetTimeRange(int hoursBack, DateTime? fromDate = null, DateTime? toD
}

/// <summary>
/// Refreshes all resource metrics data. Can be called from parent control.
/// Refreshes resource metrics data. When fullRefresh is false, only the visible sub-tab is refreshed.
/// </summary>
public async Task RefreshAllDataAsync()
public async Task RefreshAllDataAsync(bool fullRefresh = true)
{
using var _ = Helpers.MethodProfiler.StartTiming("ResourceMetrics");
if (_databaseService == null) return;

try
{
// Run all independent refreshes in parallel for better performance
await Task.WhenAll(
RefreshLatchStatsAsync(),
RefreshSpinlockStatsAsync(),
RefreshTempdbStatsAsync(),
RefreshSessionStatsAsync(),
LoadFileIoLatencyChartsAsync(),
LoadFileIoThroughputChartsAsync(),
RefreshServerTrendsAsync(),
RefreshPerfmonCountersTabAsync(),
RefreshWaitStatsDetailTabAsync()
);
if (fullRefresh)
{
// Run all independent refreshes in parallel for initial load / manual refresh
await Task.WhenAll(
RefreshLatchStatsAsync(),
RefreshSpinlockStatsAsync(),
RefreshTempdbStatsAsync(),
RefreshSessionStatsAsync(),
LoadFileIoLatencyChartsAsync(),
LoadFileIoThroughputChartsAsync(),
RefreshServerTrendsAsync(),
RefreshPerfmonCountersTabAsync(),
RefreshWaitStatsDetailTabAsync()
);
}
else
{
// Only refresh the visible sub-tab
switch (SubTabControl.SelectedIndex)
{
case 0: await RefreshServerTrendsAsync(); break;
case 1: await RefreshWaitStatsDetailTabAsync(); break;
case 2: await RefreshTempdbStatsAsync(); break;
case 3: await Task.WhenAll(LoadFileIoLatencyChartsAsync(), LoadFileIoThroughputChartsAsync()); break;
case 4: await RefreshPerfmonCountersTabAsync(); break;
case 5: await RefreshSessionStatsAsync(); break;
case 6: await RefreshLatchStatsAsync(); break;
case 7: await RefreshSpinlockStatsAsync(); break;
}
}
}
catch (Exception ex)
{
Expand Down
2 changes: 1 addition & 1 deletion Dashboard/Controls/SystemEventsContent.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
</ResourceDictionary>
</UserControl.Resources>

<TabControl TabStripPlacement="Top" Margin="0,5,0,0">
<TabControl x:Name="SubTabControl" TabStripPlacement="Top" Margin="0,5,0,0">
<!-- Overview Tab -->
<TabItem Header="Corruption Events">
<Grid Margin="10,5,10,10">
Expand Down
46 changes: 33 additions & 13 deletions Dashboard/Controls/SystemEventsContent.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -312,26 +312,46 @@ public void SetTimeRange(int hoursBack, DateTime? fromDate = null, DateTime? toD
}

/// <summary>
/// Refreshes all system events data. Can be called from parent control.
/// Refreshes system events data. When fullRefresh is false, only the visible sub-tab is refreshed.
/// </summary>
public async Task RefreshAllDataAsync()
public async Task RefreshAllDataAsync(bool fullRefresh = true)
{
using var _ = Helpers.MethodProfiler.StartTiming("SystemEvents");
if (_databaseService == null) return;

try
{
// Run all independent refreshes in parallel for better performance
await Task.WhenAll(
RefreshSystemHealthAsync(),
RefreshSevereErrorsAsync(),
RefreshIOIssuesAsync(),
RefreshSchedulerIssuesAsync(),
RefreshMemoryConditionsAsync(),
RefreshCPUTasksAsync(),
RefreshMemoryBrokerAsync(),
RefreshMemoryNodeOOMAsync()
);
if (fullRefresh)
{
// Run all independent refreshes in parallel for initial load / manual refresh
await Task.WhenAll(
RefreshSystemHealthAsync(),
RefreshSevereErrorsAsync(),
RefreshIOIssuesAsync(),
RefreshSchedulerIssuesAsync(),
RefreshMemoryConditionsAsync(),
RefreshCPUTasksAsync(),
RefreshMemoryBrokerAsync(),
RefreshMemoryNodeOOMAsync()
);
}
else
{
// Only refresh the visible sub-tab
switch (SubTabControl.SelectedIndex)
{
case 0: // Corruption Events
case 1: // Contention Events — same data source
await RefreshSystemHealthAsync(); break;
case 2: await RefreshSevereErrorsAsync(); break;
case 3: await RefreshIOIssuesAsync(); break;
case 4: await RefreshSchedulerIssuesAsync(); break;
case 5: await RefreshMemoryConditionsAsync(); break;
case 6: await RefreshCPUTasksAsync(); break;
case 7: await RefreshMemoryBrokerAsync(); break;
case 8: await RefreshMemoryNodeOOMAsync(); break;
}
}
}
catch (Exception ex)
{
Expand Down
Loading
Loading