Skip to content

Release v1.3.0#156

Merged
erikdarlingdata merged 48 commits intomainfrom
dev
Mar 29, 2026
Merged

Release v1.3.0#156
erikdarlingdata merged 48 commits intomainfrom
dev

Conversation

@erikdarlingdata
Copy link
Copy Markdown
Owner

Merges dev into main to trigger the v1.3.0 release workflow.

What's in this release

  • Query Store wait stats: global bar/ribbon/table charts, per-row wait profile with volume/% toggle, FormatWaitRatio helper, resizable separator, color legend popup
  • Wait stats ribbon chart is now the default mode
  • Day boundary lines and average line in ribbon chart
  • Time slicer overhaul: quick filter buttons (1h/4h/12h/24h/48h/7d), calendar date picker, keyboard arrow navigation, day band shading, tooltips
  • Progress bar while fetching Query Store plan grid data
  • Query Store grid query rewritten using sp_QuickieStore patterns (major perf improvement)
  • Query Store wait stats gated to SQL 2017+ with capture mode check
  • SkiaSharp native library fix on Linux ([BUG] Application Does not open on Linux #139)
  • Search filter not applied after slicer reset ([BUG] Query Store Search does not work #147)
  • Division by zero fix in wait_ratio calculation

🤖 Generated with Claude Code

erikdarlingdata and others added 30 commits March 19, 2026 21:21
- Increase metric label font size from 9 to 12 for readability
- Use ForegroundBrush instead of muted SlicerLabelBrush for metric label
- Fix metric label showing "Total" for avg metrics (e.g., avg-cpu showed
  "Total CPU" instead of "Avg CPU") by splitting combined or-patterns
- Add per-bucket average properties to QueryStoreTimeSlice so the slicer
  chart shape reflects the selected metric (total vs avg) instead of
  always showing totals

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix Query Store time slicer metric display
…ait profile with v/% toggle, resizable separator with chevron, ribbon click/double-click interactions
- display simple stacked wait profile per default instead on %stacked
Most Debug.WriteLine statements for wait stats have been removed, leaving only exception logging. The WaitProfile column visibility toggle now uses SortMemberPath instead of a hardcoded index, making the code more robust to column order changes.
Wrapped denominator in NULLIF to avoid division by zero errors
when calculating wait_ratio in QueryStoreService.cs.

Also made
minor formatting and comment improvements for clarity.
- Replace tab indentation with spaces in FetchPlanWaitStatsAsync doc
  comments to match file convention
- Rewrite doc comment for clarity (explain why plan-level WTR differs)
- Use existing _fetchCts instead of creating untracked CancellationTokenSource
  in OnWaitStatsCollapsedChanged

Follow-up to PR #137.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- tooltips for data and time in RangeTimeSlicer
- vertical bands to display days (even with gaps)
- allow to move RangeTimeSlicer period using keyboards arrows left and right
Avalonia.Skia 11.3.12 transitively pulls SkiaSharp.NativeAssets.Linux
2.88.9 (libSkiaSharp.so v88.1), but the managed SkiaSharp 3.119.0
requires native libs in the [119.0, 120.0) range. At publish time the
old .so overwrites the correct one, causing a TypeInitializationException
on startup.

Pin SkiaSharp.NativeAssets.Linux to 3.119.0 so NuGet resolution picks
the correct native library. macOS and Win32 were already at 3.119.0.

Fixes #139

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix wait stats cleanup: tab indentation and CTS leak
Fix SkiaSharp native library mismatch on Linux (#139)
…improvments

Feature/query store timerange improvments
sys.query_store_wait_stats only exists on SQL Server 2017+ (major >= 14)
and Azure SQL DB. Additionally, wait stats capture can be disabled per
database via WAIT_STATS_CAPTURE_MODE = OFF.

Changes:
- Add SupportsQueryStoreWaitStats property to ServerMetadata (major >= 14 or Azure)
- Add IsWaitStatsCaptureEnabledAsync to QueryStoreService — checks
  wait_stats_capture_mode_desc in sys.database_query_store_options
- Gate QueryStoreGridControl: accept supportsWaitStats flag, hide wait
  stats panel/column/splitter when unsupported, skip all fetch calls
- Wire both checks in QuerySessionControl before constructing the grid

On SQL 2016 or when capture is OFF: no wait stats queries are issued,
the panel is collapsed, and the Wait Profile column is hidden.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Gate Query Store wait stats for SQL 2017+ with capture mode check
Three changes from community feedback (41s → 3s on slow machines):

1. Remove WHERE p.query_plan IS NOT NULL from the ranked CTE — this
   predicate on nvarchar(max) causes an expensive implicit conversion
   scan. NULL plans are already handled by the C# reader (skip row).

2. Two-phase approach: materialize TOP N into #top_plans first using
   only numeric columns, then join to query_text/plan XML tables for
   just the winners. Avoids dragging expensive nvarchar(max) columns
   through the entire CTE evaluation.

3. Remove OPTION (LOOP JOIN) — reported 3x faster without it on
   multiple test environments.

Tested on SQL2022 with PerformanceMonitor Query Store data.

Closes #143

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Improve Query Store grid query performance (#143)
Multi-phase materialization approach for drastically better performance
on large Query Store datasets:

Phase 1: Pre-filter matching interval IDs into #intervals (clustered PK).
  All subsequent phases use EXISTS semi-join against this tiny table
  instead of re-evaluating the time predicate.

Phase 2: Aggregate runtime_stats by plan_id into #plan_stats (clustered
  PK on plan_id). Uses EXISTS against #intervals — semi-join avoids
  materializing the full interval join.

Phase 3: Rank best plan per query_id, materialize TOP N into #top_plans.
  Still no nvarchar(max) columns touched.

Phase 4: Hydrate only the TOP N winners with text, plan XML, and metadata.
  Uses TRY_CONVERT for safe plan XML handling.

Additional improvements:
- Both time filter paths now use rsi.start_time (indexed) instead of
  rs.last_execution_time (requires scanning all runtime_stats rows)
- OPTION(RECOMPILE) on aggregation phases prevents parameter sniffing
  on date range parameters producing stale plans
- Clustered PKs on temp tables enable index seeks in subsequent joins

Tested on SQL2022: ~22ms total for all 4 phases (24 intervals, 546 plans).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rewrite Query Store grid query using sp_QuickieStore patterns (#143)
… plan grid and try improve performance of querystore grid fetch
FetchPlanWaitStatsAsync now accepts an optional planIds parameter. When provided, the method creates a temp table and filters results to only those plan IDs for efficient querying. SQL construction and connection logic were refactored to support this feature.
…fixes-202603

Fix/querystore improvment and fixes 202603
erikdarlingdata and others added 18 commits March 26, 2026 18:10
- Fix fire-and-forget: globalWaitTask variable replaced with _ = discard
- Remove Debug.WriteLines from wait stats catch blocks, add separate
  OperationCanceledException handler for clean cancellation
- Remove unused System.Diagnostics using
- Restore ps. prefix on orderClause (used in ranked CTE where ps is
  the correct alias for #plan_stats)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two regressions introduced in the time slicer PR (#114):

1. Search filter (module, query_id, etc.) was built via BuildSearchFilter()
   but never passed to FetchTopPlansAsync — the filter parameter was
   dropped when the call site switched to named parameters for startUtc/
   endUtc. Add filter: back to the call.

2. Clicking Fetch reloads the slicer via LoadTimeSlicerDataAsync, which
   calls LoadData without preserving the current selection. LoadData
   defaults to last 24h when no selection is provided, discarding the
   user's custom range. Pass the current _slicerStartUtc/_slicerEndUtc
   through to LoadData's selectionStart/selectionEnd parameters.

Fixes #147

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…r-reset

Fix search filter not applied and slicer reset on Fetch (#147)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…-v1.1

Feature/query store timeslicer v1.1
- Modify WaitStatsProfileControl to default to ribbon chart mode
- Remove the grid data button from WaitStatsProfileControl header
- Add legend button to WaitStatsProfileControl that opens a color legend popup
- Add vertical dashed lines for day boundaries in WaitStatsRibbonControl
- Align X-axis ticks/labels to day boundaries (00:00) in WaitStatsRibbonControl
- Add horizontal dashed average line with label in WaitStatsRibbonControl
- Apply FormatWaitRatio in WaitStatsRibbonControl tooltips
- Apply FormatWaitRatio in WaitProfileBarControl tooltips
- Apply FormatWaitRatio in WaitStatsProfileControl grid table rows
…er light grey background

- switch wait color legend to the right of the global wait header
…-v1.1

Feature/query store wait stats v1.1
…pping

- Remove duplicate `using System;` in WaitStatsProfileControl
- Clamp avg line label position to prevent clipping at top of chart
- Skip day boundary labels when too dense to prevent overlap on wide ranges

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix wait stats: duplicate using, label overlap, avg clipping
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
@erikdarlingdata erikdarlingdata merged commit e887984 into main Mar 29, 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