Repository navigation
The materialization hole scan reports on every start: one unconditional INFORMATION line with the pass's full tally, so a zero-hole start no longer reads the same as a start whose scan never ran (#3756) - #3771
Merged
Conversation
…al INFORMATION line from the worker with the pass's full tally, so a zero-hole start, a start whose scan threw, and a start that never reached the scan no longer read the same (#3756) The pass wrote a summary only when it had repaired, deferred or failed something and a Debug line otherwise, so at the level a production log is read a start that found nothing wrote nothing - which is also what a start that never reached the scan writes. The first store to carry the scan showed exactly that: no line, and "zero needed" accepted on inference. Storage: RepairMaterializationHolesAsync returns the complete tally (HolesFound and BucketsFound counted as the scan saw them BEFORE the cap, Elapsed from the pass's own clock) and no longer writes an end-of-pass summary; the per-hole, per-deferral and per-failure lines stay. Worker: RunMaterializationHoleRepairAsync takes the tally and writes ONE INFORMATION line whatever the counts, plus a distinct line when shutdown cancels the scan before it can report (rethrown, so the drain is unchanged). The connection-open WARNING stays. Every outcome of a launched scan now leaves a different line. INFORMATION even with failures > 0: the per-aggregate WARNING already names the failure; the summary is the census. Tests (Darling.Tests only - Darling-only code): the source pin that the worker's line is unconditional (no `if` between the call and the line) and that the pass writes none of its own; the tally's straddle arithmetic; the live zero-hole pass returns the walk with zeros; a new live pass plants one 26-bucket hole under the 24-bucket cap and pins found 1/26, repaired 1/24, deferred 1/2 on the same pass. The file's private repo-root walk retired for RepoFile.ReadRepoFile.
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.
Descends from #3731 / #3653 Q10. Closes #3756.
The lie
TimescaleSupport.RepairMaterializationHolesAsync(#3731, Q10) runs at every service start and returns aMaterializationHoleRepairSummary. What the issue could not see from the log, the source shows: the pass wrote its own end-of-pass summary only when it had repaired, deferred or failed something, and a Debug line otherwise. At the level a production log is read that is nothing — and nothing is also what a start writes when the scan is never reached, and what a scan that threw before its first probe and was swallowed writes once you have not read the per-aggregate WARNING. So on the first install to carry the scan, the largest production store's log had no repair line, the reading "the day's refreshes had already healed it, the scan correctly found nothing" was almost certainly right, and it could not be confirmed. The countersign accepted "zero needed" on inference. This is the self-proving-flag class (#3574'svisibility, #3735'scollection_health_age_seconds): a check whose negative outcome is indistinguishable from its non-execution has not reported.What lands
Storage — the pass returns the whole tally and writes no summary of its own.
MaterializationHoleRepairSummarygainsHolesFoundandBucketsFound— counted as the scan SAW them, contiguous ranges and the buckets they span, before the cap decides what this start repairs and what it leaves — andElapsed, from aStopwatchstarted at entry so it is the pass's own cost (detect, every probe, every refresh) and not the caller's connection open. The record's doc states the arithmetic a reader of the line needs:BucketsFound == BucketsRepaired + BucketsDeferredalways;HolesRepaired + HolesDeferredexceedsHolesFoundby one for every range the cap split (one hole as the scan saw it, two lines as the repair reported it). Buckets are the unambiguous currency. The conditional Information/Debug summary at the end of the pass is removed — one owner, one line per start — and the per-hole, per-deferral and per-failure lines stay: they are the detail the summary counts.Worker — one INFORMATION line per start, whatever the counts.
RunMaterializationHoleRepairAsynctakes the returned tally and the very next statement is the line, with no condition between them:The
TimescaleDB:prefix is the neighbouring baseline-backfill line's. Beside it, a newcatch (OperationCanceledException)arm writes a different line — the scan was cancelled before it could report; at shutdown that is expected and the next start's scan re-finds anything this one left — and rethrows, so the drain at shutdown sees exactly the cancellation it was written to swallow. The connection-open WARNING is untouched. Every outcome of a launched scan now leaves a different line, all from this one method: returned (the summary, zeros included), threw outside its isolation or could not connect (the WARNING), cancelled (the cancellation line). A start that never launches it — plain-PostgreSQL mode, or the TimescaleDB block faulting first — already writes its own line at the launch site's catch, and nothing in the runner can or should speak for that case.Why this shape
The line is the worker's, not the pass's. The thing the line has to prove is that the scan RAN on this start, and "this start" is the worker's concept: the worker owns the connection open, the launch, the drain, and therefore the two outcomes a summary written inside the pass can never report (the pass never entered; the pass was cut short). Putting the line at the call site puts all three arms in one method, so a reader has one place to look and the pass cannot produce a second summary on a start that had holes.
INFORMATION even when
Failures > 0. The issue asks for INFORMATION with the failure count and the file's conventions agree: the per-aggregate isolation already writes a WARNING naming the aggregate and the message. The summary's job is the census; repeating the failure at WARNING would count it twice in any warning-rate read of the service log.Found counted before the cap. Otherwise "found" is just "repaired plus deferred" restated, and the reader cannot tell one 26-bucket hole from a 24 and a 2. The straddle is real (the cap splits exactly at the budget —
CapMaterializationHoleRepairs), so the record's summary states the relationship instead of leaving the reader to reconcile three numbers that do not add.What this does NOT do
holeRepair = RunMaterializationHoleRepairAsync(...)), the drain,EvaluatePgLongRunningQueryAsync,LatestCpuSql, or anything else inDarlingWorker.cs— the hunk is the runner only.RunBaselineBackfillAsync, whoseif (backfilled > 0)is the same silent-zero shape; it is coverage-gated to a no-op on every start after the first, so it is a separate noise decision, reported rather than made here.Tests (Darling.Tests only — this is Darling-only code; Lite has no twin)
Executed here (net10.0 mactest harness against
timescale/timescaledb:2.28.1-pg18, the CI tag, ondesktop-linux):MaterializationHoleRepairTests10/10 — the 8 existing pure pins plus two new:Worker_WritesTheSummaryLine_Unconditionally_AndThePassWritesNoneOfItsOwn(source pins: the runner takes the tally; the nextLogInformationis the summary; noif (orreturn;between call and line; every placeholder the tally carries is named; the cancellation arm exists, says "cancelled before it could report" and rethrows; the connection-open WARNING stays; the Storage file no longer contains the old summary template and does contain the found counters and the pass clock) andTheTally_CarriesEveryCountTheLineNames_AndStatesTheStraddleArithmetic. Red-first shown by mutation: gating the worker's line onsummary.HolesFound > 0→ the source pin fails on theif (.MaterializationHoleRepairLiveTests2/2 live: the existing outage-tail test extended — first passHolesFound 1 / BucketsFound 2,Elapsed > 0, the pass writes no summary line of its own; the zero-hole second pass returns the walk (AggregatesScanned >= 1,Scanned + Skipped == Targets.Count, every hole count 0,Elapsed > 0, and nothing at Information or Warning from the pass) — this is the issue's named pin, on the tally the worker's unconditional line reports. NewOneHoleRepairedAndOneDeferred_TheTallyCarriesFoundRepairedAndDeferredExactly_AgainstDevPostgres: one 26-bucket hole planted below the hourly policy window and inside the raw horizon, under the 24-bucket cap → found 1 / 26, repaired 1 / 24, deferred 1 / 2 on the same pass, buckets add up, ranges exceed found by one, the deferral line names[H25, H27)and the cap; pass two repairs the remainder 1 / 2; pass three is the zero walk. Red-first shown by mutation:holesFound += 0→ both live tests failExpected: 1, Actual: 0.RepoFileAdoptionTests,StragglerCommandTimeoutTests,LiveCleanupConversionRatchetTests,MeasurementContractCensusTests,McpPageContractTests,FleetIdentifierScrubTests,LivePostgresCollectionHygieneTests,DocCommentHygieneTests(76/77; the one red isTheDerivedProjectListCoversEveryProjectInTheTreefinding the uncommitted harness csproj — the documented artifact). Harness total 220/221 for the same reason.Darling.Tests(net10.0-windows) each build-c Release -p:EnableWindowsTargeting=truewith 0 warnings.CI-first: nothing of substance — the worker's line is a
LogInformationover the record's fields and cannot be driven here (the worker is a hosted service); its shape is pinned by source and its inputs live. The fullDarling.Testsrun on Windows is CI's.The existing
Worker_LaunchesTheRepair…pin'sReadWorkerSourceprivate root walk is retired forRepoFile.ReadRepoFile(raw; every anchor is single-line, so it is deliberately not on the LF roster).Closes #3756