Skip to content

feat(dotc1z): opt-in rebuild cleanup strategy (clone keep-set, repoint) - #912

Open
arreyder wants to merge 4 commits into
mainfrom
arreyder/cleanup-rebuild-alt
Open

feat(dotc1z): opt-in rebuild cleanup strategy (clone keep-set, repoint)#912
arreyder wants to merge 4 commits into
mainfrom
arreyder/cleanup-rebuild-alt

Conversation

@arreyder

@arreyder arreyder commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

What

An opt-in alternative to the per-table DELETE + VACUUM cleanup. When WithCleanupRebuild(true) (or BATON_CLEANUP_REBUILD=1) is set, Cleanup instead:

  1. Computes the keep-set (same SelectSyncsToDelete policy — keep current + N most recent).
  2. Builds a fresh, schema-complete db and copies only the kept syncs into it (reuses CloneSync's column-explicit ATTACH copy).
  3. Repoints the C1File at the compact db and drops the old file.

Cost scales with the kept set (small), not the deleted set, and the result is compact in one step — no VACUUM.

Why (vs #911)

This is the other half of the cleanup investigation. From the autoresearch benchmark (1M-grant old generation + 100k keep):

approach delete time final file
DELETE + VACUUM (current default) 18.5s 54 MB
chunked DELETE (PR #911) 12.4s 616 MB (still needs VACUUM)
rebuild (this PR) 1.7s 56 MB, one step

The two are independent and not mutually exclusive: #911 can land for everyone while this rolls out behind the flag.

Risk / review focus

  • The repoint (c.rawDb.Close() → open fresh handle on the new path → swap c.db) is the sensitive part — blast radius is the c1z save path. Please scrutinize the handle/lifecycle swap and the WAL/file cleanup of the old db.
  • Defensive guard: never rebuilds to an empty keep-set.
  • Copy drops the id column (fresh rowids) exactly like the existing CloneSync; external_id/sync_id are the stable keys.

Test

  • TestCleanupByRebuild: records each sync's grant count before cleanup, then asserts only the policy-kept syncs survive, their grant counts are byte-identical afterward (no data loss), and the repointed db passes PRAGMA integrity_check.
  • Full pkg/dotc1z suite passes; gofmt + vet clean.

Not yet wired into the platform caller; that's a follow-up once a strategy is chosen.

🤖 Generated with Claude Code

Comment thread pkg/dotc1z/sync_runs.go Outdated
Comment on lines +1029 to +1031
for _, suffix := range []string{"", "-wal", "-shm"} {
_ = os.Remove(oldPath + suffix)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: After removing the old db files, the parent directory is leaked as an empty dir. The rest of the codebase uses cleanupDbDir (which calls os.RemoveAll(filepath.Dir(...))) to clean up temp directories holding db files. Consider adding _ = os.Remove(filepath.Dir(oldPath)) after the file removals, or reusing cleanupDbDir, to avoid accumulating empty temp dirs across rebuilds.

@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

General PR Review: feat(dotc1z): opt-in rebuild cleanup strategy (clone keep-set, repoint)

Blocking Issues: 0 | Suggestions: 0 | Threads Resolved: 0
Review mode: full
View review run

Review Summary

Reviewed the full PR: a new opt-in cleanupByRebuild strategy that copies kept syncs into a fresh database and repoints the C1File, replacing the default delete+vacuum path. The implementation correctly reuses existing patterns (cloneTableColumns, quoteIdentifier, allTableDescriptors), has solid defensive guards (empty keep-set bail-out, deferred cleanup on failure, nil-out of db handles on error), and proper lifecycle management of the old and new database paths. Tests cover both the main rebuild path (with data integrity and round-trip verification) and the no-op early-return path. No security or correctness issues found.

Security Issues

None found.

Correctness Issues

None found.

Suggestions

None.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

@arreyder
arreyder marked this pull request as ready for review June 3, 2026 05:02
@arreyder
arreyder requested a review from a team June 3, 2026 05:02
Comment thread pkg/dotc1z/sync_runs.go Outdated

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

arreyder and others added 4 commits June 5, 2026 08:35
Alternative to per-table DELETE + VACUUM cleanup. When WithCleanupRebuild
(or BATON_CLEANUP_REBUILD) is set, Cleanup copies the kept syncs into a
fresh database (reusing CloneSync's column-explicit ATTACH copy) and
repoints the C1File at it, then drops the old file. Cost scales with the
kept set instead of the deleted set, and the result is compact in one step
with no VACUUM.

Benchmarked ~11x faster than DELETE+VACUUM on a 1M-grant fixture (1.7s vs
18.5s) and self-compacting. Default cleanup path is unchanged; this is
opt-in. Keep selection reuses SelectSyncsToDelete (same policy).

Test: TestCleanupByRebuild proves survivors' data is byte-identical and the
repointed db passes integrity_check; full pkg/dotc1z suite passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rip test

- Repoint now calls c.init() on the new handle so session pragmas
  (locking_mode=EXCLUSIVE, synchronous, journal_mode, caller pragmas) match a
  normally-opened C1File — previously the bare sql.Open handle had none, which
  could break finalize's WAL checkpoint under WAL journal mode.
- On close/open failure during repoint, nil out rawDb/db so a later Close()
  takes the safe nil path instead of double-closing / removing backing data.
  Set rebuilt before init so a pragma failure can't delete the adopted db.
- Nil initFile handles after close (matches CloneSync).
- Doc the keep-set copy as an O(syncLimit) transaction (scaling caveat).
- Test: reopen the c1z from disk after Close to prove the repointed db
  round-trips through saveC1z; add no-op (nothing-to-delete) path test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…alidated cols)

go-lint flagged G201 (SQL string formatting) on the per-table rebuild INSERT.
Table names come from the hardcoded allTableDescriptors and columns are
validated by cloneTableColumns/validateColumnName — same justification and
//nolint:gosec directive already used in c1file_attached.go.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The repoint removed the old db/-wal/-shm files but left the parent temp dir
as an empty leak. Reuse cleanupDbDir (os.RemoveAll on filepath.Dir), matching
finalize's cleanup, so rebuilds don't accumulate empty temp dirs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@arreyder
arreyder force-pushed the arreyder/cleanup-rebuild-alt branch from dacfb05 to cc02918 Compare June 5, 2026 13:38

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking issues found.

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