diff --git a/Darling/Darling.Tests/DarlingMcpHealthParserToolsTests.cs b/Darling/Darling.Tests/DarlingMcpHealthParserToolsTests.cs index 6f9f769a5a..d6ac043ba0 100644 --- a/Darling/Darling.Tests/DarlingMcpHealthParserToolsTests.cs +++ b/Darling/Darling.Tests/DarlingMcpHealthParserToolsTests.cs @@ -208,12 +208,12 @@ viewer entry point still delegates rather than growing an independent (drift-pro [Fact] public void Significance_SchedulerIssue_MatchesViewer() { - foreach (var status in new[] { "WARNING", "OK", "", null }) + foreach (var (sqlCpu, other, mem) in new (int?, int?, int?)[] { (90, 0, 100), (89, 0, 100), (0, 50, 100), (0, 49, 100), (0, 0, 50), (0, 0, 51), (null, null, null) }) { - var r = new SchedulerIssueRecord { Status = status }; + var r = new SchedulerIssueRecord { SqlCpuUtilization = sqlCpu, OtherProcessCpu = other, MemoryUtilization = mem }; Assert.Equal(SystemEventSignificance.IsSignificant(r), SystemHealthSignificance.IsSignificant(r)); } - Assert.True(SystemHealthSignificance.IsSignificant(new SchedulerIssueRecord { Status = "WARNING" })); + Assert.True(SystemHealthSignificance.IsSignificant(new SchedulerIssueRecord { SqlCpuUtilization = 90 })); } [Fact] @@ -311,7 +311,7 @@ public void RoundTrip_ShredFixturesThenGate_KeepsOnlySignificantRows() // SystemHealthSignificance keeps the significant rows. Assert the same significant set the // viewer's System Events tab surfaces. Assert.True(SystemHealthSignificance.IsSignificant( - SystemHealthParser.ParseSchedulerIssue(LoadFixture("scheduler_monitor.xml"))!)); // WARNING + SystemHealthParser.ParseSchedulerIssue(LoadFixture("scheduler_monitor_high_sql_cpu.xml"))!)); // SQL CPU 94 >= 90 Assert.True(SystemHealthSignificance.IsSignificant( SystemHealthParser.ParseSevereError(LoadFixture("error_reported.xml"))!)); // severity 24 Assert.True(SystemHealthSignificance.IsSignificant( @@ -398,7 +398,7 @@ await DarlingMcpTestData.ExecAsync(connection, ct, VALUES ($1,$2,$3,$4,$5,$6,$7)", CollectionIdGenerator.Next(), t, ServerId, ServerName, t, eventType, LoadFixture(fixture)); - await PlantEvent(SystemHealthParser.SchedulerMonitorEvent, "scheduler_monitor.xml"); + await PlantEvent(SystemHealthParser.SchedulerMonitorEvent, "scheduler_monitor_high_sql_cpu.xml"); await PlantEvent(SystemHealthParser.ErrorReportedEvent, "error_reported.xml"); await PlantEvent(SystemHealthParser.SpServerDiagnosticsEvent, "sp_server_diagnostics_system.xml"); await PlantEvent(SystemHealthParser.SpServerDiagnosticsEvent, "sp_server_diagnostics_query_processing_warning.xml"); @@ -441,10 +441,20 @@ await DarlingMcpTestData.ExecAsync(connection, ct, Assert.Contains("the absence is a measurement", broker.GetProperty("message").GetString()!, StringComparison.Ordinal); /* The data envelope carries the same witness pair. */ - var scheduler = JsonDocument.Parse(await DarlingMcpHealthParserTools.GetSchedulerIssues(postgres, ServerName)).RootElement; + var schedulerJson = await DarlingMcpHealthParserTools.GetSchedulerIssues(postgres, ServerName); + var scheduler = JsonDocument.Parse(schedulerJson).RootElement; Assert.True(scheduler.GetProperty("source_observed").GetBoolean()); Assert.Equal(t.ToString("o"), scheduler.GetProperty("last_captured_at").GetString()); + /* #4452: the utilization fields the planted high-CPU sample carries — SQL CPU pinned at 94, + other-process CPU 4, idle 2, memory 100 — come back on the tool's own output text. This is + the port's product-path pin: it asserts on the served JSON text (not typed properties) so it + also compiles, unchanged, against the pre-port code, where these fields are null or absent. */ + Assert.Contains("\"sql_cpu_utilization\":94", schedulerJson, StringComparison.Ordinal); + Assert.Contains("\"other_process_cpu\":4", schedulerJson, StringComparison.Ordinal); + Assert.Contains("\"system_idle\":2", schedulerJson, StringComparison.Ordinal); + Assert.Contains("\"memory_utilization\":100", schedulerJson, StringComparison.Ordinal); + /* an unknown server resolves to the listing error. */ Assert.StartsWith("Could not resolve server.", McpHelpers.ErrorMessageOf(await DarlingMcpHealthParserTools.GetSystemHealth(postgres, "darling-no-such-server")), StringComparison.Ordinal); diff --git a/Darling/Darling.Tests/Fixtures/SystemHealth/scheduler_monitor.xml b/Darling/Darling.Tests/Fixtures/SystemHealth/scheduler_monitor.xml deleted file mode 100644 index bae3a49deb..0000000000 --- a/Darling/Darling.Tests/Fixtures/SystemHealth/scheduler_monitor.xml +++ /dev/null @@ -1,10 +0,0 @@ - - 10 - 3 - 2WARNING - 1true - 0false - 1true - 4500 - 4 - diff --git a/Darling/Darling.Tests/Fixtures/SystemHealth/scheduler_monitor_high_sql_cpu.xml b/Darling/Darling.Tests/Fixtures/SystemHealth/scheduler_monitor_high_sql_cpu.xml new file mode 100644 index 0000000000..798be8e0df --- /dev/null +++ b/Darling/Darling.Tests/Fixtures/SystemHealth/scheduler_monitor_high_sql_cpu.xml @@ -0,0 +1,16 @@ + + + 0 + 0 + 94 + 2 + 2233593750 + 30312500 + 505 + 4096 + 100 + diff --git a/Darling/Darling.Tests/Fixtures/SystemHealth/scheduler_monitor_low_memory.xml b/Darling/Darling.Tests/Fixtures/SystemHealth/scheduler_monitor_low_memory.xml new file mode 100644 index 0000000000..0bd959d45d --- /dev/null +++ b/Darling/Darling.Tests/Fixtures/SystemHealth/scheduler_monitor_low_memory.xml @@ -0,0 +1,16 @@ + + + 0 + 0 + 3 + 95 + 176250000 + 7187500 + 99 + 77824 + 50 + diff --git a/Darling/Darling.Tests/Fixtures/SystemHealth/scheduler_monitor_normal.xml b/Darling/Darling.Tests/Fixtures/SystemHealth/scheduler_monitor_normal.xml new file mode 100644 index 0000000000..7f0f5c74be --- /dev/null +++ b/Darling/Darling.Tests/Fixtures/SystemHealth/scheduler_monitor_normal.xml @@ -0,0 +1,16 @@ + + + 0 + 0 + 3 + 95 + 176250000 + 7187500 + 99 + 77824 + 100 + diff --git a/Darling/Darling.Tests/Fixtures/SystemHealth/scheduler_monitor_other_process_cpu.xml b/Darling/Darling.Tests/Fixtures/SystemHealth/scheduler_monitor_other_process_cpu.xml new file mode 100644 index 0000000000..13dad6fca4 --- /dev/null +++ b/Darling/Darling.Tests/Fixtures/SystemHealth/scheduler_monitor_other_process_cpu.xml @@ -0,0 +1,16 @@ + + + 0 + 0 + 3 + 40 + 176250000 + 7187500 + 99 + 77824 + 100 + diff --git a/Darling/Darling.Tests/McpToolGuideHeads.HealthParser.cs b/Darling/Darling.Tests/McpToolGuideHeads.HealthParser.cs index 641963dd93..72044483d7 100644 --- a/Darling/Darling.Tests/McpToolGuideHeads.HealthParser.cs +++ b/Darling/Darling.Tests/McpToolGuideHeads.HealthParser.cs @@ -42,7 +42,7 @@ private static readonly (string Tool, string Fact)[] GateFacts = ("get_health_parser_memory_node_oom", "Ungated: every recorded OOM is returned."), ("get_health_parser_severe_errors", "Gated: severity 19 or higher only, benign connection-reset error numbers excluded"), ("get_health_parser_io_issues", "Gated: WARNING-state results only."), - ("get_health_parser_scheduler_issues", "Gated: WARNING-state results only."), + ("get_health_parser_scheduler_issues", "Gated: only samples where SQL Server's own CPU is pinned, other processes are using half the box or more, or memory utilization has dropped to half or below."), ("get_health_parser_memory_conditions", "Gated: only snapshots whose last notification is RESOURCE_MEMPHYSICAL_LOW."), ("get_health_parser_cpu_tasks", "Gated: WARNING-state results with at least 10 pending tasks only."), ("get_health_parser_memory_broker", "Gated: RESOURCE_MEMPHYSICAL_LOW notifications only."), diff --git a/Darling/Darling.Tests/McpToolsListBudget/DarlingMcpHealthParserTools.txt b/Darling/Darling.Tests/McpToolsListBudget/DarlingMcpHealthParserTools.txt index 10e99f6ce5..0f65baaecd 100644 --- a/Darling/Darling.Tests/McpToolsListBudget/DarlingMcpHealthParserTools.txt +++ b/Darling/Darling.Tests/McpToolsListBudget/DarlingMcpHealthParserTools.txt @@ -29,7 +29,7 @@ param get_health_parser_memory_node_oom.hours_back 41 param get_health_parser_memory_node_oom.limit 38 param get_health_parser_memory_node_oom.server_name 28 -tool get_health_parser_scheduler_issues 446 +tool get_health_parser_scheduler_issues 536 param get_health_parser_scheduler_issues.as_of 167 param get_health_parser_scheduler_issues.hours_back 41 param get_health_parser_scheduler_issues.limit 38 diff --git a/Darling/Darling.Tests/McpToolsListBudgetTests.cs b/Darling/Darling.Tests/McpToolsListBudgetTests.cs index 1698829bc5..278d2e0a35 100644 --- a/Darling/Darling.Tests/McpToolsListBudgetTests.cs +++ b/Darling/Darling.Tests/McpToolsListBudgetTests.cs @@ -184,7 +184,10 @@ shared 32 KB budget and is not a served description either. */ this PR's own get_store_host (+616) and dev's #4279 Lite-parity head change (+181). Constant set to the value McpToolsListBudgetTests itself measured on the merged tree, not the two deltas added by hand. */ // #4442: get_read_latency's served block. - private const int TotalCeilingBytes = 176_182; + // #4452 (merge): re-measured on the tree combining dev's #4442 get_read_latency addition with this + // branch's scheduler-issues tool description growth (+95). Constant set to the value + // McpToolsListBudgetTests itself measured on the merged tree, not the two deltas added by hand. + private const int TotalCeilingBytes = 176_277; diff --git a/Darling/Darling.Tests/SystemHealthParserTests.cs b/Darling/Darling.Tests/SystemHealthParserTests.cs index a120c137c9..44b31edb86 100644 --- a/Darling/Darling.Tests/SystemHealthParserTests.cs +++ b/Darling/Darling.Tests/SystemHealthParserTests.cs @@ -27,7 +27,7 @@ public class SystemHealthParserTests private static string LoadFixture(string name) => File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "Fixtures", "SystemHealth", name)); - private static readonly DateTime SchedulerTime = new(2026, 7, 5, 11, 58, 0, 123, DateTimeKind.Utc); + private static readonly DateTime SchedulerHighCpuTime = new(2026, 9, 26, 22, 8, 48, 939, DateTimeKind.Utc); private static readonly DateTime ErrorTime = new(2026, 7, 5, 12, 0, 5, 500, DateTimeKind.Utc); private static readonly DateTime ResourceTime = new(2026, 7, 5, 12, 1, 0, 0, DateTimeKind.Utc); private static readonly DateTime BrokerTime = new(2026, 7, 5, 12, 2, 10, 250, DateTimeKind.Utc); @@ -41,20 +41,75 @@ private static string LoadFixture(string name) => // ── SchedulerIssues (scheduler_monitor_system_health_ring_buffer_recorded) ── [Fact] - public void SchedulerIssue_ShredsEveryColumn() + public void SchedulerIssue_ShredsEveryColumn_HighSqlCpu() { - var r = SystemHealthParser.ParseSchedulerIssue(LoadFixture("scheduler_monitor.xml")); + // Real field capture: process_utilization=94, system_idle=2 -> other = 100-94-2=4; + // working_set_delta 4096 bytes -> 4096/1048576 = 0.00390625 MB, rounded to 0.00. + var r = SystemHealthParser.ParseSchedulerIssue(LoadFixture("scheduler_monitor_high_sql_cpu.xml")); Assert.NotNull(r); - Assert.Equal(SchedulerTime, r!.EventTime); - Assert.Equal(10, r.SchedulerId); - Assert.Equal(3, r.CpuId); - Assert.Equal("WARNING", r.Status); // data[@name="status"]/text, NOT the numeric value - Assert.True(r.IsOnline); - Assert.False(r.IsRunnable); - Assert.True(r.IsRunning); - Assert.Equal(4500L, r.NonYieldingTimeMs); // data[@name="non_yielding_time"] (no _ms suffix in XML) - Assert.Equal(4L, r.ThreadQuantumMs); // data[@name="thread_quantum"] + Assert.Equal(SchedulerHighCpuTime, r!.EventTime); + Assert.Equal(94, r.SqlCpuUtilization); + Assert.Equal(4, r.OtherProcessCpu); + Assert.Equal(2, r.SystemIdle); + Assert.Equal(100, r.MemoryUtilization); + Assert.Equal(505L, r.PageFaults); + Assert.Equal(0.00m, r.WorkingSetDeltaMb); + Assert.True(SystemHealthSignificance.IsSignificant(r)); // SQL CPU 94 >= 90 + } + + [Fact] + public void SchedulerIssue_ShredsEveryColumn_Normal() + { + // Real field capture: process_utilization=3, system_idle=95 -> other = 100-3-95=2; + // working_set_delta 77824 bytes -> 77824/1048576 = 0.074218... MB, rounded to 0.07. + var r = SystemHealthParser.ParseSchedulerIssue(LoadFixture("scheduler_monitor_normal.xml")); + + Assert.NotNull(r); + Assert.Equal(3, r!.SqlCpuUtilization); + Assert.Equal(2, r.OtherProcessCpu); + Assert.Equal(95, r.SystemIdle); + Assert.Equal(100, r.MemoryUtilization); + Assert.Equal(99L, r.PageFaults); + Assert.Equal(0.07m, r.WorkingSetDeltaMb); + Assert.False(SystemHealthSignificance.IsSignificant(r)); + } + + [Fact] + public void SchedulerIssue_OtherProcessCpu_Significant() + { + // Derived from the real "normal" capture (system_idle changed 95 -> 40) to exercise the + // other-process-CPU arm: other = 100-3-40=57 >= 50. + var r = SystemHealthParser.ParseSchedulerIssue(LoadFixture("scheduler_monitor_other_process_cpu.xml")); + Assert.NotNull(r); + Assert.Equal(57, r!.OtherProcessCpu); + Assert.True(SystemHealthSignificance.IsSignificant(r)); + } + + [Fact] + public void SchedulerIssue_LowMemory_Significant() + { + // Derived from the real "normal" capture (memory_utilization changed 100 -> 50) to exercise + // the low-memory arm: 50 <= 50. + var r = SystemHealthParser.ParseSchedulerIssue(LoadFixture("scheduler_monitor_low_memory.xml")); + Assert.NotNull(r); + Assert.Equal(50, r!.MemoryUtilization); + Assert.True(SystemHealthSignificance.IsSignificant(r)); + } + + [Theory] + [InlineData(90, 0, 100, true)] // sql cpu boundary: 90 is significant + [InlineData(89, 0, 100, false)] // one under, not significant + [InlineData(0, 50, 100, true)] // other-process boundary: 50 is significant (sql=0,idle=50->other=50) + [InlineData(0, 51, 100, false)] // other-process 49, not significant (idle=51->other=49) + [InlineData(0, 100, 50, true)] // memory boundary: 50 is significant + [InlineData(0, 100, 51, false)] // memory 51, not significant + [InlineData(null, null, null, false)] // all null -> not significant (SQL null comparison is false) + public void SchedulerIssue_SignificanceBoundaries(int? sqlCpu, int? idle, int? mem, bool expected) + { + var other = sqlCpu is { } sc && idle is { } id ? 100 - sc - id : (int?)null; + var r = new SchedulerIssueRecord { SqlCpuUtilization = sqlCpu, OtherProcessCpu = other, SystemIdle = idle, MemoryUtilization = mem }; + Assert.Equal(expected, SystemHealthSignificance.IsSignificant(r)); } // ── SevereErrors (error_reported) ── @@ -385,7 +440,7 @@ public void ParseEvents_DispatchesEachEventToItsCategory() { var events = new (string?, string?)[] { - (SystemHealthParser.SchedulerMonitorEvent, LoadFixture("scheduler_monitor.xml")), + (SystemHealthParser.SchedulerMonitorEvent, LoadFixture("scheduler_monitor_high_sql_cpu.xml")), (SystemHealthParser.ErrorReportedEvent, LoadFixture("error_reported.xml")), (SystemHealthParser.SpServerDiagnosticsEvent, LoadFixture("sp_server_diagnostics_system.xml")), (SystemHealthParser.SpServerDiagnosticsEvent, LoadFixture("sp_server_diagnostics_resource.xml")), @@ -410,7 +465,7 @@ public void ParseEvents_DispatchesEachEventToItsCategory() Assert.Single(result.MemoryConditions); Assert.Single(result.CpuTasks); Assert.Equal(2, result.IoIssues.Count); - Assert.Equal(10, result.SchedulerIssues[0].SchedulerId); + Assert.Equal(94, result.SchedulerIssues[0].SqlCpuUtilization); Assert.Equal(823, result.SevereErrors[0].ErrorNumber); Assert.Equal(6, result.SystemHealth[0].BadPagesDetected); } diff --git a/Darling/Darling.Tests/TsqlConventionGuardTests.cs b/Darling/Darling.Tests/TsqlConventionGuardTests.cs index 6c7f324f0c..b5c76dc121 100644 --- a/Darling/Darling.Tests/TsqlConventionGuardTests.cs +++ b/Darling/Darling.Tests/TsqlConventionGuardTests.cs @@ -1431,6 +1431,10 @@ where the bound has nothing to compare against. */ "PerformanceMonitor.Collectors/StallWaitProbe.cs FitsUnderBudget", "PerformanceMonitor.Common/SystemHealthParser.cs GbFromBytes", "PerformanceMonitor.Common/SystemHealthParser.cs GbFromKb", + /* #4452: the same expression-bodied one-liner shape as GbFromBytes/GbFromKb just above — it strands + only its own numeric literals (1048576, the rounding precision), not T-SQL or a tempdb label, so + no census reads a site of that kind here. */ + "PerformanceMonitor.Common/SystemHealthParser.cs MbFromBytes", "PerformanceMonitor.Notifications/WebhookAlertService.cs DeriveResourceDatabase", "Darling/PerformanceMonitor.Darling.Analysis/PgBaselineProvider.cs IsCommandTimeout", /* #4276: the same expression-bodied classifier shape as IsCommandTimeout just above — it strands diff --git a/Darling/Darling.Tests/ViewerSystemEventsTests.cs b/Darling/Darling.Tests/ViewerSystemEventsTests.cs index 8aa4f05879..8ccbd5f612 100644 --- a/Darling/Darling.Tests/ViewerSystemEventsTests.cs +++ b/Darling/Darling.Tests/ViewerSystemEventsTests.cs @@ -98,21 +98,24 @@ public void DatabaseNameMapRead_ReferencesColumnsThatExistInTheGeneratedTable() } } - // ── Significance: Scheduler Issues (status = WARNING) ── + // ── Significance: Scheduler Issues (SQL CPU >= 90, other-process CPU >= 50, or memory <= 50) ── [Theory] - [InlineData("WARNING", true)] - [InlineData("OK", false)] - [InlineData("", false)] - [InlineData(null, false)] - public void SchedulerIssue_SignificantOnlyWhenWarning(string? status, bool expected) => - Assert.Equal(expected, SystemEventSignificance.IsSignificant(new SchedulerIssueRecord { Status = status })); + [InlineData(90, 0, 100, true)] + [InlineData(89, 0, 100, false)] + [InlineData(0, 50, 100, true)] + [InlineData(0, 49, 100, false)] + [InlineData(0, 0, 50, true)] + [InlineData(0, 0, 51, false)] + [InlineData(null, null, null, false)] + public void SchedulerIssue_SignificantOnThresholds(int? sqlCpu, int? other, int? mem, bool expected) => + Assert.Equal(expected, SystemEventSignificance.IsSignificant(new SchedulerIssueRecord { SqlCpuUtilization = sqlCpu, OtherProcessCpu = other, MemoryUtilization = mem })); [Fact] public void SchedulerIssue_Fixture_IsSignificant() { - var record = SystemHealthParser.ParseSchedulerIssue(LoadFixture("scheduler_monitor.xml"))!; - Assert.Equal("WARNING", record.Status); + var record = SystemHealthParser.ParseSchedulerIssue(LoadFixture("scheduler_monitor_high_sql_cpu.xml"))!; + Assert.Equal(94, record.SqlCpuUtilization); Assert.True(SystemEventSignificance.IsSignificant(record)); } @@ -350,7 +353,7 @@ public void ShredThenFilter_KeepsOnlySignificantRows_AcrossAllCategories() { var events = new (string?, string?)[] { - (SystemHealthParser.SchedulerMonitorEvent, LoadFixture("scheduler_monitor.xml")), + (SystemHealthParser.SchedulerMonitorEvent, LoadFixture("scheduler_monitor_high_sql_cpu.xml")), (SystemHealthParser.ErrorReportedEvent, LoadFixture("error_reported.xml")), (SystemHealthParser.SpServerDiagnosticsEvent, LoadFixture("sp_server_diagnostics_system.xml")), (SystemHealthParser.SpServerDiagnosticsEvent, LoadFixture("sp_server_diagnostics_resource.xml")), diff --git a/Darling/PerformanceMonitor.Darling.Service/Mcp/DarlingMcpHealthParserTools.cs b/Darling/PerformanceMonitor.Darling.Service/Mcp/DarlingMcpHealthParserTools.cs index 18e2664591..be1fdb812c 100644 --- a/Darling/PerformanceMonitor.Darling.Service/Mcp/DarlingMcpHealthParserTools.cs +++ b/Darling/PerformanceMonitor.Darling.Service/Mcp/DarlingMcpHealthParserTools.cs @@ -221,7 +221,7 @@ public static async Task GetIOIssues( catch (Exception ex) when (ex is not OperationCanceledException) { return McpHelpers.FormatError("get_health_parser_io_issues", ex); } } - [McpServerTool(Name = "get_health_parser_scheduler_issues"), Description("Gets scheduler issues from system_health (non-yielding schedulers, scheduler-monitor warnings) over an event_time window ending at as_of, newest first. Gated: WARNING-state results only. An empty answer with status empty is a real result: nothing in this window passed the gate, and events_in_window counts what was captured and filtered out. status unavailable with source_observed false is no evidence either way. <> Gets scheduler issues from system_health: non-yielding schedulers and scheduler-monitor warnings, with the scheduler/cpu ids, online/runnable/running state, and non-yielding time. " + McpToolGuideTopics.SystemHealthEmptyWindows)] + [McpServerTool(Name = "get_health_parser_scheduler_issues"), Description("Gets scheduler-monitor utilization samples from system_health over an event_time window ending at as_of, newest first. Gated: only samples where SQL Server's own CPU is pinned, other processes are using half the box or more, or memory utilization has dropped to half or below. An empty answer with status empty is a real result: nothing in this window passed the gate, and events_in_window counts what was captured and filtered out. status unavailable with source_observed false is no evidence either way. <> Gets scheduler-monitor utilization samples from system_health: SQL Server CPU percent, other-process CPU percent, idle CPU percent, memory utilization percent, page faults, and working-set change in MB. Significant when SQL CPU is pinned (>= 90), other-process CPU is high (>= 50), or memory utilization is low (<= 50). " + McpToolGuideTopics.SystemHealthEmptyWindows)] public static async Task GetSchedulerIssues( NpgsqlDataSource postgres, [Description("Server name or display name.")] string? server_name = null, @@ -239,7 +239,7 @@ public static async Task GetSchedulerIssues( if (c.EarlyReturn != null) return c.EarlyReturn; if (c.Rows.Count == 0) return await EmptyAsync(postgres, c, hours_back, SystemHealthParser.SchedulerMonitorEvent, - "none was a scheduler-monitor record in the WARNING state", cancellationToken); + "none was a significant scheduler-monitor sample (SQL CPU, other-process CPU, or memory utilization outside the warning thresholds)", cancellationToken); return JsonSerializer.Serialize(new { @@ -252,14 +252,12 @@ public static async Task GetSchedulerIssues( issues = c.Rows.Take(limit).Select(r => new { event_time = r.EventTime?.ToString("o"), - scheduler_id = r.SchedulerId, - cpu_id = r.CpuId, - status = r.Status, - is_online = r.IsOnline, - is_runnable = r.IsRunnable, - is_running = r.IsRunning, - non_yielding_time_ms = r.NonYieldingTimeMs, - thread_quantum_ms = r.ThreadQuantumMs + sql_cpu_utilization = r.SqlCpuUtilization, + other_process_cpu = r.OtherProcessCpu, + system_idle = r.SystemIdle, + memory_utilization = r.MemoryUtilization, + page_faults = r.PageFaults, + working_set_delta_mb = r.WorkingSetDeltaMb }) }, McpHelpers.JsonOptions); } diff --git a/Darling/PerformanceMonitor.Darling.Viewer/ViewerDataService.SystemEvents.cs b/Darling/PerformanceMonitor.Darling.Viewer/ViewerDataService.SystemEvents.cs index 70f348dc33..0f4ffed435 100644 --- a/Darling/PerformanceMonitor.Darling.Viewer/ViewerDataService.SystemEvents.cs +++ b/Darling/PerformanceMonitor.Darling.Viewer/ViewerDataService.SystemEvents.cs @@ -40,18 +40,16 @@ public static string Local(DateTime? utc) => utc is { } t ? ViewerTimeHelper.ForDisplay(t).ToString("yyyy-MM-dd HH:mm:ss") : ""; } -/// One scheduler-monitor WARNING row (Scheduler Issues sub-tab). Mirrors *_SchedulerIssues. +/// One scheduler-monitor utilization sample (Scheduler Issues sub-tab), flagged the way sp_HealthParser flags this section: SQL CPU pinned, other-process CPU high, or memory utilization low. public sealed class SchedulerIssueRow(SchedulerIssueRecord record) { public string EventTimeLocal => SystemEventRowFormat.Local(record.EventTime); - public int? SchedulerId => record.SchedulerId; - public int? CpuId => record.CpuId; - public string? Status => record.Status; - public bool? IsOnline => record.IsOnline; - public bool? IsRunnable => record.IsRunnable; - public bool? IsRunning => record.IsRunning; - public long? NonYieldingTimeMs => record.NonYieldingTimeMs; - public long? ThreadQuantumMs => record.ThreadQuantumMs; + public int? SqlCpuUtilization => record.SqlCpuUtilization; + public int? OtherProcessCpu => record.OtherProcessCpu; + public int? SystemIdle => record.SystemIdle; + public int? MemoryUtilization => record.MemoryUtilization; + public long? PageFaults => record.PageFaults; + public decimal? WorkingSetDeltaMb => record.WorkingSetDeltaMb; } /// diff --git a/Darling/PerformanceMonitor.Darling.Viewer/ViewerServerTab.xaml b/Darling/PerformanceMonitor.Darling.Viewer/ViewerServerTab.xaml index a14f9c8217..4e944b11b8 100644 --- a/Darling/PerformanceMonitor.Darling.Viewer/ViewerServerTab.xaml +++ b/Darling/PerformanceMonitor.Darling.Viewer/ViewerServerTab.xaml @@ -3219,17 +3219,15 @@