Wait names lose their trailing space, and two Hyperscale timer waits are ignored - #4884
Merged
erikdarlingdata merged 5 commits intoOct 1, 2026
Merged
Conversation
…are ignored Four SQL Server wait names come back from the server with a trailing space. Every exact compare in the product (the C# ignore set, the DuckDB and PostgreSQL predicates) missed them, so an ignore entry could never match one and a lookup by the clean name found nothing. - The wait readers trim the name where it leaves the result set: wait_stats, waiting_tasks, query snapshots, the stall probe and the system_health significant waits. One helper does it: PerformanceMonitor.Common.WaitTypeName. - The trimmed name is a new delta key. The first sample under it is a baseline, never the running total. Rows stored with the space age out with retention. - RBIO_COMM_RETRY and SQP_STATS_REPORTING join the default ignore list in both apps, with the measurement that shows they are timers. REMOTE_BLOCK_IO stays visible.
The Collectors and Common assemblies must carry no reference to each other in their compiled form, and PerfmonCounterTypeTests pins that. The trim helper lived in Common, so the collectors calling it broke the pin. The helper moves to Collectors. The system_health parser in Common repeats the one-line TrimEnd with a comment that says where the rule is written down, the same way the perfmon gauge set is spelled on both sides.
The comment beside the new ignore entries now states that the documentation page lists REMOTE_BLOCK_IO as "Internal use only", and no more than that.
erikdarlingdata
marked this pull request as ready for review
October 1, 2026 00:24
The Live Snapshot button runs the query snapshots collector's query but reads the rows itself. Its grid kept the trailing space on a wait name, while the stored row and Darling's live fetch trim it. - The row read moves into ServerTab.ReadLiveSnapshotRow, which trims the name with WaitTypeName.Trim. - WaitNameTrimTests drives that method with the spaced name the server returns.
erikdarlingdata
deleted the
fix/wait-names-trimmed-and-hyperscale-background-waits
branch
October 1, 2026 01:09
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Four SQL Server wait names come back from the server with a trailing space. The collectors now trim the name where they read it. RBIO_COMM_RETRY and SQP_STATS_REPORTING, two Azure SQL Database Hyperscale timer waits, join the default ignore list. Lite and Darling share these collectors, so both apps change.
Why
The four names are EXTERNAL_GOVERNANCE_ATTR_SYNC_BACKGROUND, EDC_DOPP_LOCK, EDC_DOPP_BACKGROUND and SQP_STATS_REPORTING. T-SQL ignores the trailing space, so the server never shows a problem. Every comparison in this product is exact. That includes the C# ignore set, the DuckDB filters and the PostgreSQL predicates. An ignore entry without the space never matched one of these names. A lookup by the clean name found nothing.
A 3-hour measurement on one Hyperscale database (93 buckets of 2 minutes) shows two background waits that are timers:
One waiter on a fixed interval that does not follow the workload is a timer, not contention. Kept in the charts, it only adds a constant line.
REMOTE_BLOCK_IO has the same timer shape in that measurement, and it stays visible. Its name says real remote I/O. The test workload most likely never forced a page-server read. Microsoft Learn lists it as "Internal use only". A Hyperscale operator needs to see a real page-server read wait.
A store on a SQL Server target holds none of the four names. The wait_stats collector writes only types with wait time above zero. All four sit at zero on the SQL Server 2022 and 2025 instances checked. Only Azure targets see a key change.
What changes
WaitTypeName.Trimin the Collectors assembly. It removes trailing whitespace and passes null through.ServerTab.ReadLiveSnapshotRow, which trims the name the same way. Darling's live Active Queries fetch already reads through the collector.PerfmonCounterTypeTestspins that. So the system_health parser in Common repeats the one-lineTrimEnd(), with a comment that points at the helper.IgnoredWaitDefaults.Alland Lite'sconfig/ignored_wait_types.json. A test keeps the two lists in step. The measurement sits in a comment beside the entries. That comment also says why REMOTE_BLOCK_IO is not listed.Which component(s) does this affect?
Test plan
The baseline pins model an upgrade. A store holds a row with the spaced name, the delta calculator seeds from it, and the first pass reads the same name. The Lite pin uses a real DuckDB file and the real
DeltaCalculator. The Darling pin uses the realDarlingDeltaCalculatorwith the spaced baseline in its cache, as the store seed leaves it. A clean-named wait seeded the same way is the control. It gets a real delta on the same pass.Red on the base code
The new pins ran with the read sites, the two ignore entries and the system_health trim reverted. The helper stayed so the tests compile. Each line below is red on that code and green with this change.
Lite.Tests, 12 of the 49 tests in the four classes failed:
Darling.Tests, 6 of the 7 tests in the class failed:
Some pins pass on the base code by design. The four helper tests in Lite need the helper. The Darling runner pin holds behavior that already exists and that the new entries depend on. Each of them went red under a mutation.
The helper mutation and the guard mutation ran alone. The list entry and the runner binding ran together in one pass, and each failure set below belongs to one of them. Only the list entries can fail a list pin, and only the runner source can fail the runner pin. Git restored every mutated file after the runs:
WaitNameTrimBaselineLiteTests.FirstSampleUnderTheTrimmedKey_IsABaseline_NotTheRunningTotal [FAIL]was the only failure among the 15 tests in the two WaitNameTrim classes (Expected 0, Actual 101). The Darling test of the same name was also the only failure, with the same values.StallWaitProbePolicyTests.ReadAsync_TrimsTheTrailingSpaceAWaitNameCarries. In Darling, 3 of 7 failed: the two..._AndStillCollectsRemoteBlockIotests and the baseline test.SharedCollectorDefaultsPinTestspins failed. In Darling, the two..._AndStillCollectsRemoteBlockIotests andTheSharedIgnoreList_NamesTheHyperscaleTimers_AndLeavesRemoteBlockIoVisiblefailed.IgnoredWaitTypesbinding inDarlingCollectorRunnerreplaced with an empty set.WaitNameTrimDarlingTests.TheServiceRunner_BindsTheSharedIgnoreList_AndNoOtherSource [FAIL].ServerTab.ReadLiveSnapshotRow, run alone. That method is new in this change, so its pin cannot run on the base code.WaitNameTrimTests.LiveSnapshot_ReadRow_ShowsTheTrimmedWaitType [FAIL]: Expected "EXTERNAL_GOVERNANCE_ATTR_SYNC_BACKGROUND", Actual "EXTERNAL_GOVERNANCE_ATTR_SYNC_BACKGROUND " (the name with its trailing space).What to double-check
ignored_wait_types.json. The seeder never overwrites it. So the two new entries reach new Lite installs and Darling only. A merge of new defaults into an existing list adds back any entry that a user removed on purpose. That is a product decision, so this PR leaves it.SystemHealthSignificanceholds a verbatim copy of the sp_HealthParser ignore list. The parser now trims the name before the gate compares it.SessionSummaryStatsCollectoruses wait_type only in a server-side LIKE test that counts sessions. It stores no name.DmvBlockingSnapshotCollectordoes store a value built from the name. Its lock_mode column is the wait type with the LCK_M_, PAGEIOLATCH_ and PAGELATCH_ prefixes removed. But its filter admits only LCK_, PAGELATCH_, PAGEIOLATCH_ and RESOURCE_SEMAPHORE waits, so none of the four names can reach it.CHANGELOG
SECTION: Fixed
ENTRY:
REF:
[Wait names lose their trailing space, and two Hyperscale timer waits are ignored #4884]: Wait names lose their trailing space, and two Hyperscale timer waits are ignored #4884