Repository navigation
The newest-snapshot block-count test no longer fails between 00:00 and 04:00 UTC (#5608) - #5609
Merged
Merged
Conversation
…unk, so it no longer fails between 00:00 and 04:00 UTC (#5608)
…nd a census fails one that does not (#5608)
…Clock, so a run can be proved at 00:30 and 12:00 UTC (#5608)
…m LiveClock, and the census covers them (#5608) Audit e1: live test files seed and read through LiveClock (five anchored inside one chunk). Files whose product path reads the real clock stay on it with a reason, and LiveClockCensusTests gains a second rule for live files that read EXPLAIN or the chunk catalog and a wall clock directly.
…ss-path test seeds from the clock the product reads (#5608) The 22 entries that said another lane would audit the file now state why the file is safe as it is. The force-plan access-path test went back to the real clock for its seed: the product read takes its two-hour window from its own clock, so a seed placed by the test clock override fell outside it.
erikdarlingdata
marked this pull request as ready for review
October 9, 2026 03:09
erikdarlingdata
enabled auto-merge (squash)
October 9, 2026 03: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.
Fixes #5608.
SECTION: None
ENTRY: (test-only change; no CHANGELOG entry)
Why
The newest-snapshot block-count test seeded four hours of perfmon rows ending at the wall clock and compared the blocks the old and new reads touch. Its 3x margin held at midday and failed at 00:50 UTC (perfmon old=264 new=120), so every pull request whose Darling PG shard ran between 00:00 and 04:00 UTC failed.
Diagnosis, measured on a throwaway rig by pinning the test's end time (perfmon blocks, old read 264 in every layout):
So the cause is the chunk boundary, but not as "two chunks cost twice": raw chunks are one UTC day. When the window sits wholly in one chunk the new read costs 78 blocks at every hour. When it straddles midnight the newest chunk holds only the rows since 00:00, and the 60 rows that share the newest timestamp sit in a different physical order. The newest read walks back through those rows until it meets each counter ("Rows Removed by Filter" was 39 to 41 per counter at 00:50, 15 to 17 at 01:13), so the count swings between 13 and 138. That is a property of the seed (60 tied rows scrambled by the insert), bounded by one snapshot's rows, not a worse shipped read: the product read never touches more than the newest snapshot's rows per counter. No product change.
What changes
LiveClocktest helper (Darling/Darling.Tests/LiveClock.cs):AnchoredEnd()is a fixed point inside one chunk (22:00 UTC on the previous UTC day), andNow()is the clock with a test-only override, the environment variableDARLING_TEST_CLOCK_UTC, read only by tests. The machine clock is never touched.NewestSnapshotReadsLiveTests: every window ends atLiveClock.AnchoredEnd(). The guard is unchanged.PlanRegressionDailyReadShapeLiveTestsandQueryStoreIntervalFloorWarmUpTests: their wall-clock reads go throughLiveClock.Now()so a run can be proved at any hour (verdicts below).LiveClockCensusTests(Guard stage, per-file allow list with a reason per entry, stale entries fail, anti-vacuity floor): a live test that reads a plan'sShared Hit Blocks/Shared Read Blocksand reads the wall clock withoutLiveClock.fails. The allow list is empty.EXPLAIN,show_chunks,timescaledb_information.chunks,compress_chunkorChunkAppendand reads the wall clock in code withoutLiveClock.fails unless it is ins_allowedPlanShapewith a reason. Stale entries fail; a floor of 20 files keeps the scan from going vacuous.Audit (W1)
Files that are live, read a wall clock and look at a plan or chunk catalog: about 77. Audited so far, the ones that assert on block counts:
Audit e1
Method: every file on the e1 list was read at its clock sites. Where the test hands its "now" to the product read, it now takes it from
LiveClock; where the product reads the real clock itself (SQLnow(),DateTime.UtcNowin product code) the test stays on the real clock and the assertion is checked to hold for any chunk layout. Each converted class ran withDARLING_TEST_CLOCK_UTCat 00:30Z, at 12:00Z and unset (the real clock was 02:00 to 02:40 UTC, inside the failing window), three runs at once on separate databases. A first pass of the same classes at the real clock, before any change, passed everywhere.LiveClock.Now; the two tool tests stay on the real clock (they failed under 12:00Z)LiveClock.AnchoredEnd()now(); assertions hold for any layoutLiveClock.Nownow(); per-chunk setting is layout-freeLiveClock.NowLiveClock.NowSeedFromStoreAsyncreads the real clock; two tests failed under 00:30ZLiveClock.Nownow(); failed under 12:00ZLiveClock.NowLiveClock.NowLiveClock.NowLiveClock.AnchoredEnd()LiveClock.NowLiveClock.Now(see Not done)LiveClock.Now, except that one testLiveClock.Nownow()(failed under 00:30Z)LiveClock.Noweverywhere except that one seedNot on either list, found by the census scan:
JobHistoryWatermarkEpochLiveTests(now passed to the product; converted toLiveClock.Now) andPerfmonRegroupLiveTests(only a poll deadline; e2 checked it, see Audit e2).Census: the e2 files were put on
s_allowedPlanShapewith the reason "e2 audits". After merging e2's push I dropped the eight entries for the files it converted. 22 "e2 audits" entries remain, one per file e2 left on the real clock; each should be replaced with e2's reason from the Audit e2 table (not done, see below). RED-plant at this head: a planted live file withEXPLAINandDateTime.UtcNowmade the new test fail and name it. The class is new on this branch, so there is no origin/dev copy to run; the same regexes over origin/dev's test folder match every such file because dev has no helper.Totals (final code except the merge, one run each; every class Failed: 0 unless noted):
Largest classes: DarlingSelfAlertTests Total 294, FrozenRollupLiveTests 31, DarlingRetentionTests 48.
Not run (the lane hit its wall limit): the
*Census*and*Ratchet*classes other thanLiveClockCensusTests,-trait Stage=Guard -failSkips, andLiveClockCensusTestsafter the merge with e2's push and the eight-entry removal. The merge was clean, but the census was last run before it. The rig on port 5607 may still be running.Not done:
TheShippedRead_PlansAsAnIndexOnlyScanOnTheCoveringIndex_AgainstDevPostgres, passed under 00:30Z in the first sweep and failed under 00:30Z on a fresh database in the last one; it passed at 12:00Z and unset. It also failed in all three settings when anchored withAnchoredEnd(), which is why it is onLiveClock.Now. Not diagnosed; CI runs it unset, where it passes. A successor should find what the index-only-scan plan needs (visibility map or the chunk the rows land in).Audit e2
Second half of the live files that read a wall clock and use EXPLAIN or the chunk catalog (34 files). The real clock was 01:47 to 02:30 UTC while I worked, so every class below also ran inside the 00:00 to 04:00 window for real, and the converted ones were also run with the override at 00:30Z and 12:00Z.
Result: 8 files moved to
LiveClock.Now()(the test hands that time to the product as the window end), 26 left alone with the reason below. No file neededAnchoredEnd(): none has an assertion that moves with the chunk or day layout.now(), compares the same store before and after the indexes, so the layout is the same on both sidesnow()is only a stampRuns (rig on UTC, Total / Failed):
*Census*,*Ratchet*, LiveClockCensusTests-trait Stage=Guard -failSkips0 build warnings.
Not done in this section:
AnchoredEnd()and no assertion was changed, so there is no guard to plant against. The one-way proof that the override reaches the product is the PgTargetSeqScanTests failure at 12:00Z.Finish (lane f)
Head after this lane: 2641503 (one commit on top of 2848555). The branch was not merged with dev.
What I ran on the merged head, rig on UTC, 0 build warnings:
*Census*class*Census*rerun, 499 / 0)*Ratchet*class-trait Stage=Guard -failSkipsWhat I fixed:
PerfmonRegroupLiveTests.csentry pointed at lane e2's audit, and it now states e2's verdict (whole UTC days from the run start date, the two-days-back seed that was Perfmon data is compressed per counter, so a one-counter trend reads only that counter's data (#5574) #5579's defect replaced by three to five days back, chunk counts from the database clock). No "e2 audits" or "lane e2" text remains inLiveClockCensusTests, and theE2Auditsconstant is gone. The stale-entry check passed with all of them, so none of the files stopped matching the rule.ForcePlanFailuresAccessPathTests: see the verdict.ForcePlanFailuresAccessPathTests verdict: a clock-mixing defect, not a layout defect, and now fixed.
Assert.Single(failures)on the product read (The collection was empty), which is after every plan assertion, so the plan check passed in all five.LiveClock.Now()and handed that to the EXPLAIN, butDarlingAlertReadAdapter.GetForcePlanFailuresAsynctakes its two-hour window from the product's ownDateTime.UtcNowand has no clock seam. With the override at 00:30Z and the real clock about two and a half hours later, the seed fell outside the product's window and the read came back empty. At 12:00Z the seed sat in the future of the real clock, and the window has no upper bound, so it passed. That also explains the earlier single failure, the earlier pass at 00:30Z (the real clock was then within two hours of it), and whyAnchoredEnd()failed in all three settings (22:00 on the previous day is always more than two hours before the real clock).DateTime.UtcNow, with a comment saying why. The plan assertions hold across a chunk boundary (the five 00:30Z runs passed all of them before the read), so no anchor is needed. The file moves to the census allow list with that reason.Assert.Contains() Failure: Sub-string not foundon "Index Only Scan" / the index name; 4 / 1). Plant reverted, rebuilt, 4 / 0.RED-plant of the census at origin/dev (5ac86ba): I copied
LiveClock.csandLiveClockCensusTests.csfrom this head onto a clean origin/dev checkout in scratch, built (0 warnings), and ranLiveClockCensusTests: 4 total, 2 failed. Both rules fail: the block-counting rule and the plan/chunk-catalog rule together name 34 distinct live files (37 names across the two failures) that read the clock withoutLiveClock. The checkout is deleted.Rig: removed. No full suite was run on this laptop; CI decides.
Not done
Nothing. Lane f finished both items left under "Not done" in Audit e1 (see Finish above).
Test plan
NewestSnapshotReadsLiveTestswith the clock override unset, at 00:30Z, 00:55Z and 12:00Z: Total 6, Failed 0 eachPlanRegressionDailyReadShapeLiveTests: Total 2, Failed 0 unset, at 00:30Z and at 12:00ZQueryStoreIntervalFloorWarmUpTests: Total 17, Failed 0 unset, at 00:30Z and at 12:00ZLiveClockCensusTests: Total 4, Failed 0 before the e2 merge; RED-plant (wall-clock end put back, and a planted EXPLAIN plus clock file) fails naming that file*Census*: Total 497, Failed 0;*Ratchet*: Total 15, Failed 0;-trait Stage=Guard -failSkips: Total 1446, Failed 0 (these three were run by e2 on its tree, not on the merged tree)Double-check
The anchor is the previous UTC day at 22:00, so the data is a day old; no test here depends on recency.
LiveClock.Now()returns an unspecified-kind time; converted tests wrap it as the kind they used before. Check ForcePlanFailuresAccessPathTests at 00:30Z (see Audit e1) and run the census on the merged head before readying.