Skip to content

feat(compaction): compact fragments over an overlay-count limit#7772

Open
wjones127 wants to merge 3 commits into
will/oss-1324-take-can-read-overlaysfrom
will/oss-1326-min-overlay-compaction
Open

feat(compaction): compact fragments over an overlay-count limit#7772
wjones127 wants to merge 3 commits into
will/oss-1324-take-can-read-overlaysfrom
will/oss-1326-min-overlay-compaction

Conversation

@wjones127

@wjones127 wjones127 commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

A minimal, first slice of overlay-aware compaction (OSS-1326), stacked on #7536 (OSS-1324, will/oss-1324-take-can-read-overlays) — review against that base.

What it does

Adds a max_overlays_per_fragment: Option<usize> option to CompactionOptions. When a fragment carries more than this many data overlay files, the planner marks it CompactItself, so compact_files fully rewrites it into a fresh fragment with its overlays and deletions materialized into the base data. A fragment at or below the limit is not a candidate on this basis.

The read side needs no new machinery: compaction already scans each fragment through the normal read path, which (per #7536) resolves overlays and applies deletions, so the merged post-image is what gets written to the new base file.

Index correctness

A full rewrite removes the overlays, and with them the staleness signal the query path uses to mask an index older than an overlay. So the Rewrite commit now drops the rewritten fragment from the coverage of any index left stale by those overlays — field-aware (only indices covering a field an overlay actually supplied) and version-gated (overlay.committed_version > index.dataset_version). Those rows fall back to a flat scan until reindex; an index is never left serving stale values. Non-stale indices (built at/after the overlay, or on un-overlaid fields) are remapped normally.

This reuses the existing Operation::Rewrite path — no new persisted operation or conflict-matrix change.

Scope / follow-ups

Only the "fully compact the fragment" strategy is implemented. Overlay→overlay merge, in-place overlay→base folds (column rewrite preserving row addresses), and rebuild-in-place index reconciliation are left as follow-ups.

Tests

  • Planner/e2e (optimize.rs): overlay count over the limit triggers a full compaction (fresh single-file fragment, overlays cleared, values materialized); at-or-below limit is a no-op; deletions are materialized alongside overlays; a stale scalar index is dropped from the compacted fragment's coverage and the indexed query stays correct.
  • Unit (transaction.rs): prune_overlay_stale_fields_from_indices is field-aware and version-gated (stale index dropped; index at/after the overlay kept; index on an un-overlaid field untouched).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added an optional setting to compact fragments when they accumulate too many data overlays.
    • Overlay-triggered compaction now materializes updates and deletions into fresh, streamlined data.
  • Bug Fixes

    • Improved query accuracy after compaction by preventing stale indexes from returning outdated values.
    • Ensured compacted fragments are correctly removed from outdated index coverage while preserving valid indexes.

Add a `max_overlays_per_fragment` option to `CompactionOptions`. When a
fragment carries more than this many data overlay files, it is fully
compacted into a fresh fragment with its overlays (and deletions)
materialized into the base data.

Because the rewrite removes the overlays that mask stale index values at
query time, the `Rewrite` commit drops the compacted fragment from the
coverage of any index left stale by those overlays (field-aware,
version-gated via `committed_version > index.dataset_version`), so an
index is never left serving stale values; the rows fall back to a flat
scan until reindex.

Overlay->overlay merge and in-place folds are left as follow-ups.

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

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (1)
  • main

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b6b50c7c-9e63-4ed4-8f3b-6a910d3338b9

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch will/oss-1326-min-overlay-compaction

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the enhancement New feature or request label Jul 14, 2026
Comment thread rust/lance/src/dataset/optimize.rs Outdated
@wjones127 wjones127 marked this pull request as ready for review July 14, 2026 18:48

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@rust/lance/src/dataset/optimize.rs`:
- Around line 8074-8106: Update the id_val_map helper to use
scanner.try_into_batch() instead of try_into_stream().try_collect(), then
process the returned single RecordBatch directly while preserving the existing
id-to-value mapping behavior.
- Around line 7946-7969: Update the test helper create_base_dataset to construct
the batch with arrow_array’s record_batch! macro, replacing the manual
ArrowSchema, Arc, and RecordBatch::try_new setup. Preserve the existing id and
val columns and values, then continue using the resulting batch with
RecordBatchIterator and Dataset::write unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 4b224d4d-3ae2-4788-a9e7-86c9335b97f8

📥 Commits

Reviewing files that changed from the base of the PR and between a1bce9a and e78fadf.

📒 Files selected for processing (2)
  • rust/lance/src/dataset/optimize.rs
  • rust/lance/src/dataset/transaction.rs

Comment thread rust/lance/src/dataset/optimize.rs
Comment thread rust/lance/src/dataset/optimize.rs
…dule

Address review feedback: move the overlay-compaction tests out of a separate
`overlay_compaction_tests` module into the existing `tests` module, and use the
`record_batch!` macro and `try_into_batch()` test idioms.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@wjones127 wjones127 requested a review from westonpace July 14, 2026 19:50
Enable the overlay-count compaction trigger by default so fragments that
accumulate more than 10 data overlay files are folded back into base data
without an explicit opt-in. Set to None to disable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant