From df8befe8cb0ba550ab1ec58e287f994bca4550af Mon Sep 17 00:00:00 2001 From: Erik Darling <2136037+erikdarlingdata@users.noreply.github.com> Date: Tue, 14 Jul 2026 06:29:00 -0400 Subject: [PATCH] Tier-2 batch-2b: priority-boost + lightweight-pooling config arms Two server-config classification arms in the shared FactScorer (land in Darling + Lite; the deprecated Dashboard collector is untouched). server_config already collects the full sys.configurations set, so the two ACTIVE fact collectors (DuckDbFactCollector + PgFactCollector) just add 'priority boost' + 'lightweight pooling' to their curated fact list, emitting CONFIG_PRIORITY_BOOST / CONFIG_LIGHTWEIGHT_POOLING. - Priority boost enabled -> WARNING (install/50:368): above-normal Windows scheduling priority, starves OS threads. - Lightweight pooling enabled -> WARNING (install/50:401): breaks OLEDB etc. Each scores 0.9 (WARNING band) when value_in_use == 1 -- rare, clearly-wrong settings, so higher than the 0.4 config-advisory base -- and roots via InferenceEngine.ConfigAdvisoryRootKeys. Sourced advice in FactAdvice. FactScorerTests (Lite + Dashboard) pin the band + dormancy. 71 Lite FactScorer tests green; Darling analysis builds clean. Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 2 + .../PgFactCollector.Config.cs | 6 ++- Dashboard.Tests/FactScorerTests.cs | 49 +++++++++++++++++++ Lite.Tests/FactScorerTests.cs | 49 +++++++++++++++++++ Lite/Analysis/DuckDbFactCollector.Config.cs | 6 ++- PerformanceMonitor.Analysis/FactAdvice.cs | 16 ++++++ PerformanceMonitor.Analysis/FactScorer.cs | 14 ++++++ .../InferenceEngine.cs | 6 +++ 8 files changed, 146 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d997fabd..91149116 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- **Analysis engine: two server-config issue-classification arms (priority boost + lightweight pooling) in the shared FactScorer (Tier-2 parity, batch-2b)** ([#1520]) — the shared `PerformanceMonitor.Analysis` scorer (ONE code path for Lite, the deprecated Dashboard, AND Darling) now classifies two dangerous `sp_configure` settings the Dashboard's `config.critical_issues` analyzer flagged as WARNINGs but the ported engine did not. Both are near-pure additions over data ALREADY collected: `server_config` accumulates the FULL `sys.configurations` set, so the two ACTIVE fact collectors (Lite `DuckDbFactCollector` + Darling `PgFactCollector`) simply add `'priority boost'` + `'lightweight pooling'` to their curated fact-emission list, emitting `CONFIG_PRIORITY_BOOST` / `CONFIG_LIGHTWEIGHT_POOLING` — **the deprecated Dashboard's `SqlServerFactCollector` is untouched** (it just never emits the facts). (1) **Priority boost enabled** (`install/50_configuration_issues_analyzer.sql:368`, WARNING) — above-normal Windows scheduling priority that starves OS-critical threads. (2) **Lightweight pooling / fiber mode enabled** (`install/50:401`, WARNING) — breaks OLEDB and other in-process components. Each scores the WARNING band (`0.9` in `FactScorer`) when `value_in_use == 1` — surfacing prominently, since these are rare, clearly-wrong settings rather than routine tuning choices like MAXDOP/CTFP (which stay at the low `0.4` config-advisory base) — and roots a standalone card (added to `InferenceEngine.ConfigAdvisoryRootKeys`). Sourced advice (the Dashboard messages + the `sp_configure ..., 0; RECONFIGURE;` remediation) added to `FactAdvice`. `FactScorerTests` in BOTH `Lite.Tests` and `Dashboard.Tests` pin the WARNING band when enabled and dormancy when disabled; the arms are silent on every existing fixture (no seeder enables either setting). Lands in Darling + Lite; the deprecated Dashboard is not developed further. - **Analysis engine: two tempdb-deep issue-classification arms in the shared FactScorer (Tier-2 parity)** ([#1519]) — the #1517 companion: the shared `PerformanceMonitor.Analysis` scorer (ONE code path for Lite, the deprecated Dashboard, AND Darling) now classifies two tempdb problems the deprecated Dashboard's `report.tempdb_pressure` / `report.tempdb_contention_analysis` SQL flagged but the ported engine only partially covered. Both are PURE `FactScorer` additions over facts the collectors ALREADY emit — no collector, schema, or `install/*.sql` change — each citing its source SQL line in a code comment. (1) **Version-store pressure by ABSOLUTE size** — `TEMPDB_USAGE` previously scored the space-FRACTION only, blind to a multi-GB version store sitting in a barely-full tempdb (a long-running RCSI/snapshot transaction pinning row versions). It now scores the WORSE of space-fraction and version-store size, reading `max_version_store_mb` (already in the fact metadata — the same key `ComposeTempdbUsage` reads) and tiering it to `report.tempdb_pressure`'s `pressure_level` CASE (`install/47_create_reporting_views.sql` lines 1431-1433): > 5000 MB CRITICAL-tier, > 2000 MB HIGH, > 1000 MB MEDIUM; `tempdb_contention_analysis` corroborates the 1 GB bar (`version_store_high_warning`, install/47:2504). The tempdb recommendation card now names the version store as the driver whenever it clears the 1 GB bar — even if another consumer is nominally larger — mirroring the source view keying `pressure_level` purely on version-store size. (2) **Allocation / PFS-GAM-SGAM PAGELATCH contention** — `ScoreWaitFact` had PAGEIOLATCH + LATCH_EX/SH but no PAGELATCH arm. It now scores the `PAGELATCH_UP` wait fact by ABSOLUTE `wait_time_ms` (the same absolute-ms shape as the THREADPOOL gate), tripping at > 10000 ms -> MEDIUM per `tempdb_contention_analysis`'s `contention_level` CASE (install/47:2515) — read from the SAME server-wide `wait_stats` the view reads, so it is a faithful port; a new advice block (routed through the wait-family `ComposeWaitByKey`, so the card states the actual wait total) names the add-tempdb-files / TF 1118 fix (install/47:2522, 2535). Base severity maxes at 1.0 (WARNING) like every base fact — the CRITICAL band stays reserved for corroboration. NOTE: the view's stronger CRITICAL "allocation contention" signal (`allocation_contention_warning`, a tempdb-scoped `dm_os_waiting_tasks` snapshot computed in `install/34`) is NOT carried in any fact, so per the pure-scorer scope it was left to the PAGELATCH_UP wait rather than adding collection. `FactScorer` tests in BOTH `Lite.Tests` and `Dashboard.Tests` pin every tier (fires at the right band, stays silent below the bar, and the version-store arm never lowers a worse space-fraction score). No test-seeder fix was needed (unlike #1517's Query-Store default): the clean-server fixture seeds a 10 MB version store and no PAGELATCH waits, and the largest version store across all seeders is 900 MB — below the 1 GB bar. Behavior of the existing arms is unchanged. - **Analysis engine: three more issue-classification arms in the shared FactScorer (Tier-2 parity)** ([#1517]) — the shared `PerformanceMonitor.Analysis` scorer/inference brain (ONE code path for Lite, the deprecated Dashboard, AND Darling) now classifies three problems the deprecated Dashboard's `report.*` / `config.critical_issues` SQL flagged but the ported engine had dropped. All three are pure `FactScorer` additions over facts the collectors ALREADY emit — no collector, schema, or `install/*.sql` change — so they light up in all three apps' recommendations at once, each citing its source SQL line in a code comment. (1) **Query Store disabled** — an INFO advisory folded into the existing `DB_CONFIG` finding, detected as `query_store_on_count < database_count` (user DBs only, system DBs excluded on both counts), mirroring `install/50_configuration_issues_analyzer.sql`'s INFO Query-Store-off issue (line 83); the DB-config recommendation now lists "N with Query Store off" and appends enable-QS guidance. (2) **Security-cache (TokenAndPermUserStore) growth** — a flat WARNING when the `USERSTORE_TOKENPERM` memory clerk reaches >= 1 GB, read from the otherwise context-only `MEMORY_CLERKS` fact (the clerk size rides in metadata keyed by clerk_type), mirroring `install/50`'s WARNING at `pages_kb / 1024 / 1024 >= 1.0` (lines 562, 583); a new recommendation names the `DBCC FREESYSTEMCACHE('TokenAndPermUserStore')` relief plus the ad-hoc / dynamic-SQL churn cause. (3) **Runnable-queue / worker pressure** — a STANDALONE arm rooting the collected `cpu_scheduler_stats` snapshot on `total_runnable_tasks_count`, tiered to `report.cpu_scheduler_pressure`'s `> 50 / > 20 / > 10` CASE plus the collector's `runnable_tasks_warning` small-box fallback (`install/47_create_reporting_views.sql` lines 1839-1844), additive to and distinct from the #1494 runnable-queue -> THREADPOOL amplifier (kept unchanged). Base severity maxes at 1.0 (WARNING) like every base fact — the CRITICAL band stays reserved for amplifier-corroborated cases (the runnable-queue -> THREADPOOL escalation). `FactScorer` tests in BOTH `Lite.Tests` and `Dashboard.Tests` pin every tier (fires when it should, stays silent below the bar, at the right band) and that a co-present worse `DB_CONFIG` issue is left unchanged. Behavior of the existing arms is unchanged. - **Lite MCP: the 18 Darling-only tools ported (drains the MCP inventory ratchet to ZERO)** ([#1518]) — the mechanical `[McpServerTool]` companion to the #1504 / #1505 / #1509 viewer BUILDs: every tool the #1501 `CrossAppMcpToolInventoryPinTests` ratchet listed as Darling-only is now exposed by Lite's MCP host, so `KnownLiteMissingMcpTools` is **empty** (only the `get_blocked_process_reports` <-> `get_blocking` naming-drift pair remains, handled separately). The 18: `get_latch_stats`, `get_spinlock_stats`, `get_plan_cache_bloat`, `get_cpu_scheduler_pressure`, `get_resource_semaphore`, `get_default_trace_events`, `get_daily_summary`, `get_server_config_changes`, `get_database_config_changes`, `get_trace_flag_changes`, and the eight `get_health_parser_*` tools (system_health / severe_errors / io_issues / scheduler_issues / memory_conditions / cpu_tasks / memory_broker / memory_node_oom). Each is a thin wrapper over the reader Lite ALREADY had from the viewer builds — no new collection, no live-server hit: latch/spinlock wrap the latest-snapshot readers, plan-cache-bloat folds the true-total summary + composition grid + the shared `ClassifyPlanCacheBloat` banding, cpu-scheduler-pressure surfaces the latest scheduler snapshot with the collector's warning flags, daily-summary wraps the Performance Calendar aggregate (composite health band), the three config-change tools wrap the shared `ConfigChangeDiff` histories, and default-trace + the eight health-parser tools wrap the System Events parse-on-read readers (gated by the shared `SystemHealthSignificance` / `DefaultTraceEventSignificance` — the SAME significant set the viewer shows). The ONE reader added is `GetResourceSemaphoreSnapshotAsync` (a latest-snapshot read of the FULL semaphore ceiling columns — target / max-target / total workspace memory + cumulative timeout/forced-grant counts — which the existing per-pool `GetMemoryGrantChartDataAsync` chart reader doesn't surface), so Lite's `get_resource_semaphore` matches the Dashboard/Darling shape rather than duplicating `get_memory_grants`. A raw naive-UTC `EventTime` / `ChangeTime` accessor was added to the System Events + Config Changes viewer row DTOs so the tools emit ISO-8601 timestamps (matching every other Lite MCP tool) instead of the grids' display-local strings. All 18 are registered in `McpHostService` and documented in the MCP server instructions. Lite-only — no Darling, Full Dashboard, or `install/*.sql` changes. @@ -316,6 +317,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#1513]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/1513 [#1514]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/1514 [#1515]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/1515 +[#1520]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/1520 [#1519]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/1519 [#1518]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/1518 [#1516]: https://github.com/erikdarlingdata/PerformanceMonitor/pull/1516 diff --git a/Darling/PerformanceMonitor.Darling.Analysis/PgFactCollector.Config.cs b/Darling/PerformanceMonitor.Darling.Analysis/PgFactCollector.Config.cs index 97e4527c..23147d86 100644 --- a/Darling/PerformanceMonitor.Darling.Analysis/PgFactCollector.Config.cs +++ b/Darling/PerformanceMonitor.Darling.Analysis/PgFactCollector.Config.cs @@ -33,7 +33,9 @@ AND configuration_name IN ( 'max degree of parallelism', 'max server memory (MB)', 'min server memory (MB)', - 'max worker threads' + 'max worker threads', + 'priority boost', + 'lightweight pooling' ) ) SELECT configuration_name, value_in_use @@ -74,6 +76,8 @@ private async Task CollectServerConfigFactsAsync(AnalysisContext context, List "CONFIG_MAX_MEMORY_MB", "min server memory (MB)" => "CONFIG_MIN_MEMORY_MB", "max worker threads" => "CONFIG_MAX_WORKER_THREADS", + "priority boost" => "CONFIG_PRIORITY_BOOST", + "lightweight pooling" => "CONFIG_LIGHTWEIGHT_POOLING", _ => null }; diff --git a/Dashboard.Tests/FactScorerTests.cs b/Dashboard.Tests/FactScorerTests.cs index 1e04d57c..6d951772 100644 --- a/Dashboard.Tests/FactScorerTests.cs +++ b/Dashboard.Tests/FactScorerTests.cs @@ -486,6 +486,55 @@ public void Score_MemoryClerks_NoSecurityCacheClerk_DoesNotScore() Assert.Equal(0.0, facts.First(f => f.Key == "MEMORY_CLERKS").Severity, precision: 4); } + // batch-2b — priority boost enabled is a Dashboard WARNING (install/50:368). CONFIG_PRIORITY_BOOST + // bands WARNING (0.9) when value_in_use == 1, and scores 0 when disabled. + [Fact] + public void Score_ConfigPriorityBoost_Enabled_BandsWarning() + { + var facts = new List + { + new() { Source = "config", Key = "CONFIG_PRIORITY_BOOST", Value = 1, Metadata = new() { ["value_in_use"] = 1 } }, + }; + new FactScorer().ScoreAll(facts); + var f = facts.First(f => f.Key == "CONFIG_PRIORITY_BOOST"); + Assert.True(f.Severity >= 0.75 && f.Severity < 1.5, "priority boost bands WARNING when enabled"); + } + + [Fact] + public void Score_ConfigPriorityBoost_Disabled_DoesNotScore() + { + var facts = new List + { + new() { Source = "config", Key = "CONFIG_PRIORITY_BOOST", Value = 0, Metadata = new() { ["value_in_use"] = 0 } }, + }; + new FactScorer().ScoreAll(facts); + Assert.Equal(0.0, facts.First(f => f.Key == "CONFIG_PRIORITY_BOOST").Severity, precision: 4); + } + + // Lightweight pooling enabled is a Dashboard WARNING (install/50:401) — same shape. + [Fact] + public void Score_ConfigLightweightPooling_Enabled_BandsWarning() + { + var facts = new List + { + new() { Source = "config", Key = "CONFIG_LIGHTWEIGHT_POOLING", Value = 1, Metadata = new() { ["value_in_use"] = 1 } }, + }; + new FactScorer().ScoreAll(facts); + var f = facts.First(f => f.Key == "CONFIG_LIGHTWEIGHT_POOLING"); + Assert.True(f.Severity >= 0.75 && f.Severity < 1.5, "lightweight pooling bands WARNING when enabled"); + } + + [Fact] + public void Score_ConfigLightweightPooling_Disabled_DoesNotScore() + { + var facts = new List + { + new() { Source = "config", Key = "CONFIG_LIGHTWEIGHT_POOLING", Value = 0, Metadata = new() { ["value_in_use"] = 0 } }, + }; + new FactScorer().ScoreAll(facts); + Assert.Equal(0.0, facts.First(f => f.Key == "CONFIG_LIGHTWEIGHT_POOLING").Severity, precision: 4); + } + // ARM 1 — Query Store off on a user database is an INFO advisory carried by the DB_CONFIG fact, // detected as query_store_on_count < database_count (install/50:83 severity=INFO). Low 0.3 base = // INFO band, and DB_CONFIG roots at any positive severity (a ConfigAdvisoryRootKey). diff --git a/Lite.Tests/FactScorerTests.cs b/Lite.Tests/FactScorerTests.cs index 895497ea..f549d832 100644 --- a/Lite.Tests/FactScorerTests.cs +++ b/Lite.Tests/FactScorerTests.cs @@ -639,6 +639,55 @@ public void Score_MemoryClerks_NoSecurityCacheClerk_DoesNotScore() Assert.Equal(0.0, facts.First(f => f.Key == "MEMORY_CLERKS").Severity, precision: 4); } + // batch-2b — priority boost enabled is a Dashboard WARNING (install/50:368). CONFIG_PRIORITY_BOOST + // bands WARNING (0.9) when value_in_use == 1, and scores 0 when disabled. + [Fact] + public void Score_ConfigPriorityBoost_Enabled_BandsWarning() + { + var facts = new List + { + new() { Source = "config", Key = "CONFIG_PRIORITY_BOOST", Value = 1, Metadata = new() { ["value_in_use"] = 1 } }, + }; + new FactScorer().ScoreAll(facts); + var f = facts.First(f => f.Key == "CONFIG_PRIORITY_BOOST"); + Assert.True(f.Severity >= 0.75 && f.Severity < 1.5, "priority boost bands WARNING when enabled"); + } + + [Fact] + public void Score_ConfigPriorityBoost_Disabled_DoesNotScore() + { + var facts = new List + { + new() { Source = "config", Key = "CONFIG_PRIORITY_BOOST", Value = 0, Metadata = new() { ["value_in_use"] = 0 } }, + }; + new FactScorer().ScoreAll(facts); + Assert.Equal(0.0, facts.First(f => f.Key == "CONFIG_PRIORITY_BOOST").Severity, precision: 4); + } + + // Lightweight pooling enabled is a Dashboard WARNING (install/50:401) — same shape. + [Fact] + public void Score_ConfigLightweightPooling_Enabled_BandsWarning() + { + var facts = new List + { + new() { Source = "config", Key = "CONFIG_LIGHTWEIGHT_POOLING", Value = 1, Metadata = new() { ["value_in_use"] = 1 } }, + }; + new FactScorer().ScoreAll(facts); + var f = facts.First(f => f.Key == "CONFIG_LIGHTWEIGHT_POOLING"); + Assert.True(f.Severity >= 0.75 && f.Severity < 1.5, "lightweight pooling bands WARNING when enabled"); + } + + [Fact] + public void Score_ConfigLightweightPooling_Disabled_DoesNotScore() + { + var facts = new List + { + new() { Source = "config", Key = "CONFIG_LIGHTWEIGHT_POOLING", Value = 0, Metadata = new() { ["value_in_use"] = 0 } }, + }; + new FactScorer().ScoreAll(facts); + Assert.Equal(0.0, facts.First(f => f.Key == "CONFIG_LIGHTWEIGHT_POOLING").Severity, precision: 4); + } + // ARM 1 — Query Store off on a user database is an INFO advisory carried by the DB_CONFIG fact, // detected as query_store_on_count < database_count (install/50:83 severity=INFO). Low 0.3 base = // INFO band, and DB_CONFIG roots at any positive severity (a ConfigAdvisoryRootKey). diff --git a/Lite/Analysis/DuckDbFactCollector.Config.cs b/Lite/Analysis/DuckDbFactCollector.Config.cs index bd5fd7aa..b7c67d57 100644 --- a/Lite/Analysis/DuckDbFactCollector.Config.cs +++ b/Lite/Analysis/DuckDbFactCollector.Config.cs @@ -41,7 +41,9 @@ AND configuration_name IN ( 'max degree of parallelism', 'max server memory (MB)', 'min server memory (MB)', - 'max worker threads' + 'max worker threads', + 'priority boost', + 'lightweight pooling' ) ) SELECT configuration_name, value_in_use @@ -72,6 +74,8 @@ FROM latest "max server memory (MB)" => "CONFIG_MAX_MEMORY_MB", "min server memory (MB)" => "CONFIG_MIN_MEMORY_MB", "max worker threads" => "CONFIG_MAX_WORKER_THREADS", + "priority boost" => "CONFIG_PRIORITY_BOOST", + "lightweight pooling" => "CONFIG_LIGHTWEIGHT_POOLING", _ => null }; diff --git a/PerformanceMonitor.Analysis/FactAdvice.cs b/PerformanceMonitor.Analysis/FactAdvice.cs index 6db17849..a9ed13a2 100644 --- a/PerformanceMonitor.Analysis/FactAdvice.cs +++ b/PerformanceMonitor.Analysis/FactAdvice.cs @@ -2015,6 +2015,22 @@ private static Dictionary BuildAdviceTable() Remediation: "For tempdb-driven PAGEIOLATCH_EX, fix the spilling queries — update statistics with FULLSCAN to correct the cardinality estimates that produced too-small grants, or rewrite the operator that spills. For modification-workload pressure, batch large operations into smaller chunks so the buffer pool can flush between batches. If the storage itself is slow (write latency consistently above 10 ms on data, 2 ms on log), no amount of query tuning will recover it — the storage is the bottleneck and needs hardware-side investigation."); + t["CONFIG_PRIORITY_BOOST"] = new AdviceBlock( + Headline: + "Priority boost is enabled — SQL Server threads run at above-normal Windows scheduling priority, which can starve OS-critical threads", + Investigation: + "`sp_configure 'priority boost'` raises the Windows scheduling priority of SQL Server's threads above normal. Microsoft recommends against it for essentially all servers: it can starve OS threads (network, disk, cluster heartbeat), destabilize the instance, and cause connectivity or failover problems — with no reliable throughput gain. It is almost always a leftover from bad tuning advice rather than a deliberate, measured choice.", + Remediation: + "Disable it: `EXECUTE sp_configure 'priority boost', 0; RECONFIGURE;` — the change takes effect after a SQL Server service restart. There is effectively no workload for which priority boost is the right fix."); + + t["CONFIG_LIGHTWEIGHT_POOLING"] = new AdviceBlock( + Headline: + "Lightweight pooling (fiber mode) is enabled — it breaks OLEDB and other in-process components and is not recommended", + Investigation: + "`sp_configure 'lightweight pooling'` (fiber mode) switches SQL Server to fiber-based scheduling. It disables features that assume thread-based scheduling — most OLEDB providers (linked servers, some CLR, extended stored procedures) fail or misbehave — and its narrow high-end NUMA / context-switch benefit almost never applies. Microsoft recommends leaving it off for essentially all workloads.", + Remediation: + "Disable it: `EXECUTE sp_configure 'lightweight pooling', 0; RECONFIGURE;` — takes effect after a service restart. Only consider re-enabling it under a specific, measured high-end OLTP/NUMA scenario with vendor guidance, and never with linked servers / OLEDB in use."); + t["RESOURCE_SEMAPHORE"] = new AdviceBlock( Headline: "RESOURCE_SEMAPHORE waits — queries are queueing for memory grants because the workspace pool is exhausted", diff --git a/PerformanceMonitor.Analysis/FactScorer.cs b/PerformanceMonitor.Analysis/FactScorer.cs index 2af032ee..3b23dcf6 100644 --- a/PerformanceMonitor.Analysis/FactScorer.cs +++ b/PerformanceMonitor.Analysis/FactScorer.cs @@ -379,6 +379,20 @@ private static double ScoreConfigFact(Fact fact) case "CONFIG_MIN_MAX_MEMORY_NARROW": return 0.4; + // Priority boost enabled (value_in_use == 1) — a Dashboard WARNING (install/50_configuration_issues_analyzer.sql + // line 368: "Priority boost is enabled ... not recommended"): it hands SQL Server threads an + // above-normal Windows scheduling priority, starving OS-critical threads. It is rare and + // clearly-wrong (not a routine tuning choice like MAXDOP/CTFP), so it scores the WARNING band + // (0.9) — surfacing prominently when present — rather than the low 0.4 config-advisory base. + case "CONFIG_PRIORITY_BOOST": + return fact.Value == 1 ? 0.9 : 0.0; + + // Lightweight pooling / fiber mode enabled (value_in_use == 1) — a Dashboard WARNING + // (install/50:401: "Lightweight pooling (fiber mode) is enabled ... issues with OLEDB and other + // components"). Same rationale: rare, clearly-wrong, WARNING band (0.9). + case "CONFIG_LIGHTWEIGHT_POOLING": + return fact.Value == 1 ? 0.9 : 0.0; + // WS5 server-health advisories (advise-only — no Apply). Each carries the bad/good // signal in Value so it scores its 0.4 advisory base only when bad and 0 otherwise; // the noise-control gating (Express / small-RAM for LPIM, dumps>0, IFI-known) lives in diff --git a/PerformanceMonitor.Analysis/InferenceEngine.cs b/PerformanceMonitor.Analysis/InferenceEngine.cs index 134a6dba..e527a95d 100644 --- a/PerformanceMonitor.Analysis/InferenceEngine.cs +++ b/PerformanceMonitor.Analysis/InferenceEngine.cs @@ -45,6 +45,12 @@ public class InferenceEngine "CONFIG_CTFP", "CONFIG_MAX_MEMORY_MB", "CONFIG_MIN_MAX_MEMORY_NARROW", + // batch-2b: priority boost / lightweight pooling enabled — Dashboard WARNINGs. Score the + // WARNING band (0.9, FactScorer) and root a standalone card here so a rare, clearly-wrong + // scheduling setting surfaces on a quiet, healthy server (redundant with the 0.5 incident + // threshold at 0.9, but keeps every CONFIG_* fact in one rooting set). + "CONFIG_PRIORITY_BOOST", + "CONFIG_LIGHTWEIGHT_POOLING", // WS5: server-health advisories (advise-only). Each scores its 0.4 advisory base only when // bad (FactScorer) and roots its own standalone card here, bypassing the 0.5 incident // threshold — a standing server-health gap should surface on a quiet, healthy server.