Skip to content

Don't re-hold the raw purge for an empty successor mid-upgrade (#4300) - #4406

Merged
erikdarlingdata merged 2 commits into
devfrom
fix/4300-l1-empty-successor-window
Sep 26, 2026
Merged

erikdarlingdata merged 2 commits into
devfrom
fix/4300-l1-empty-successor-window

Conversation

@erikdarlingdata

@erikdarlingdata erikdarlingdata commented Sep 26, 2026 •

Copy link
Copy Markdown
Owner

Refs #4300.

Why

An upgrade with no outage should not re-hold the raw purge just because a
freshly-shipped successor rollup has not run its first refresh yet. The
purge-arming probe's stitched fallback (used when the successor holds no
bucket above the frozen legacy's last one) treated every hour between the
legacy's freeze and right now as an unprobed hole, reporting the coverage
slot as Short. On a healthy store mid-upgrade that's a false alarm: the
successor's own first refresh reaches every bucket newer than its refresh
start offset on its own, so those recent hours are never actually at risk
from the raw purge (which only drops chunks older than its own multi-day
retention window). The false alarm sent the operator toward a "hold" log
line for a state that clears itself within about the refresh's own start
offset.

What changes

RetentionArmSafetySql's legacy-branch fallback now ends the probe window
at now() - HourlyRefreshStartOffset instead of a bare now(). The probe's
other bound (the successor's first bucket above the legacy's last one, when
one exists) is unchanged, as is the - INTERVAL '1 hour' bucket-width
trim. The fallback still derives entirely from the same
HourlyRefreshStartOffset constant already used everywhere else the hourly
refresh window matters — no new literal was introduced. The class doc
comment's sentence about the upper bound is updated to describe the new
bound and why it's safe.

Test plan

  • Unit (RED on dev, GREEN on this branch):
    TimescaleContinuousAggregateTests.RetentionArmSafetySql_StitchedSlot_FallbackUpperBoundUsesHourlyRefreshStartOffset
    asserts the generated SQL carries the offset-adjusted fallback and no
    longer carries the bare now() form. RED on dev (fails: old SQL has no
    offset subtraction), GREEN on this branch. Full class: 44/44 pass on this
    branch.
  • Live, no outage (RED on dev, GREEN on this branch):
    FrozenRollupLiveTests.Upgrade_NoOutage_EmptySuccessor_RawPurgeStaysCovered
    seeds a legacy frozen a few hours back, raw rows every hour up to now, and
    an empty successor. IsRawTierDropSafeAsync must read true. RED on
    dev (reads false), GREEN on this branch.
  • Live, real outage still caught (passes on both dev and this branch —
    the safety pin):

    FrozenRollupLiveTests.Upgrade_RealOutageInsideTheFallbackWindow_RawPurgeStaysNotSafe
    seeds the legacy's last bucket 3 days back and raw rows between 3 and 1
    days back that neither side ever materialized — a genuine hole sitting
    entirely inside the new fallback's window. IsRawTierDropSafeAsync stays
    false on both commits: the fix only narrows the fallback's upper bound,
    it never widens what counts as a hole below it.
  • Full class runs against a fresh TimescaleDB 2.30.1 rig, this branch:
    FrozenRollupLiveTests 21/21 pass, TimescaleContinuousAggregateTests
    44/44 pass, MaterializationHoleRepairTests 18/18 pass,
    RollupBackfillLiveTests 9 pass / 3 skipped (Windows-only prerequisites,
    as before), 0 failed across all four classes.
  • Darling.Tests and Lite.Tests both build 0 errors / 0 warnings with
    EnableWindowsTargeting=true. Both target net10.0-windows and cannot
    run on this platform; the live results above come from running
    Darling.Tests.dll in-process on this Mac against a real container, not
    from CI, which still decides the Windows-only classes.

CHANGELOG entry

SECTION: Fixed
ENTRY:

@erikdarlingdata
erikdarlingdata marked this pull request as ready for review September 26, 2026 11:39
@erikdarlingdata
erikdarlingdata merged commit 3ccda25 into dev Sep 26, 2026
15 of 16 checks passed
@erikdarlingdata
erikdarlingdata deleted the fix/4300-l1-empty-successor-window branch September 26, 2026 11:39
erikdarlingdata added a commit that referenced this pull request Sep 26, 2026
…et (#4443)

Adds a live pin that the raw-purge seam probe stops at the successor's first bucket above the frozen legacy's end.

- Upgrade_NonEmptySuccessor_HoleInItsOwnSpan_SeamProbeStopsAtItsFirstBucket_RawPurgeStaysCovered seeds three things: a frozen legacy, a non-empty successor with no seam hole, and one unmaterialized hour inside the successor's own span, above its first bucket.
- IsRawTierDropSafeAsync must still read Covered. The seam probe covers only the gap between the legacy and the successor, not the successor's own span.
- The existing tests stayed green when the probe's upper bound was widened to now() - 1 day. Under that change, this pin fails: the probe walks into the successor's span, finds the hole and reads Short.
- Test-only.

Refs #4406
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant