Skip to content

Merge dev to main#352

Merged
erikdarlingdata merged 33 commits intomainfrom
dev
Feb 27, 2026
Merged

Merge dev to main#352
erikdarlingdata merged 33 commits intomainfrom
dev

Conversation

@erikdarlingdata
Copy link
Owner

ClaudioESSilva and others added 30 commits February 26, 2026 15:05
…s-issue-319

Fixes Missing tooltip on charts (high DPI / multi-monitor DPI issue)
…-issue-321

Some graphs don't get dark mode until they get data
Follow-up to PR #322 — SystemEventsContent had the same issue where
charts flash white until data loads. Adds upfront ApplyDarkModeToChart
calls for all 19 charts in the constructor.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…heme

Apply dark theme to SystemEventsContent charts in constructor
Existing SQL Agent jobs (owner, schedule, notifications) are now
preserved during upgrades when --preserve-jobs is specified (CLI)
or the checkbox is checked (GUI). Missing jobs are still created.

Uses a @preserve_jobs variable in the T-SQL — the C# installer
flips it from 0 to 1 via string replace when the flag is set.

Fixes #324

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add --preserve-jobs flag to CLI and GUI installers
* Add 13 new plan analysis rules to PlanAnalyzer

Expands the plan analyzer from 11 to 24 rules with domain-specific
detection patterns for common SQL Server performance anti-patterns.

New rules:
- Non-SARGable predicates (CONVERT_IMPLICIT, function calls, ISNULL,
  leading wildcard LIKE, CASE expressions)
- Data type mismatch (GetRangeWithMismatchedTypes)
- Lazy spool ineffective rebind/rewind ratio
- Join OR clause expansion (Concatenation + Constant Scan)
- Nested Loops high inner-side execution count
- Many-to-many Merge Join
- Large memory grant with sort/hash consumer identification
- Compile memory exceeded (early abort)
- High compile CPU (>= 1000ms)
- Local variables without RECOMPILE
- CTE referenced multiple times
- Table variable detection
- Table-valued function detection
- Top above rowstore scan

Also fixes existing scan rules to exclude columnstore indexes
(designed to be scanned) and extracts IsRowstoreScan() helper.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Sync PlanAnalyzer from plan-b: statement-level UDF timing + CASE detection reorder

- Add statement-level Rule 4 check for UDF execution timing from QueryTimeStats
  (some plans report UDF timing only at statement level, not per-node)
- Reorder non-SARGable detection: check CASE expression before CONVERT_IMPLICIT
  since CASE bodies often contain CONVERT_IMPLICIT that isn't the root cause

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Replace the ComboBox statement selector with a sortable DataGrid panel
on the left side. Columns: #, Query, CPU, Elapsed, UDF (conditional),
Est. Cost (for estimated plans), Critical, Warnings. Uses SortMemberPath
for proper numeric sorting on formatted display columns.

Add click-and-drag canvas panning via Preview mouse events with hit
testing to distinguish node clicks from empty canvas drags.

Ported from plan-b Avalonia implementation, adapted for WPF.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…-pan

Add statement DataGrid and canvas panning to plan viewer
Scheduled at 6:00 AM UTC (1:00 AM EST), builds from dev branch.
Skips if no new commits in 24 hours. Creates a rolling "nightly"
pre-release with Dashboard, Lite, and Installer zips.

Also supports manual trigger via workflow_dispatch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Rule 5: Handle zero actual rows without division by zero (was producing Unicode infinity)
- Rule 8: Add minimum row threshold for parallel skew (threadCount * 1000) to avoid false positives on low-row operators
- Rule 24: Only fire Top Above Scan on inner side of Nested Loops, not standalone SELECT TOP
- Lite: Full sync from Rules 1-11 to Rules 1-24 (was missing 12-24 entirely)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Plan XML UdfCpuTime/UdfElapsedTime values are milliseconds, not
microseconds. The property names, /1000 division in PlanAnalyzer,
and "us" labels in the properties panel were all wrong — showing
1/1000th of real time and making the critical severity threshold
effectively unreachable.

- Rename UdfCpuTimeUs/UdfElapsedTimeUs → UdfCpuTimeMs/UdfElapsedTimeMs
- Remove /1000.0 division in Rule 4, use values directly
- Fix properties panel labels from "us" to "ms"
- Both Dashboard and Lite

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix UDF timing units: microseconds to milliseconds
…335) (#340)

All 8 collectors with first-run backfill logic now look back 1 hour
instead of 3-7 days (or unlimited). The aggressive lookback was a
development convenience that becomes catastrophic on large environments
— particularly query_store_collector with XML plan decompression on
multi-GB Query Store databases, causing timeout→rollback→retry loops.

Changed collectors:
- 08_collect_query_stats: all DMV data → 1 hour
- 09_collect_query_store: 3 days → 1 hour
- 10_collect_procedure_stats: all DMV data → 1 hour
- 18_collect_cpu_utilization_stats: 7 days → 1 hour
- 22_collect_blocked_processes: 3 days → 1 hour
- 24_collect_deadlock_xml: 3 days → 1 hour
- 28_collect_system_health_wrapper: 3 days → 1 hour
- 29_collect_default_trace: all available → 1 hour

Clean install verified on sql2016 with zero errors.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
… analysis

Spill severity is now based on what percentage of statement elapsed time
the spilling operator accounts for (Warning >= 10%, Critical >= 50%).

Uses QueryTimeStats.ElapsedTimeMs for statement time and calculates
operator-own time by subtracting child elapsed (row mode) or using
elapsed directly (batch mode). Skips Parallelism exchange operators
when subtracting child time due to known timing bugs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…lysis

Rule 7: Spill time-based analysis replaces write-count threshold
DOP 2 with all work on one thread is still skew worth flagging.
Threshold is now 75% at DOP 2, 50% at DOP 3+. Removed the >= 4
thread requirement that prevented detection at low DOP.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rule 8: Lower parallel skew thresholds, remove 4-thread minimum
No memory grant warnings should fire below 1GB on modern hardware.
Excessive grant (9a) and large grant (9c) both now require >= 1GB.
Critical severity raised from 512MB to 4GB.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…hresholds

Rule 9: Raise memory grant warning floor to 1GB
…umns

Rule 14: Fix lazy spool threshold — warn when cache hits < 5x misses

Rule 13 now detects both GetRangeWithMismatchedTypes (type mismatch)
and GetRangeThroughConvert (explicit CONVERT/CAST on column) with
distinct messages for each.

Rule 14 replaces the confusing rebinds*2 >= rewinds condition with
a clearer model: warn when rewinds < rebinds*5 (cache not earning
its overhead). Critical when rewinds < rebinds (net negative).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ents

Rules 13+14: GetRangeThroughConvert detection, lazy spool threshold fix
…#325) (#345)

Collectors that iterate databases and USE/query into them now exclude
AG secondary replicas configured with READ_ONLY connections, which
reject non-read-intent sessions with "not accessible for queries" errors.

Filter uses sys.availability_replicas.secondary_role_allow_connections_desc
to identify secondaries that won't accept our connection type. Databases
on primaries, non-AG databases, and secondaries with ALL connections
continue to be collected normally.

Changed locations:
- install/39_collect_database_configuration.sql (scoped config cursor)
- install/09_collect_query_store.sql (QS discovery cursor)
- Lite/RemoteCollectorService.ServerConfig.cs (scoped config db list)
- Lite/RemoteCollectorService.QueryStore.cs (QS discovery cursor)

Clean install verified on sql2016 with zero errors.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…on detection

Rule 13: Detect GetRangeThroughConvert (CONVERT/CAST on column) in
addition to GetRangeWithMismatchedTypes, with distinct messages.

Rule 14: Warn when cache hits (rewinds) < 5x cache misses (rebinds).
Critical when hits < misses (net negative). Replaces confusing
rebinds*2 >= rewinds condition.

Rule 15: Use Merge Interval ancestor instead of join ancestor to
detect OR expansion. Merge Interval + TopN Sort is the definitive
fingerprint for this pattern.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rule 15: Use Merge Interval for OR expansion detection
Merge Interval + TopN Sort is the strongest signal for OR expansion,
but the join ancestor check is a broader catch that shouldn't have
been removed. Now fires on either condition.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
erikdarlingdata and others added 3 commits February 27, 2026 10:42
…-ancestor

Rule 15: Restore join ancestor check alongside Merge Interval
…337)

Adds collect_query and collect_plan bit columns to config.collection_schedule
(NOT NULL, DEFAULT 1) so users can disable text/plan storage per collector to
reduce data volume. Collectors 08, 09, 10 read the flags and return NULL when
disabled. Includes ALTER TABLE for existing installations. Clean install tested
on sql2016, verified flags work correctly (enabled: data collected, disabled:
NULLs stored).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…llection

Add optional query text and plan collection flags (#337)
@erikdarlingdata erikdarlingdata merged commit 4202ad7 into main Feb 27, 2026
7 checks passed
erikdarlingdata added a commit that referenced this pull request Feb 27, 2026
This reverts commit 4202ad7, reversing
changes made to 7c9d981.
erikdarlingdata added a commit that referenced this pull request Feb 27, 2026
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.

2 participants