Take the default trace's archival-empty cutoff from the server's clock - #2985
Merged
Merged
Conversation
ft.StartTime is the monitored server's local wall clock, so the host-UTC bound the archival-empty branch supplied delivered 17 hours at UTC-7 and 34 at UTC+10 instead of 24 - a silent hole in trace history on one side, and on the other the parquet double-count that branch exists to prevent. The branch now binds NULL and the query's COALESCE derives the floor from SYSDATETIME(), matching JobHistoryCollector's GETDATE()-relative fallback, whose run_datetime is server-local for the same reason. The watermark branch needs no correction: the watermark IS a stored event_time.
|
Reviewed the diff (
No correctness, parity, security, or performance issues found. Nice catch, and the added |
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.
default_trace_events.event_timeis the monitored server's LOCAL wall clock — the.trcfiles store local time andft.StartTimeships verbatim — but the collector's archival-empty branch bound its cutoff from the host'scontext.CollectionTime, which is naive UTC. Two clocks, one comparison, no error.What it delivers instead of 24 hours
ft.StartTime > @cutoff_timewith@cutoff_time = hostUtcNow - 24h. SubstitutingStartTime = utc + offset:v_default_trace_eventsUNIONs hot + parquet with no dedup, so those rows double-count — the exact outcome this bounded window exists to prevent — and recur every archival cycleOnly this branch was ever exposed. The steady-state bound is the watermark, which IS a previously-stored
event_timeand therefore already in the server's clock; the true-first-run bound is a 1900 sentinel no clock can misread. The archival branch fires only when the hot store has been emptied by retention/archival on a server that has collected before, so it is both the rarest path and the one nobody watches.The fix, and the precedent it matches
The branch binds NULL and the query's
COALESCE(@cutoff_time, DATEADD(HOUR, -24, SYSDATETIME()))derives the floor on the server. Asserting NULL rather than a computed value is what makes it stick: any host clock reintroduced here has to put a value back in that parameter.JobHistoryCollectoralready does exactly this, and its remarks say why — "computed server-side againstGETDATE(), sincerun_datetimeis the server's LOCAL wall clock", and itsArchivalEmptyFilteris built the same way. That collector's doc also records that it ports the idiom from this one. So the later port got the frame right and the original it was ported from kept the skew — the same shape #2971 found betweenDarlingAlertReadAdapterand the Lite original it came from.COALESCEis non-sargable and that costs nothing here:fn_trace_gettablematerializes every file it is handed, so no predicate onft.StartTimepushes down. The class remarks already say so.Verified
The suites are
net10.0-windowsand cannot execute on macOS, so the real test sources were compiled into anet10.0shim harness and run —DefaultTraceEventsCollectorDefinitionTestsagainst the actualPerformanceMonitor.Collectorsbuild, andCollectorTimestampFrameTestsagainst the collector source.DefaultTraceEventsCollectorDefinitionTests(19 facts)BuildQuery_Watermark_AndFirstRunGuard_TrueFirstRunVsArchivalEmpty, reporting the offending7/8/2026 12:00:00 PMCollectorTimestampFrameTests(3 facts)SYSDATETIME()removed from the cutoff expressionTwo of those failures are the point: the updated assertion and the new pin both go red on the pre-fix code rather than passing either way.
Assert.Equalin the harness compares collections structurally, mirroring xUnit — the reference-equality default made two unrelated passing pins look like failures until that was corrected.The new frame pin
CollectorTimestampFrameTestsgainsDefaultTraceEvents_DerivesEveryCutoffFromTheServersClock: this collector must carry a local clock and no UTC clock, the exact inverse of thememory_pressure_eventspin sitting beside it. Both facts stay per-TABLE. There is no store-wide rule to appeal to and no name-keyed one either —system_health.event_timeis UTC under the same column name, which is whyStoreSqlClockDisciplineTestslistsevent_timeinAmbiguousFrameColumnsand cannot judge either.