Skip to content

Plan viewer: add a Server Context card to the Plan Insights strip (#4597) - #4614

Merged
erikdarlingdata merged 42 commits into
devfrom
viewer/4597-server-context-card
Sep 28, 2026
Merged

erikdarlingdata merged 42 commits into
devfrom
viewer/4597-server-context-card

Conversation

@erikdarlingdata

@erikdarlingdata erikdarlingdata commented Sep 28, 2026 •

Copy link
Copy Markdown
Owner

Fixes #4597. #4607 is merged; this is now against dev. #4604 is merged. Part of #4511.

Why

erikdarlingdata/PerformanceStudio's desktop plan viewer shows a Server Context card in its Plan Insights strip: the server a plan belongs to, its edition and version, hardware, instance-level settings such as MAXDOP and cost threshold for parallelism, and the plan's database with its compatibility level. PM's viewer had no such card, even though ServerMetadata is now wired into PlanViewerControl for every Darling Viewer and Lite plan-opening site, and now carries a Database field.

What changes

  • New pure PerformanceMonitor.PlanAnalysis.ServerContextCard.Rows(ServerMetadata?) builds the card's label/value rows in PerformanceStudio's order and text: Server (name, edition with the " (64-bit)" suffix trimmed, version), Hardware (dropped when CpuCount is 0), MAXDOP, Cost threshold, Max memory, and now Database (name and compatibility level) when the reader found one. Returns an empty list when metadata is null.
  • PlanViewerControl.Properties.cs gets ShowServerContext(), ported from PerformanceStudio dev's ShowServerContext (erikdarlingdata/PerformanceStudio@85492a1, src/PlanViewer.App/Controls/PlanViewerControl.RuntimeSummary.cs:203-278). It only lays out the grid; all row logic is in the pure helper above.
  • The card is first in the Plan Insights strip (ahead of Runtime Summary), matching PerformanceStudio's placement. It goes quiet (muted header, dimmed accent edge) when there's no metadata, same as every other card in the strip's shared quiet-state system.
  • ServerMetadata's setter now re-renders the card in place when set after a statement is already showing, matching PerformanceStudio's Metadata setter.
  • Adds InsightServerBrush to all six theme files (Darling Viewer + Lite, each Dark/Light/CoolBreeze) for the card's accent edge and header. Dark theme uses PerformanceStudio's own colour (#9B9BFF); PerformanceStudio ships only a dark theme, so Light and CoolBreeze use matching purple tones picked to sit alongside the other insight-card accents in those themes.

Lite: gets this too (shared control) — PlanViewerControl is hosted by both the Darling Viewer and Lite.

Not ported

PerformanceStudio's DatabaseMetadata carries more than name and compatibility level: collation, isolation state, stats settings, and scoped configs. None of those other members show on PerformanceStudio's own card either — its ShowServerContext only ever reads Name and CompatibilityLevel off Database — so nothing is missing here relative to what a user sees on PerformanceStudio's card today.

Test plan

Merged in the current heads of #4604 and the reader branch (twice, once for the initial reader push and once for its later StorageCommandTimeoutTests-related update), then origin/dev at c650606fe (five merges total, three conflicting: PlanViewerControl.Properties.cs and PlanViewerControl.xaml.cs each had a spot where an old copy of one base branch's change collided with the other base's or dev's current version of a nearby method; DarlingServerMetadataReader.cs had the reader branch's own CommandTimeout conflict, resolved when merging the reader branch's updated head, not here. In each case the current/latest version was kept, and where both sides added distinct members both were kept side by side).

Pin: Darling/Darling.Tests/Viewer4597Tests.cs, now 13 cases: the existing full-metadata row order/text, the edition-suffix trim, null server name/edition/version, the zero-CPU-count Hardware-row drop, MAXDOP/cost-threshold staying at "0" rather than being dropped, plus two new cases for this change — Database set adds a "Database" / "AdventureWorks (compat 160)" row last, Database null adds no Database row.

  • Build: Darling.Tests, PerformanceMonitor.Ui, Lite/PerformanceMonitorLite.csproj, Lite.Tests, Darling/PerformanceMonitor.Darling.Viewer, Darling/PerformanceMonitor.Darling.Service — all 0 errors, 0 warnings on every log (grep -E "warning (CA|CS|IDE)[0-9]+" empty). Also fixed a pre-existing CA1310 on ServerContextCard's IndexOf call (add StringComparison.Ordinal) so the file builds warning-free.
  • dotnet restore PerformanceMonitor.sln --locked-mode: passes, no lock file changes needed.
  • RED (runtime mutation): removed the Database-row if block from ServerContextCard.Rows; Viewer4597Tests went to Total: 11, Errors: 0, Failed: 1 (Rows_DatabaseSet_AddsDatabaseRowLast). Reverted, rebuilt (0 warnings), back to green.
  • Viewer4597Tests, Viewer4598Tests, Viewer4573Tests, the PlanSync4530* classes, PlanSync4597ServerContextFieldsLiveTests, StorageCommandTimeoutTests, ClipboardWriteCensusTests: Total: 72, Errors: 0, Failed: 0, Skipped: 3 (the live class skips locally with no Postgres rig on this box; CI runs it). StorageCommandTimeoutTests confirms every NpgsqlCommand in DarlingServerMetadataReader.cs still carries an explicit CommandTimeout after the merge that combined this branch's database-config read with Plan analysis: rule 38 reads the server's edition and MAXDOP at every entry point (#4530) #4601's deadline on the same (single, joined) query.
  • Test-census family (CommentFilterAdoptionTests, DocCommentHygieneTests, McpPayloadContractCensusTests, McpToolsListBudgetTests, every *Census* class): Total: 359, Errors: 0, Failed: 0, Skipped: 1.
  • WPF-dependent Viewer* classes that touch PresentationFramework types (ViewerDataServiceTests, ViewerWave2Tests) can't run on macOS and are skipped; neither references any file this change touches.

Screenshot plan (Darling Viewer and Lite, any plan tab, on a plan whose entry point carries a database name the store has a database_config row for): open a stored plan, expand "Plan Insights". The Server Context card's last row should now read "Database" with the database's name and compatibility level, e.g. "AdventureWorks (compat 160)", below "Max memory". With no database on the metadata (a pasted plan XML, or no matching store row), the card shows exactly the same five rows as before this change — no blank or placeholder Database row.

Update: merged current dev after #4604 squash-merged

Branch head is now fa1a398f8, merged from origin/dev at 5aea3af896 (post-#4604). Conflicts were exactly the six theme files (each just the InsightServerBrush line vs. dev's Parameters-card additions — resolved by keeping both brushes) and PlanViewerControl.xaml/.xaml.cs (the Parameters/Wait Stats Grid.Column indices shifting to make room for Server Context at column 0, and the ServerAccentBrush property — resolved by keeping this card's column numbering, 0–4, and adding the ServerAccentBrush accessor alongside dev's Parameters one). No duplicate x:Key/x:Names; strip order is Server Context, Runtime Summary, Missing Indexes, Parameters, Wait Stats.

Re-ran after the merge:

  • Builds (Darling.Tests, Lite/PerformanceMonitorLite.csproj, Lite.Tests, Darling/PerformanceMonitor.Darling.Viewer): all 0 errors, 0 warnings.
  • dotnet restore PerformanceMonitor.sln --locked-mode: passes.
  • Viewer4597Tests, Viewer4598Tests, Viewer4573Tests, StorageCommandTimeoutTests, DocCommentHygieneTests, CommentFilterAdoptionTests: Total: 135, Errors: 0, Failed: 0, Skipped: 0.
  • Test-census family plus PlanSync4597ServerContextFieldsLiveTests: Total: 276, Errors: 0, Failed: 39 — every failure is LivePostgresStoreFixture refusing 127.0.0.1:5432 (no Postgres rig on this box, same class of skip as before); none is a real regression from the merge.

Update: merged #4601's squash-merge commit into #4602, then #4607, then this branch

Branch head is now 3883064a8, built by merging origin/plan-sync/4597-server-context-fields (itself remerged to ff9013140) into viewer/4597-server-context-card; origin/dev at 209aca7fe was already an ancestor by that point, so the direct dev merge was a no-op (Already up to date). No conflicts in this merge step itself — the base-branch merge upstream carried the resolutions.

Re-ran after the merge:

  • Builds (Darling.Tests, Lite.Tests, Lite/PerformanceMonitorLite.csproj, Darling/PerformanceMonitor.Darling.Viewer, Darling/PerformanceMonitor.Darling.Service): all 0 errors, 0 warnings.
  • dotnet restore PerformanceMonitor.sln --locked-mode: passes.
  • Viewer4597Tests, Viewer4598Tests, Viewer4573Tests, PlanSync4597ServerContextFieldsLiveTests, PlanSync4530*, StorageCommandTimeoutTests, plus the census family: Total: 341, Errors: 0, Failed: 41, Skipped: 0. All 41 failures are LivePostgresStoreFixture refusing 127.0.0.1:5432 (no Postgres rig on this box); none is a regression from the merge.

CHANGELOG

SECTION: Added
ENTRY: - Plan viewer: a Server Context card ([#4614]) - The Plan Insights strip now opens with a Server Context card, as in PerformanceStudio's desktop viewer. It shows the server a plan belongs to: its name, edition and version, CPUs and memory, MAXDOP, cost threshold for parallelism, max server memory, and the plan's database with its compatibility level, from what the collector has stored for that server.
REF: [#4614]: #4614

erikdarlingdata and others added 30 commits September 28, 2026 09:15
Ports PerformanceStudio's AnalyzerConfig/RulesConfig (disabled rules,
severity overrides) and the top-level ServerMetadata fields into
PerformanceMonitor.PlanAnalysis, adds PlanWarning.RuleNumber, and adds
Analyze/Run overloads that take (config, serverMetadata, cancellationToken)
and thread both down to the statement and node walks. The existing
overloads still forward to AnalyzerConfig.Default and a null metadata, and
no rule reads either new parameter yet, so analyzer output is unchanged.
…ing (#4535)

Ports erikdarlingdata/PerformanceStudio's severity-override pass: overrides now find a warning's rule via PlanWarning.RuleNumber instead of matching WarningType against a rule-to-name table, and the engine's own warnings (Source == SqlServer) are always skipped. Also ports PlanStatements.EnumerateAllWithContainer, with EnumerateAll delegating to it so the plain and container-carrying walks stay one walk.
#4535, #4566)

The plumbing step's RuleNumber_DefaultsToNull_UntilLaterStepsStampIt pinned the
state partway through the per-rule configuration rollout (only rule 3 stamped).
Every step that stamps a rule has landed on this branch, so that pin is wrong
by design now.

Replaces it with PlanSync4535RuleNumberCensusTests: a code census over every
new PlanWarning construction in PlanAnalyzer.cs (each must set RuleNumber,
except a SqlServer-sourced initializer), a runtime census over the existing
plan fixtures (every analyzer finding carries a non-null RuleNumber), and a
cross-step check that disabling every stamped rule number leaves zero analyzer
findings.
Ports PerformanceStudio dev's insights-strip card system
(erikdarlingdata/PerformanceStudio@87bad14) to the shared plan viewer used by
both the Darling Viewer and Lite. All three cards (Runtime Summary, Missing
Indexes, Wait Stats) now sit on the same neutral BackgroundLightBrush surface
with a 3px per-card accent edge (InsightIndexBrush, InsightWaitsBrush) instead
of a full-bleed tinted background. Runtime Summary keeps a blank edge as the
neutral centre.

A card with nothing to report now goes quiet: its header drops to the muted
foreground and its accent edge dims to 0.35, via a shared SetInsightQuiet
helper whose quiet/non-quiet values live in the new pure
PerformanceMonitor.PlanAnalysis.InsightCardStyle so they are testable outside
WPF.

No inline hex is left in the insight-card markup or code.
Adds three tests the earlier #4530 steps skipped:

- a live Postgres pin that seeds collect.server_properties and
  collect.server_config on a migrated store and asserts
  DarlingServerMetadataReader.ReadAsync returns the edition and MAXDOP,
  then runs the same plan XML through the analysis formatter and
  confirms rule 38 (Standard Edition DOP 2 limitation) gives its
  Warning, plus a no-rows case that stays on the Info branch;
- a drill-down pin: a PLAN_WARNING finding's drill-down surfaces the
  rule-38 Warning when PgDrillDownCollector runs against a seeded
  Standard Edition / MAXDOP 8 server;
- a Lite.Tests pin (build-only on macOS) for
  LocalDataService.GetServerMetadataForPlanAnalysisAsync against a
  seeded DuckDB fixture, plus a Darling.Tests source-census pin that
  the Lite reader's SQL names the same view and columns Darling's
  migrated schema and reader use.

Ported the reader's column-name RED by temporarily renaming the
FROM clause, confirming the new live pin fails, then reverting.
Ports PerformanceStudio dev's Parameters insight card
(erikdarlingdata/PerformanceStudio@85492a1) to the shared plan viewer used
by both the Darling Viewer and Lite. The card lists each parameter's name,
data type, compiled value and runtime value, tints a runtime value amber
when it differs from its compiled value (possible parameter sniffing),
and annotates the card when every compiled value is missing (OPTIMIZE FOR
UNKNOWN vs. OPTION(RECOMPILE), read from the masked statement text so a
hint spelled inside a comment or string literal doesn't count) or when
the statement text references a local variable with no matching
parameter.

The row/column/annotation logic lives in a new pure
PerformanceMonitor.PlanAnalysis.ParameterCard, pinned by
Darling.Tests/Viewer4598Tests.cs; the WPF code-behind (ShowParameters in
PlanViewerControl.Properties.cs) only builds and lays out the grid.

Adds InsightParamsBrush to all three themes for the card's accent edge
and header, matching PerformanceStudio's green tone.
)

Ports PerformanceStudio dev's Server Context insight card
(erikdarlingdata/PerformanceStudio@85492a1) to the shared plan viewer used
by both the Darling Viewer and Lite. The card shows the server a plan
belongs to: name, edition (with the " (64-bit)" suffix trimmed), version,
hardware, MAXDOP, cost threshold for parallelism, and max server memory,
in PerformanceStudio's order and text. It sits first in the strip, ahead of
Runtime Summary, and goes quiet when no server metadata has been captured.

The row list lives in a new pure PerformanceMonitor.PlanAnalysis.ServerContextCard,
pinned by Darling.Tests/Viewer4597Tests.cs; the WPF code-behind
(ShowServerContext in PlanViewerControl.Properties.cs) only builds and
lays out the grid, and re-runs it when ServerMetadata is set after a
statement is already showing.

Adds InsightServerBrush to all six theme files (Darling Viewer + Lite,
each in Dark/Light/CoolBreeze) for the card's accent edge and header,
matching PerformanceStudio's colour in Dark and picking matching
purple tones for Light/CoolBreeze (PerformanceStudio ships only a dark
theme).

Not ported: PerformanceStudio's Database row (compat level, collation,
isolation and scoped-config facts). PM's ServerMetadata model has no
Database field yet -- it is out of scope for this change and left to a
future port once that field exists.
…and seed every required server_properties column in the Lite metadata test
…oints

# Conflicts:
#	PerformanceMonitor.Ui/PlanViewerControl.xaml.cs
…-card

# Conflicts:
#	Darling/PerformanceMonitor.Darling.Viewer/Themes/CoolBreezeTheme.xaml
#	Darling/PerformanceMonitor.Darling.Viewer/Themes/DarkTheme.xaml
#	Darling/PerformanceMonitor.Darling.Viewer/Themes/LightTheme.xaml
#	Lite/Themes/CoolBreezeTheme.xaml
#	Lite/Themes/DarkTheme.xaml
#	Lite/Themes/LightTheme.xaml
#	PerformanceMonitor.Ui/PlanViewerControl.Properties.cs
#	PerformanceMonitor.Ui/PlanViewerControl.Rendering.cs
#	PerformanceMonitor.Ui/PlanViewerControl.xaml
#	PerformanceMonitor.Ui/PlanViewerControl.xaml.cs
erikdarlingdata and others added 12 commits September 28, 2026 12:25
…o viewer/4597-server-context-card

# Conflicts:
#	PerformanceMonitor.Ui/PlanViewerControl.Properties.cs
#	PerformanceMonitor.Ui/PlanViewerControl.xaml.cs
…elds' into viewer/4597-server-context-card

# Conflicts:
#	PerformanceMonitor.Ui/PlanViewerControl.xaml.cs
…o plan-sync/4597-server-context-fields

# Conflicts:
#	Darling/PerformanceMonitor.Darling.Storage/DarlingServerMetadataReader.cs
…elds' into viewer/4597-server-context-card
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