Skip to content

Wire wait stats config as single source of truth (#215 part 2)#300

Merged
erikdarlingdata merged 1 commit intodevfrom
feature/wait-stats-config-part2
May 2, 2026
Merged

Wire wait stats config as single source of truth (#215 part 2)#300
erikdarlingdata merged 1 commit intodevfrom
feature/wait-stats-config-part2

Conversation

@erikdarlingdata
Copy link
Copy Markdown
Owner

Part 2 of #215. Wires the JSON config from Part 1 (#296) into the analysis pipeline so it actually drives behavior, then collapses the parallel C# data files into the single source of truth.

Changes

WaitStats.json

  • Trim 52 → 44 entries: drop server-level / instance-level waits (THREADPOOL, BACKUPIO, BACKUPBUFFER, HADR_SYNC_COMMIT, DBMIRROR_DBM_EVENT, SQLTRACE_BUFFER_FLUSH) and connection-setup preemptive waits (PREEMPTIVE_OS_AUTHENTICATIONOPS, PREEMPTIVE_OS_LOOKUPACCOUNTSID) — none of these appear in plan XML's <WaitStats> element. Per @jobbish-sql's feedback on Seed wait stats config file (#215 part 1) #296.
  • Populate attributes 6–8 from existing tool behavior:
    • showWaitCount: true everywhere (matches current ShowWaitStats always rendering "(N waits)")
    • showAverageWaitTime: true for PAGEIOLATCH_SH/EX/UP/DT (the four entries WaitStatsKnowledge.ShowEffectiveLatency=true was set on); false elsewhere
    • timeCalculationModel: "cpu time based" for isExternal=true (4 PREEMPTIVE_* + MEMORY_ALLOCATION_EXT); "direct" for RESOURCE_SEMAPHORE and ASYNC_NETWORK_IO (Joe's explicit examples); "elapsed time based" for the remaining 37 entries (his stated default)
  • Attributes 9–12 (applicable operators, descriptions, URLs, internal comment) intentionally remain null — those need domain expertise.

WaitStatsConfig (new)

  • PlanViewer.Core.Services.WaitStatsConfig loads the JSON from an embedded resource on first access, caches by name (case-insensitive Dictionary).
  • Resource lookup is suffix-based (EndsWith("Resources.WaitStats.json")) so the file works whether embedded under PlanViewer.Core.* or PlanViewer.Web.* manifest prefixes (Web project compiles linked Core sources into its own assembly).
  • Public surface: Get(name), IsExternal(name), ShowAverageWaitTime(name). Lookup misses return null / false, preserving prior defaults for unknown waits.

Code wiring

  • BenefitScorer.IsExternalWait now delegates to WaitStatsConfig.IsExternal.
  • BenefitScorer.EmitWaitStatWarnings drops the WaitStatsKnowledge.Lookup dependency and reads ShowAverageWaitTime from the config.
  • WaitStatsKnowledge.cs deleted — its only structural data (ShowEffectiveLatency) is now sourced from the config; Description and HowToFix were intentionally empty since [FEATURE] Add system to assign a "maximum benefit" for plan analysis rules #215 D3, so nothing of substance is lost.

Project files

  • PlanViewer.Core.csproj: add Resources/WaitStats.json as <EmbeddedResource>.
  • PlanViewer.Web.csproj: replace the linked WaitStatsKnowledge.cs with WaitStatsConfig.cs; add a linked <EmbeddedResource> for the JSON so the WebAssembly assembly carries its own copy.

Test plan

  • dotnet build clean for Core, App, and Web (4 pre-existing AVLN3001 warnings in App, unrelated)
  • dotnet test — 75/75 Core tests pass
  • Smoke-test in app: open a .sqlplan with PAGEIOLATCH_SH waits and verify the warning still includes "Effective latency: ... per wait"
  • Smoke-test in app: open a .sqlplan with PREEMPTIVE_* or MEMORY_ALLOCATION_EXT waits and verify the CPU-based external-wait benefit % still calculates

🤖 Generated with Claude Code

- Trim WaitStats.json from 52 to 44 entries — drop server-level / instance-
  level waits (THREADPOOL, BACKUPIO, BACKUPBUFFER, HADR_SYNC_COMMIT,
  DBMIRROR_DBM_EVENT, SQLTRACE_BUFFER_FLUSH) and connection-setup waits
  (PREEMPTIVE_OS_AUTHENTICATIONOPS, PREEMPTIVE_OS_LOOKUPACCOUNTSID) that
  don't appear in plan XML's <WaitStats> element.

- Populate attributes 6-8 (showWaitCount, showAverageWaitTime,
  timeCalculationModel) from existing tool behavior. Attributes 9-12
  (applicable operators, descriptions, URLs, internal comment) remain
  null — those need domain expertise.

- Add WaitStatsConfig loader (PlanViewer.Core.Services.WaitStatsConfig)
  that reads the JSON from an embedded resource at first access and
  caches by name (case-insensitive). Resource lookup is suffix-based so
  the file works whether embedded under PlanViewer.Core.* or
  PlanViewer.Web.* manifest prefixes.

- BenefitScorer.IsExternalWait now delegates to WaitStatsConfig.IsExternal.
  Lookup misses fall back to false, preserving prior default for unknown
  waits. EmitWaitStatWarnings drops the WaitStatsKnowledge dependency
  entirely.

- Delete WaitStatsKnowledge.cs — its ShowEffectiveLatency flag was the
  only structural data and is now sourced from
  WaitStatsConfig.ShowAverageWaitTime. Description/HowToFix were
  intentionally empty since #215 D3, so nothing of substance is lost.

- Wire embedded resource into PlanViewer.Core.csproj and link it +
  WaitStatsConfig.cs into PlanViewer.Web.csproj (which links Core
  source files rather than referencing the project).

All 75 Core tests pass; Core, App, and Web all build clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@erikdarlingdata erikdarlingdata merged commit bfb19f2 into dev May 2, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant