Skip to content

Lite reads wait history stored with a trailing space as the clean wait name - #4939

Merged
erikdarlingdata merged 1 commit into
devfrom
fix/wait-name-trailing-space-history
Oct 2, 2026
Merged

erikdarlingdata merged 1 commit into
devfrom
fix/wait-name-trailing-space-history

Conversation

@erikdarlingdata

Copy link
Copy Markdown
Owner

What changed

Since #4884, the collectors trim wait names before they store them. Rows stored before that keep the DMV's trailing space, for example SQP_STATS_REPORTING , until retention removes them. Lite compared the stored name exactly, so:

  • The ignore list never hid the spaced spelling. The default list names SQP_STATS_REPORTING, yet SQP_STATS_REPORTING still showed as one of the biggest waits in history from before the upgrade.
  • A wait stored both ways came back as two rows.
  • A lookup by the clean name missed the spaced history.

Every Lite read that filters, groups or looks up by wait name now uses rtrim(wait_type):

  • IgnoredWaitTypes.BuildExclusionClause emits AND rtrim(wait_type) NOT IN (...), so the ignore list hides both spellings.
  • Reads that group by wait name key on rtrim(wait_type) and return the clean name, so the two spellings sum into one row.
  • Lookups by name compare rtrim(wait_type) = rtrim($n).

The fix is on the read side only. There is no schema change, and no stored row is rewritten.

Reads checked: 32

Changed (13)

Read Used by Change
GetWaitStatsAsync Wait Stats grid, get_wait_stats exclusion, group key
GetDistinctWaitTypesAsync, GetDistinctWaitTypesForPickerAsync wait picker, get_wait_types, the hint in get_wait_trend exclusion, group key
GetTotalWaitTrendAsync overview total wait line exclusion
GetWaitingTasksAsync Current Waits, get_waiting_tasks exclusion, returned name
GetWaitingTaskTrendAsync Current Waits chart, waiting-task trend tool exclusion, group key
GetWaitStatsTrendAsync single wait trend lookup
WaitTrendsSql (GetWaitStatsTrendsByTypesAsync) wait charts lookup, group key, LAG partition
GetWaitBucketsAsync get_wait_trend lookup
GetQuerySnapshotsByWaitTypeAsync wait drill-down lookup
DailySummaryRangeSql daily summary top wait group key
GetWaitCategorySummaryAsync FinOps wait categories group key
DuckDbFactCollector wait facts analysis group key
AnomalyDetector top contributors ANOMALY_WAIT_PROFILE metadata group key

No change needed (19)

  • PoisonWaitAccumulationSql (poison-wait alerts). It names THREADPOOL, RESOURCE_SEMAPHORE and RESOURCE_SEMAPHORE_QUERY_COMPILE only. None of the three ever had the trailing space. The four names that did are EXTERNAL_GOVERNANCE_ATTR_SYNC_BACKGROUND, EDC_DOPP_LOCK, EDC_DOPP_BACKGROUND and SQP_STATS_REPORTING (see WaitTypeName).
  • AnomalyDetector young-baseline bar filter, and the C# check beside it. Both name REMOTE_BLOCK_IO only.
  • Lock-wait reads (4): GetLockWaitTrendAsync, GetLockWaitFamilyTrendAsync, GetLockWaitTypesAsync (get_lock_wait_trend) and the analysis drill-down's lock waits. They read LCK% names only.
  • HasAnyWaitStatAsync and HasAnyWaitingTaskSampleAsync (2). They only check that a row exists.
  • GetSignificantWaitsWithCaptureAsync (system_health significant waits). It parses the name from the stored XML on every read, and SystemHealthParser already trims it there (Wait names lose their trailing space, and two Hyperscale timer waits are ignored #4884).
  • GetCurrentBlockingWaitAsync (blocking alert). It reads blocking snapshots and no wait name.
  • Row-by-row reads that return wait_type but never filter or group on it (5). A trailing space changes nothing there. They are GetLatestQuerySnapshotsAsync, the blocking-chain query-snapshot read, the analysis query drill-down, PileupSnapshotReader and the session tool.
  • The active-query filters on WAITFOR, BACKUPTHREAD, BACKUPIO, XE_LIVE_TARGET_TVF and SP_SERVER_DIAGNOSTICS. That is T-SQL run on SQL Server, where a trailing space compares equal.
  • DeltaCalculator.WaitStatsSeedSql. This is the collector's baseline seed. Wait names lose their trailing space, and two Hyperscale timer waits are ignored #4884 pins that the first sample under the trimmed name is a baseline.
  • Mute rules (DuckDbMuteRuleStore, get_mute_rules). These store rules only. Matching uses the finding's wait name, which now comes from the facts under the clean name.
  • Config and schema references (App, MainWindow, ConfigSeeder, the collector's ignore-list load, Schema, DuckDbSchemaGenerator, the project file). None of them reads wait rows.

Notes

  • The daily summary, the FinOps wait categories and the analysis reads apply no ignore list, as before. A day from before the upgrade can still name SQP_STATS_REPORTING as its top wait, now once and under its clean name. This change does not alter what those reads count.
  • There is still no index on wait_stats, waiting_tasks or query_snapshots, and the reads go through the v_ archive views. So rtrim costs no index plan.

Tests

WaitNameSpacedHistoryReadTests adds 15 tests against a real DuckDB through the real reads, with the bundled default ignore list. 14 of them failed against the reads before the fix, each on its assertion. The 15th checks that the default list names SQP_STATS_REPORTING, and passes either way.

Failures before the fix:

  • WaitStats_TheDefaultIgnoreList_HidesTheSpacedSpelling: Assert.DoesNotContain() Failure: Filter matched in collection
  • Picker_TheDefaultIgnoreList_HidesTheSpacedSpelling: Assert.DoesNotContain() Failure: Filter matched in collection, collection ["SQP_STATS_REPORTING ", "CXPACKET"]
  • TotalWaitTrend_TheDefaultIgnoreList_LeavesTheSpacedSpellingOut: Assert.Equal() Failure: Values are not within 6 decimal places, expected 10
  • WaitStats_ANameStoredBothWays_IsOneRowWithTheSummedValues: Assert.Single() Failure: The collection contained 2 matching items
  • Picker_ANameStoredBothWays_IsOneCleanName: Assert.Equal() Failure: Collections differ at index 0
  • WaitTrend_ByTheCleanName_IncludesTheSpacedHistory: Assert.Equal() Failure: Collections differ
  • WaitTrends_ByTheCleanName_AreOneSeriesThatIncludesTheSpacedHistory: Assert.Equal() Failure: Collections differ
  • WaitingTasks_TheDefaultIgnoreList_HidesTheSpacedSpelling: Assert.DoesNotContain() Failure: Filter matched in collection
  • WaitingTaskTrend_ANameStoredBothWays_IsOneSeriesUnderTheCleanName: Assert.Equal() Failure: Collections differ at index 1
  • QueryDrillDown_ByTheCleanName_FindsASnapshotStoredWithTheSpace: Assert.Single() Failure: The collection was empty
  • WaitBuckets_ByTheCleanName_IncludeTheSpacedHistory: Assert.Equal() Failure: Values differ, expected 2
  • DailySummary_TopWait_SumsBothSpellingsUnderTheCleanName: Assert.Equal() Failure: Strings differ, expected EDC_DOPP_LOCK
  • WaitCategorySummary_TopWait_SumsBothSpellingsUnderTheCleanName: Assert.Equal() Failure: Strings differ, expected EDC_DOPP_LOCK
  • AnalysisWaitFacts_ANameStoredBothWays_IsOneFactUnderTheCleanName: Assert.Single() Failure: The collection contained 2 matching items

All 15 pass with the fix. The two clause-text assertions in IgnoredWaitTypesTests now expect the rtrim form. The AnomalyDetector contributor change has no test of its own. It runs only when an ANOMALY_WAIT_PROFILE anomaly fires, and it uses the same group key as the wait-facts test.

Full suites at faddc40:

  • Lite.Tests: Total 7157, Failed 0, Skipped 0.
  • Darling.Tests: Total 19836, Failed 0, Skipped 1299 (the live PostgreSQL tests, not configured here), Not Run 1.
  • Builds of Lite, Lite.Tests and Darling.Tests: 0 warnings.

CHANGELOG

No new entry: this amends the [3.9.0] #4884 line, and the release owner applies it.

Proposed sentence:

Lite now reads older wait rows with the trailing space under the clean name, so the ignore list hides them and each wait shows once.

…t name

Wait rows stored before the collectors trimmed wait names keep the DMV's
trailing space, for example "SQP_STATS_REPORTING ", until retention removes
them. Lite compared the stored name exactly, so the ignore list never hid the
spaced spelling, and a wait stored both ways came back as two rows.

Every Lite read that filters, groups or looks up by wait name now compares
rtrim(wait_type): the ignore list hides both spellings, the two spellings sum
into one row under the clean name, and a lookup by the clean name finds the
spaced history. This covers the Wait Stats grid, picker and trends, the
overview total wait line, Current Waits, the wait drill-down, the MCP wait
tools, the daily summary's top wait, the FinOps wait categories, and the
analysis wait facts and anomaly contributors.
@erikdarlingdata
erikdarlingdata marked this pull request as ready for review October 2, 2026 07:58
@erikdarlingdata
erikdarlingdata merged commit fe1e6b7 into dev Oct 2, 2026
15 of 16 checks passed
@erikdarlingdata
erikdarlingdata deleted the fix/wait-name-trailing-space-history branch October 2, 2026 07:58
erikdarlingdata added a commit that referenced this pull request Oct 2, 2026
The 3.9.0 entry for the wait-name fix (#4884) now covers #4939 and #4941, which completed it after the release was cut: its references gain both PRs in the index and the archive, rows stored with the trailing space before the upgrade are described as reading as the clean name in both apps, and the entry says Lite's wait views hide the two newly ignored waits in history from before the upgrade while Darling shows that history until retention removes it. The archive census carries the new 3.9.0 prose hash; the entry count is unchanged.
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