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
6 changes: 2 additions & 4 deletions .github/sql/ci_validate_installation.sql
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ IF SCHEMA_ID(N'report') IS NULL BEGIN SET @missing += 1; PRINT ' MISSING: sche
PRINT '';

/*
Procedures in collect schema (37)
Procedures in collect schema (36)
*/
PRINT 'Checking collect procedures...';

Expand Down Expand Up @@ -65,7 +65,6 @@ IF OBJECT_ID(N'collect.tempdb_stats_collector', N'P') IS NULL BEGIN SE
IF OBJECT_ID(N'collect.plan_cache_stats_collector', N'P') IS NULL BEGIN SET @missing += 1; PRINT ' MISSING: collect.plan_cache_stats_collector'; END; SET @checked += 1;
IF OBJECT_ID(N'collect.session_stats_collector', N'P') IS NULL BEGIN SET @missing += 1; PRINT ' MISSING: collect.session_stats_collector'; END; SET @checked += 1;
IF OBJECT_ID(N'collect.waiting_tasks_collector', N'P') IS NULL BEGIN SET @missing += 1; PRINT ' MISSING: collect.waiting_tasks_collector'; END; SET @checked += 1;
IF OBJECT_ID(N'collect.session_wait_stats_collector', N'P') IS NULL BEGIN SET @missing += 1; PRINT ' MISSING: collect.session_wait_stats_collector'; END; SET @checked += 1;
IF OBJECT_ID(N'collect.server_configuration_collector', N'P') IS NULL BEGIN SET @missing += 1; PRINT ' MISSING: collect.server_configuration_collector'; END; SET @checked += 1;
IF OBJECT_ID(N'collect.database_configuration_collector', N'P') IS NULL BEGIN SET @missing += 1; PRINT ' MISSING: collect.database_configuration_collector'; END; SET @checked += 1;
IF OBJECT_ID(N'collect.configuration_issues_analyzer', N'P') IS NULL BEGIN SET @missing += 1; PRINT ' MISSING: collect.configuration_issues_analyzer'; END; SET @checked += 1;
Expand Down Expand Up @@ -142,7 +141,6 @@ IF OBJECT_ID(N'report.blocking_chain_analysis', N'V') IS NULL BEGIN
IF OBJECT_ID(N'report.tempdb_contention_analysis', N'V') IS NULL BEGIN SET @missing += 1; PRINT ' MISSING: report.tempdb_contention_analysis'; END; SET @checked += 1;
IF OBJECT_ID(N'report.parameter_sensitivity_detection', N'V') IS NULL BEGIN SET @missing += 1; PRINT ' MISSING: report.parameter_sensitivity_detection'; END; SET @checked += 1;
IF OBJECT_ID(N'report.scheduler_cpu_analysis', N'V') IS NULL BEGIN SET @missing += 1; PRINT ' MISSING: report.scheduler_cpu_analysis'; END; SET @checked += 1;
IF OBJECT_ID(N'report.session_wait_analysis', N'V') IS NULL BEGIN SET @missing += 1; PRINT ' MISSING: report.session_wait_analysis'; END; SET @checked += 1;
IF OBJECT_ID(N'report.critical_issues', N'V') IS NULL BEGIN SET @missing += 1; PRINT ' MISSING: report.critical_issues'; END; SET @checked += 1;
IF OBJECT_ID(N'report.memory_usage_trends', N'V') IS NULL BEGIN SET @missing += 1; PRINT ' MISSING: report.memory_usage_trends'; END; SET @checked += 1;
IF OBJECT_ID(N'report.running_jobs', N'V') IS NULL BEGIN SET @missing += 1; PRINT ' MISSING: report.running_jobs'; END; SET @checked += 1;
Expand Down Expand Up @@ -182,7 +180,7 @@ WHERE OBJECT_SCHEMA_NAME(t.object_id) = N'config';
PRINT ' collect schema tables: ' + CONVERT(varchar(10), @collect_tables);
PRINT ' config schema tables: ' + CONVERT(varchar(10), @config_tables);

IF @collect_tables < 20 BEGIN SET @missing += 1; PRINT ' MISSING: expected >= 20 collect tables, found ' + CONVERT(varchar(10), @collect_tables); END; SET @checked += 1;
IF @collect_tables < 19 BEGIN SET @missing += 1; PRINT ' MISSING: expected >= 20 collect tables, found ' + CONVERT(varchar(10), @collect_tables); END; SET @checked += 1;
IF @config_tables < 5 BEGIN SET @missing += 1; PRINT ' MISSING: expected >= 5 config tables, found ' + CONVERT(varchar(10), @config_tables); END; SET @checked += 1;

PRINT '';
Expand Down
85 changes: 62 additions & 23 deletions Dashboard/Controls/ResourceMetricsContent.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -201,38 +201,77 @@
</Grid>
</TabItem>

<!-- File I/O Latency Sub-Tab - User DB only, TempDB moved to TempDB Stats -->
<TabItem Header="File I/O Latency">
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>

<!-- User DB Read Latency -->
<Border Grid.Row="0" BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1" Margin="0,0,0,2">
<Grid>
<!-- File I/O Sub-Tab -->
<TabItem Header="File I/O">
<TabControl Background="Transparent" BorderThickness="0">
<!-- File I/O Latency sub-tab -->
<TabItem Header="File I/O Latency">
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="User DB Read Latency (ms)" FontWeight="Bold" FontSize="11" Margin="5,3" Foreground="{DynamicResource ForegroundBrush}"/>
<ScottPlot:WpfPlot x:Name="UserDbReadLatencyChart" Grid.Row="1"/>

<!-- User DB Read Latency -->
<Border Grid.Row="0" BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1" Margin="0,0,0,2">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="User DB Read Latency (ms)" FontWeight="Bold" FontSize="11" Margin="5,3" Foreground="{DynamicResource ForegroundBrush}"/>
<ScottPlot:WpfPlot x:Name="UserDbReadLatencyChart" Grid.Row="1"/>
</Grid>
</Border>

<!-- User DB Write Latency -->
<Border Grid.Row="1" BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1" Margin="0,2,0,0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="User DB Write Latency (ms)" FontWeight="Bold" FontSize="11" Margin="5,3" Foreground="{DynamicResource ForegroundBrush}"/>
<ScottPlot:WpfPlot x:Name="UserDbWriteLatencyChart" Grid.Row="1"/>
</Grid>
</Border>
</Grid>
</Border>
</TabItem>

<!-- User DB Write Latency -->
<Border Grid.Row="1" BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1" Margin="0,2,0,0">
<Grid>
<!-- File I/O Throughput sub-tab -->
<TabItem Header="File I/O Throughput">
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="User DB Write Latency (ms)" FontWeight="Bold" FontSize="11" Margin="5,3" Foreground="{DynamicResource ForegroundBrush}"/>
<ScottPlot:WpfPlot x:Name="UserDbWriteLatencyChart" Grid.Row="1"/>

<!-- User DB Read Throughput -->
<Border Grid.Row="0" BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1" Margin="0,0,0,2">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="User DB Read Throughput (MB/s)" FontWeight="Bold" FontSize="11" Margin="5,3" Foreground="{DynamicResource ForegroundBrush}"/>
<ScottPlot:WpfPlot x:Name="FileIoReadThroughputChart" Grid.Row="1"/>
</Grid>
</Border>

<!-- User DB Write Throughput -->
<Border Grid.Row="1" BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1" Margin="0,2,0,0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="User DB Write Throughput (MB/s)" FontWeight="Bold" FontSize="11" Margin="5,3" Foreground="{DynamicResource ForegroundBrush}"/>
<ScottPlot:WpfPlot x:Name="FileIoWriteThroughputChart" Grid.Row="1"/>
</Grid>
</Border>
</Grid>
</Border>
</Grid>
</TabItem>
</TabControl>
</TabItem>

<!-- Perfmon Counters Sub-Tab -->
Expand Down
54 changes: 50 additions & 4 deletions Dashboard/Controls/ResourceMetricsContent.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ public partial class ResourceMetricsContent : UserControl
private Helpers.ChartHoverHelper? _spinlockStatsHover;
private Helpers.ChartHoverHelper? _fileIoReadHover;
private Helpers.ChartHoverHelper? _fileIoWriteHover;
private Helpers.ChartHoverHelper? _fileIoReadThroughputHover;
private Helpers.ChartHoverHelper? _fileIoWriteThroughputHover;
private Helpers.ChartHoverHelper? _perfmonHover;
private Helpers.ChartHoverHelper? _waitStatsHover;
private Helpers.ChartHoverHelper? _tempdbStatsHover;
Expand All @@ -111,6 +113,8 @@ public ResourceMetricsContent()
_spinlockStatsHover = new Helpers.ChartHoverHelper(SpinlockStatsChart, "collisions/sec");
_fileIoReadHover = new Helpers.ChartHoverHelper(UserDbReadLatencyChart, "ms");
_fileIoWriteHover = new Helpers.ChartHoverHelper(UserDbWriteLatencyChart, "ms");
_fileIoReadThroughputHover = new Helpers.ChartHoverHelper(FileIoReadThroughputChart, "MB/s");
_fileIoWriteThroughputHover = new Helpers.ChartHoverHelper(FileIoWriteThroughputChart, "MB/s");
_perfmonHover = new Helpers.ChartHoverHelper(PerfmonCountersChart, "");
_waitStatsHover = new Helpers.ChartHoverHelper(WaitStatsDetailChart, "ms/sec");
_tempdbStatsHover = new Helpers.ChartHoverHelper(TempdbStatsChart, "MB");
Expand Down Expand Up @@ -146,6 +150,10 @@ private void SetupChartContextMenus()
// File I/O Latency charts
TabHelpers.SetupChartContextMenu(UserDbReadLatencyChart, "UserDB_Read_Latency", "collect.file_io_stats");
TabHelpers.SetupChartContextMenu(UserDbWriteLatencyChart, "UserDB_Write_Latency", "collect.file_io_stats");

// File I/O Throughput charts
TabHelpers.SetupChartContextMenu(FileIoReadThroughputChart, "UserDB_Read_Throughput", "collect.file_io_stats");
TabHelpers.SetupChartContextMenu(FileIoWriteThroughputChart, "UserDB_Write_Throughput", "collect.file_io_stats");
TabHelpers.SetupChartContextMenu(TempDbLatencyChart, "TempDB_Latency", "collect.file_io_stats");

// Server Utilization Trends charts
Expand Down Expand Up @@ -225,6 +233,7 @@ await Task.WhenAll(
RefreshTempdbStatsAsync(),
RefreshSessionStatsAsync(),
LoadFileIoLatencyChartsAsync(),
LoadFileIoThroughputChartsAsync(),
RefreshServerTrendsAsync(),
RefreshPerfmonCountersTabAsync(),
RefreshWaitStatsDetailTabAsync()
Expand Down Expand Up @@ -837,11 +846,11 @@ private async Task LoadFileIoLatencyChartsAsync()

// Load User DB data only - TempDB latency moved to TempDB Stats tab
var userDbData = await _databaseService.GetFileIoLatencyTimeSeriesAsync(isTempDb: false, _fileIoHoursBack, _fileIoFromDate, _fileIoToDate);
LoadFileIoChart(UserDbReadLatencyChart, userDbData, d => d.ReadLatencyMs, "Read Latency (ms)", colors, xMin, xMax, _fileIoReadHover);
LoadFileIoChart(UserDbWriteLatencyChart, userDbData, d => d.WriteLatencyMs, "Write Latency (ms)", colors, xMin, xMax, _fileIoWriteHover);
LoadFileIoChart(UserDbReadLatencyChart, userDbData, d => d.ReadLatencyMs, "Read Latency (ms)", colors, xMin, xMax, _fileIoReadHover, d => d.ReadQueuedLatencyMs);
LoadFileIoChart(UserDbWriteLatencyChart, userDbData, d => d.WriteLatencyMs, "Write Latency (ms)", colors, xMin, xMax, _fileIoWriteHover, d => d.WriteQueuedLatencyMs);
}

private void LoadFileIoChart(ScottPlot.WPF.WpfPlot chart, List<FileIoLatencyTimeSeriesItem> data, Func<FileIoLatencyTimeSeriesItem, decimal> latencySelector, string yLabel, ScottPlot.Color[] colors, double xMin, double xMax, Helpers.ChartHoverHelper? hover = null)
private void LoadFileIoChart(ScottPlot.WPF.WpfPlot chart, List<FileIoLatencyTimeSeriesItem> data, Func<FileIoLatencyTimeSeriesItem, decimal> latencySelector, string yLabel, ScottPlot.Color[] colors, double xMin, double xMax, Helpers.ChartHoverHelper? hover = null, Func<FileIoLatencyTimeSeriesItem, decimal>? queuedSelector = null)
{
DateTime rangeStart = DateTime.FromOADate(xMin);
DateTime rangeEnd = DateTime.FromOADate(xMax);
Expand All @@ -856,6 +865,9 @@ private void LoadFileIoChart(ScottPlot.WPF.WpfPlot chart, List<FileIoLatencyTime
TabHelpers.ApplyDarkModeToChart(chart);
hover?.Clear();

// Check if any queued data exists (only render overlay if there's real data)
bool hasQueuedData = queuedSelector != null && data != null && data.Any(d => queuedSelector(d) > 0);

if (data != null && data.Count > 0)
{
// Get all unique time points for gap filling
Expand All @@ -879,13 +891,31 @@ private void LoadFileIoChart(ScottPlot.WPF.WpfPlot chart, List<FileIoLatencyTime
var scatter = chart.Plot.Add.Scatter(xs, ys);
scatter.LineWidth = 2;
scatter.MarkerSize = 5;
scatter.Color = colors[colorIndex % colors.Length];
var color = colors[colorIndex % colors.Length];
scatter.Color = color;

// Use just the filename for legend (not database.filename which is redundant)
var fileName = fileData.First().FileName;
scatter.LegendText = fileName;
hover?.Add(scatter, fileName);

// Add queued I/O overlay as dashed line with same color
if (hasQueuedData)
{
var queuedValues = fileData.Select(d => (double)queuedSelector!(d));
if (queuedValues.Any(v => v > 0))
{
var (qxs, qys) = TabHelpers.FillTimeSeriesGaps(timePoints, queuedValues);
var queuedScatter = chart.Plot.Add.Scatter(qxs, qys);
queuedScatter.LineWidth = 2;
queuedScatter.MarkerSize = 0;
queuedScatter.Color = color;
queuedScatter.LinePattern = ScottPlot.LinePattern.Dashed;
queuedScatter.LegendText = $"{fileName} (queued)";
hover?.Add(queuedScatter, $"{fileName} (queued)");
}
}

colorIndex++;
}
}
Expand Down Expand Up @@ -913,6 +943,22 @@ private void LoadFileIoChart(ScottPlot.WPF.WpfPlot chart, List<FileIoLatencyTime
chart.Refresh();
}

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

DateTime rangeEnd = _fileIoToDate ?? Helpers.ServerTimeHelper.ServerNow;
DateTime rangeStart = _fileIoFromDate ?? rangeEnd.AddHours(-_fileIoHoursBack);
double xMin = rangeStart.ToOADate();
double xMax = rangeEnd.ToOADate();

var colors = TabHelpers.ChartColors;

var throughputData = await _databaseService.GetFileIoThroughputTimeSeriesAsync(isTempDb: false, _fileIoHoursBack, _fileIoFromDate, _fileIoToDate);
LoadFileIoChart(FileIoReadThroughputChart, throughputData, d => d.ReadThroughputMbPerSec, "Read Throughput (MB/s)", colors, xMin, xMax, _fileIoReadThroughputHover);
LoadFileIoChart(FileIoWriteThroughputChart, throughputData, d => d.WriteThroughputMbPerSec, "Write Throughput (MB/s)", colors, xMin, xMax, _fileIoWriteThroughputHover);
}

#endregion

#region Server Trends Tab
Expand Down
4 changes: 4 additions & 0 deletions Dashboard/Models/FileIoLatencyTimeSeriesItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,9 @@ public class FileIoLatencyTimeSeriesItem
public decimal WriteLatencyMs { get; set; }
public long ReadCount { get; set; }
public long WriteCount { get; set; }
public decimal ReadQueuedLatencyMs { get; set; }
public decimal WriteQueuedLatencyMs { get; set; }
public decimal ReadThroughputMbPerSec { get; set; }
public decimal WriteThroughputMbPerSec { get; set; }
}
}
Loading