Skip to content

refactor(materializer): remove the per-pass fold — the daily refresh is the only rollup writer (#55 step 5) - #65

Merged
elffjs merged 1 commit into
mainfrom
dq55-remove-fold
Aug 10, 2026
Merged

refactor(materializer): remove the per-pass fold — the daily refresh is the only rollup writer (#55 step 5)#65
elffjs merged 1 commit into
mainfrom
dq55-remove-fold

Conversation

@elffjs

@elffjs elffjs commented Aug 10, 2026

Copy link
Copy Markdown
Member

The final code step of #55: with prod on MATERIALIZER_DAILY_ROLLUP_MODE=on for three days (spans 275–515s → 6–19s, cardinality holding rows==keys through every refresh), the per-pass fold is dead code — and dead code that raced din's compaction into 823k duplicate rows when it was alive. This deletes it and retires the shadow scaffolding that existed to validate its replacement.

What's deleted

  • captureRollupDelta + foldSignalsRollup + their span-transaction call sites (the span txn is now base-insert + late-subject marking + KV publish + cursor, unconditionally).
  • DailyRollupShadow mode, the shadow diff, dailyRollupDiffRows, the shadow-table creation path, PruneDecoded's shadow orphan block. The lake.signals_latest_daily name survives only in the boot-time promote (a node upgrading straight from shadow-era still gets the verified swap; an aborted shadow seed is dropped).
  • tests/ducklake_incremental_rollup_test.go wholesale — every test in it existed to prove fold==recompute, and the fold is gone. Shared helpers moved to tests/rollup_helpers_test.go; every surviving rollup assertion now pins against a recompute oracle or the production daily-refresh path (no assertions weakened; the commit body lists the per-test dispositions).

Behavior changes

  • Default mode is now on (empty MATERIALIZER_DAILY_ROLLUP_MODE ⇒ daily refresh): with the fold gone, an unmaintained serving rollup must not be reachable by accident. off remains for tests and logs a warning; shadow no longer parses (configs must move to on — prod already has).
  • LAKE_ROLLUP_DAILY_SERVING sample/docs flipped to true to match.
  • kv shadow-mode docs note the day-stale-baseline kv_newer noise (the modes remain for gross-mismatch checks).

Prod deploys this as a no-op (config already on); the paired dimo-node PR will bump the image and return MATERIALIZER_MAX_SNAPSHOT_SPAN to 16 — the last line of #55.

Includes #64's probe-per-refresh fix (rebased over its merge). Authored with a subagent executing a written spec; diff reviewed hunk-by-hunk and all gates re-run independently: build clean, full suite green, lint 0 issues.

Refs #55. Closes #55 when deployed.

🤖 Generated with Claude Code

…is the only rollup writer (#55 step 5)

With prod on MATERIALIZER_DAILY_ROLLUP_MODE=on, captureRollupDelta +
foldSignalsRollup (and the _rollup_delta/_rollup_new temp tables) are dead
code: delete them, so the span transaction is base-insert + late-subject
marking only. The shadow-mode scaffolding that existed to validate the flip
goes with them — DailyRollupShadow, DailyRollupDiff (and the diff-rows
gauge), createDailyTable, the shadow branches of seedDailyRollup /
observeRollupCardinality / PruneDecoded, and dailyTargetTable (always
lake.signals_latest now). The boot-time promote of a leftover
lake.signals_latest_daily table is KEPT: a node upgrading straight from
shadow-era config still needs it, and after the promote the table is gone
forever.

ParseDailyRollupMode: empty now means ON — with the fold gone an
unmaintained rollup must not be reachable by default; "shadow" is invalid so
stale configs fail loud at boot; "off" survives for tests/one-off ops and
warns at LoadDailyRollupState. FlushRollup's signals path serves only mode
off + the backfill catch-up (its remaining consumers); RecomputeRollup keeps
its mode-on refusal. dailyRollupSideRows only ever emits side="live";
the cardinality probe now also runs after every mode-on refresh (it
previously rode on the shadow diff). LAKE_ROLLUP_DAILY_SERVING docs/sample
flip to true — a plain rollup read under-counts the tail against a
daily-maintained rollup.

Tests: ducklake_incremental_rollup_test.go is deleted whole (it tested the
deleted fold; its rollupRow/dumpRollupMap/incrRunner/drainNoFlush helpers
move to rollup_helpers_test.go, joined by oracleRecompute and a
refreshRollup helper that runs the production daily-refresh path).
assertMatchesRecompute is deleted with it — the verify campaign now asserts
through an explicit recompute, since the differential premise is gone.
ducklake_daily_rollup_test.go is rewritten to prove seed/fold/late/
idempotency under mode on against lake.signals_latest with a
full-recompute oracle; the shadow-diff "detects divergence" scenario is
deleted (no diff exists to test). The flip test manufactures shadow-era
leftovers directly and adds the aborted-seed drop path. Fold-dependent
tests (rollup/query/kv/parity/retention/migration/only) gain an explicit
refresh or recompute after draining; the subject-scoped flush and
dirty-set-overflow tests switch to backfill mode, the machinery's remaining
driver.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@elffjs
elffjs merged commit c67a18f into main Aug 10, 2026
6 checks passed
@elffjs
elffjs deleted the dq55-remove-fold branch August 10, 2026 12:50
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.

materializer: replace per-pass signals_latest fold with a daily watermarked refresh

1 participant