You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
The compression band puts the three heaviest hypertables on consecutive minutes: a 3.5-minute triple overlap starves alert reads nightly — the grid spaces by list position, not by measured runtime #3678
The nightly compression band schedules the three largest raw-table compressions on consecutive minutes — on the largest production store query_stats at 00:40 (ran 270 s), query_snapshots at 00:41 (265 s), query_store_stats at 00:42 (454 s) — so their runs overlap into one three-wide window from 00:42 to ~00:45:30. Inside it, ~12 servers' capture-down alert reads died at their 10-second deadline (stamps clustered 00:43:27–00:46:07), and a four-server simultaneous kill at 01:01:27 (12-second stamps — the checkpoint tail after the compression WAL storm is the unproven suspect) followed. Itemised for the first time tonight: yesterday's install reset the read-failure counter, so last night's identical band hid inside the .444-era noise. +39 failures in the hour; the store's 01:15 heavy refresh set an all-time 922 s at 00:15 the same night.
Mechanism (VERIFIED at source)
TimescaleSupport.CompressionPhaseMinutes is derived to avoid every refresh's window (BuildCompressionPhaseMinutes, ~3606), and a hypertable takes its minute by list position: CompressionPhaseMinutes[index % Count] over CompressionPhaseOrder (~3682), CompressionPhaseMaxPerMinute = 3. The grid (#3012 → #3035 → #3174 → #3185) was derived to deconflict compression from refresh locks and to keep the band's spread thin (#3112's midnight chunk-close burst); its doctrine is "every boundary is derived from a measurement or from the catalog." What it does not carry is a per-member runtime term, so the three heaviest members can — and on this store do — land on adjacent minutes, and a job that runs 4–8 minutes on the daily chunk close overlaps the next two. The band protects writers from lock convoys; nothing spaces heavy compressions apart from each other, and the collateral is read starvation (the same class as #3597, one job family over).
Fix shape — in the grid's own doctrine, not a tuning knob
Space the heavy members by measured runtime: the compression job's own job_history durations (available since #3585/#3610 through the owner pool) give each hypertable a p95 chunk-close runtime; members whose p95 exceeds the per-minute step get their own clearance — the next heavy member starts no earlier than the previous one's p95 — while the ~65 light members keep the three-per-minute spread. Equivalent formulation the scheduler could carry instead: hold job N+1 until job N completes when N's table is above a size threshold. Either turns a 3.5-minute triple overlap into three separate ~1-minute windows without widening the band past what the heaviest refresh's slot can spare (the constraint #3174 pinned).
Sequencing — re-measure before laning
The two victim reads named tonight are about to change: #3647 took the capture-down read from 9,573 buffers to 14 (chunk-ordered, newest chunk only) and #3586 made the forced-plan read an Index Only Scan. Both ride the pending nightly. If the victims are immune, the band's overlap costs only compression time, not alert coverage, and this issue's lever is optional hygiene; if reads still die inside 00:42–00:46 after the install, the lever is live. Discriminator: the first post-install night's band — the read-failure counter's 00:36–00:59 delta and its per-server stamps against the three jobs' job_history start/finish. The monitoring seat's ticks already treat band-window jumps as known-class; the re-measure is one tick's read. Lane only on a non-zero result.
Evidence: monitoring seat's tick 20260919T014009Z + per-server sweep and job-ledger reads ~01:45–01:50Z; source as cited. Related: #3597 (reads dying under the heavy refresh — same disease, refresh family), #3112, #3174.
Night 1 on the new build: the discriminator came back near-zero, so this is a deferral, not a lane
The issue's own sequencing rule was "lane only on a non-zero result" from the first post-install night's band. The full-band read for tonight (00:36–00:59Z, the first band carrying #3647's chunk-ordered capture-down read and #3586's Index Only Scan for the forced-plan read) on the largest production store:
Alert-side read failures inside the band: 0. The original victim class — capture-down and forced-plan alert reads dying at their 10-second deadline — went from 39 kills in the hour (the night this issue was filed) to none. That is the whole of the harm this issue named, and it is gone on the reads' own fix, before anything was done to the band.
One boundary kill, not in the victim class. The MCP overview's 7-day collection-health scan — the heaviest read class on the store — started 00:35:53 and died 00:36:08, exactly at the band boundary. Night 0 and night 1 carry one-time lifts (the install's cold caches, the first chunk close under the new refresh grid), and the kill did not recur inside the band, which is consistent with a lift rather than the overlap.
So the triple overlap at :36–:38 is real and still costs compression time, but tonight it cost no alert coverage — the lever this issue reached for is, on tonight's evidence, optional hygiene.
Disposition
The instrument stays armed for night 2 (tomorrow, 00:36–00:59Z): the first band with no night-0 lifts in it.
If any alert read dies inside the band on night 2, the lane opens — with the smallest arm first: nudge the band's first minute from :36 to :37 (one constant — CompressionPhaseGuardMinutes, or the band's first-minute derivation in TimescaleSupport.BuildCompressionPhaseMinutes) so the aggregate band's tail and the raw band's head stop touching, rather than re-spacing three hypertables by measured runtime. The runtime-spacing design above stays on the record as the second arm if the nudge is not enough.
#3745 (the daily band re-derivation) keeps its stated dependency on this issue's outcome either way.
The three-band design — one decision, three issues (#3678, #3781, #3745), written here as the coupling home
The monitoring seat's week of reads (bus 20260921T170059Z, 19:43Z, 21:40Z) settled what each band does to the largest store's readers. Every read-kill this week belongs to one of three write bands, and the fix for each is different in kind, so the decision is to name them and stop treating "the band" as one thing.
the 73 raw hypertables' daily policy_compression, assigned CompressionPhaseMinutes[index % 25] in REGISTRY order over the :35–:59 tail of hour 00
once a night
night 2 (steady state): query_stats 552 s at 00:40, query_snapshots 324 s at 00:41, query_store_stats beside them — eleven alert-read cancels 00:43–00:46; night 1's clean was a partial-day artefact
one continuous aggregate's daily compression per hour, minute :35 (= last minute of the heaviest refresh window, the minute the hourly grid holds and leaves unused)
01–23Z, one per hour
steady-state runs 68 s-class; the killer runs were the STAGED first lifts (1,246 s for query_store_stats_hourly at 03:35) — 23 of them, one per night, ending ~Oct 12; the exposed readers are anything on a 7-day scan at :35 sharp
count-only, with a re-open bar
C — hourly successor refreshes (the "third band")
#3731's *_interval_hourly successors materializing ~340–700 K rows each per hour (~2 M rows/hour at evening peak) + the checkpoint fsync tail behind them (8–25 s, chronic)
every hour
≥ 8 alert-read kills 17:16–21:04Z Sep 21; three in the 19Z hour alone; every one a seconds-long transient
no band move — reader-side, landed
D — the daily tier's single-transaction materialization (#3745)
query_store_stats_interval_daily writing 7.8–8.3 M rows in ONE transaction
once a day ~15:20Z
Sep 20: blew max_wal_size (1 GB) into a WAL-forced second checkpoint, four minutes of dead reads; Sep 21 under 16 GB: zero kills, timed checkpoints
storm class FIXED by the knob (#3802 authors it); slicing arm stays as the structural fix
Why the bands do not have to be decided together after all
The coupling risk was "moving one band's minute puts it on top of another". None of the four verdicts moves a minute:
A is fixed by a PERMUTATION inside the existing band, not a wider or shifted band. CompressionPhaseMinutes[index % Count] hands out minutes in registry order, so the three heaviest hypertables — registered consecutively — land on consecutive minutes and their 300–550 s runs overlap three-wide for ~5 minutes. The assignment becomes weight-aware: a declared, measured HeaviestHypertables set (today query_stats, query_snapshots, query_store_stats, with the measurement's date and the store class on the constant) takes the band's first minute and every Count / N-th minute after it (:35, :43, :51 for three in a 25-minute band), and the remaining tables fill the remaining slots in registry order. Same band width, same start, same HeaviestRefreshWindowMinutes — the 896 s ceiling and its 4 s margin are untouched, and RefreshCeilingProvenancePinTests does not move. What changes is which table starts on which minute; the pin is that no two of the heaviest set are within Count / N minutes of each other, plus the existing "no two policies contending for the same relation start on the same minute" invariant. A table joining the heaviest set is a measured decision, recorded on the constant.
C: nothing to lane; acceptance read after the install.
Rulings none of this needs; product decisions stated so the maintainer can overrule: (a) B closes count-only; (b) D's re-derivation waits for a real daily successor.
Activity
erikdarlingdata commentedon Sep 20, 2026
Night 1 on the new build: the discriminator came back near-zero, so this is a deferral, not a lane
The issue's own sequencing rule was "lane only on a non-zero result" from the first post-install night's band. The full-band read for tonight (00:36–00:59Z, the first band carrying #3647's chunk-ordered capture-down read and #3586's Index Only Scan for the forced-plan read) on the largest production store:
So the triple overlap at :36–:38 is real and still costs compression time, but tonight it cost no alert coverage — the lever this issue reached for is, on tonight's evidence, optional hygiene.
Disposition
The instrument stays armed for night 2 (tomorrow, 00:36–00:59Z): the first band with no night-0 lifts in it.
CompressionPhaseGuardMinutes, or the band's first-minute derivation inTimescaleSupport.BuildCompressionPhaseMinutes) so the aggregate band's tail and the raw band's head stop touching, rather than re-spacing three hypertables by measured runtime. The runtime-spacing design above stays on the record as the second arm if the nudge is not enough.#3745 (the daily band re-derivation) keeps its stated dependency on this issue's outcome either way.
erikdarlingdata commentedon Sep 22, 2026
The three-band design — one decision, three issues (#3678, #3781, #3745), written here as the coupling home
The monitoring seat's week of reads (bus 20260921T170059Z, 19:43Z, 21:40Z) settled what each band does to the largest store's readers. Every read-kill this week belongs to one of three write bands, and the fix for each is different in kind, so the decision is to name them and stop treating "the band" as one thing.
policy_compression, assignedCompressionPhaseMinutes[index % 25]in REGISTRY order over the :35–:59 tail of hour 00query_stats552 s at 00:40,query_snapshots324 s at 00:41,query_store_statsbeside them — eleven alert-read cancels 00:43–00:46; night 1's clean was a partial-day artefact:35(= last minute of the heaviest refresh window, the minute the hourly grid holds and leaves unused)query_store_stats_hourlyat 03:35) — 23 of them, one per night, ending ~Oct 12; the exposed readers are anything on a 7-day scan at :35 sharp*_interval_hourlysuccessors materializing ~340–700 K rows each per hour (~2 M rows/hour at evening peak) + the checkpoint fsync tail behind them (8–25 s, chronic)query_store_stats_interval_dailywriting 7.8–8.3 M rows in ONE transactionmax_wal_size(1 GB) into a WAL-forced second checkpoint, four minutes of dead reads; Sep 21 under 16 GB: zero kills, timed checkpointsWhy the bands do not have to be decided together after all
The coupling risk was "moving one band's minute puts it on top of another". None of the four verdicts moves a minute:
CompressionPhaseMinutes[index % Count]hands out minutes in registry order, so the three heaviest hypertables — registered consecutively — land on consecutive minutes and their 300–550 s runs overlap three-wide for ~5 minutes. The assignment becomes weight-aware: a declared, measuredHeaviestHypertablesset (todayquery_stats,query_snapshots,query_store_stats, with the measurement's date and the store class on the constant) takes the band's first minute and everyCount / N-th minute after it (:35, :43, :51 for three in a 25-minute band), and the remaining tables fill the remaining slots in registry order. Same band width, same start, sameHeaviestRefreshWindowMinutes— the 896 s ceiling and its 4 s margin are untouched, andRefreshCeilingProvenancePinTestsdoes not move. What changes is which table starts on which minute; the pin is that no two of the heaviest set are withinCount / Nminutes of each other, plus the existing "no two policies contending for the same relation start on the same minute" invariant. A table joining the heaviest set is a measured decision, recorded on the constant.AggregateCompressionBandMinuteas one comment line, riding A's PR (same file; fold-in policy).checkpointerrow makes the sync tail visible on the store's own card from the next install, and The seven self-alert store reads in DarlingSelfAlertEvaluator still run without #3851's retry-once seam — route them through it as instance methods (follow-up to #3848) #3854 (pm-user-worker) extends the retry to the seven evaluator reads. Acceptance: the site-1 counter flattens after the 06:00Z install carries The alert pass retries a store read once, two seconds later, before counting it failed — a write band's twelve-second stall no longer blinds an alert (#3848) #3851.max_wal_sizeit runs under, and the fsync tail scales with the burst —refresh_continuous_aggregate(view, start, end)over bounded bucket ranges (theRepairMaterializationHolesAsyncshape, Three hourly rollups counted every restart's zero as a sample and a >24 h outage left a two-hour hole under a floor that said covered: interval-honest successors beside the legacy trio with the phase grid re-derived by its own method, and a targeted refresh of each materialization hole at service start (partial #3653 — items 16 + 9, Q12 + Q10) #3731) instead of one policy window. That is the lane Daily band re-derivation is DEFERRED until a daily successor is added (band full 23/23) — the slicing arm landed in #3865 (buckets_per_batch = 1); the storm class was fixed by the WAL ceiling (#3802) #3745 becomes. The daily band re-derivation the issue was filed for is DEFERRED until a daily successor is actually added (the band is full at 23/23 today; re-deriving it with nothing to add moves 23 jobs for no gain) — recorded on Daily band re-derivation is DEFERRED until a daily successor is added (band full 23/23) — the slicing arm landed in #3865 (buckets_per_batch = 1); the storm class was fixed by the WAL ceiling (#3802) #3745 as the sequencing rule.Lanes, in order
15:20 → 15:33cadence drift noted on the issue rides as a rider if it is a policyinitial_startfact). Re-derivation deferred with the sequencing rule.Rulings none of this needs; product decisions stated so the maintainer can overrule: (a) B closes count-only; (b) D's re-derivation waits for a real daily successor.
The compression band seats its three heaviest hypertables on spread m…
erikdarlingdata commentedon Sep 22, 2026
Closed by the watcher: delivered in PR #3862, merged to dev.