Skip to content

feat: add opt-in runtime BTree segment selection - #98

Merged
yanghua merged 2 commits into
lance-format:mainfrom
Gabriel39:dev/runtime-scalar-index-selection
Oct 26, 2026
Merged

yanghua merged 2 commits into
lance-format:mainfrom
Gabriel39:dev/runtime-scalar-index-selection

Conversation

@Gabriel39

@Gabriel39 Gabriel39 commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Add opt-in runtime BTree segment selection for ordinary, explicitly fragment-scoped scans. Callers currently choose a physical scalar segment before execution, even when another segment covering the same read domain has a narrower driver predicate.

lance_scanner_set_scalar_index_auto_selection and Scanner::scalar_index_auto_selection compare the current snapshot's BTree page min/max and NULL-count metadata. Estimated work is matching pages times rows per page. A strictly cheaper eligible segment can replace the caller's default; a broad estimate can choose a scoped non-indexed scan. The complete filter, fragment domain, deletion handling, and LIMIT/OFFSET are preserved. No candidate row-ID sets are materialized just to estimate costs.

The option defaults to false. Unknown estimates preserve the selected segment, and Bitmap/LabelList behavior remains unchanged. Candidate count, serialized lookup bytes, decoded lookup buffers, page count, IN values, and estimation work are bounded. Lookup batches share the bounded Session metadata cache. Statistics callbacks report the compared candidates, changed selections, estimated pages/rows, and scan fallbacks.

Decoded lookup buffers have a separate aggregate 8 MiB allowance, checked from fixed-width schema and row count before materialization and charged on both cold loads and cache hits. The current IndexReader API cannot bound decoded variable-width values, so string/binary costs remain unknown: retain such a default or skip such an alternative before opening its lookup for estimation. These indices remain usable through the existing caller-selected path.

The implementation uses existing pinned Lance APIs and does not change dependency revisions or the on-disk format. README documents the API, model limitations, and fallback behavior.

Validation

  • cargo test --offline --locked: 501 tests passed, with the three C/C++ compilation tests intentionally ignored by the normal test command.
  • Includes all 381 C API tests and 72 unit tests. The compressed-string regression was first observed failing on the original implementation, then passing with the fix. It uses 1,024 BTree pages with repeated 64 KiB strings whose serialized lookup fits the I/O budget but whose decoded extrema would occupy 128 MiB, and verifies retaining the integer driver without caching the string probe.
  • Added decoded-budget coverage for cold loads, cumulative cache-hit accounting, wide fixed-width fields, unknown variable-width sizes, and integer overflow.
  • Rustfmt and Clippy with --all-targets -- -D warnings.
  • Runtime integration tests compare two competing BTree segments and verify the disabled option, broad-filter scan fallback, fixed fragment scope, empty results, and LIMIT/OFFSET.
  • On the initial feature commit (b0a79c5), all three C/C++ compilation/execution tests and RUSTDOCFLAGS="-D warnings" cargo doc --offline --locked --no-deps passed. The decoded-memory follow-up changes private Rust logic/tests and README only; upstream CI reruns the full matrix.

lance-gatekeeper[bot]

This comment was marked as outdated.

@lance-gatekeeper lance-gatekeeper Bot added the K-approved Latest Gatekeeper recommendation permits acceptance. label Oct 26, 2026
@lance-gatekeeper lance-gatekeeper Bot removed the K-approved Latest Gatekeeper recommendation permits acceptance. label Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

Follow-up safety fix in c6cbde1: serialized file size and cache admission do not bound transient decoded lookup memory. Probes now preflight a separate cumulative 8 MiB decoded-buffer allowance before materialization, and cache hits consume the same allowance.

The current IndexReader API cannot provide a safe decoded-size bound for variable-width extrema. Those probes now return unknown before opening the lookup: retain a string/binary default, or skip that alternative. The existing caller-selected index path remains available.

Added a compressed long-string regression (1,024 pages, repeated 64 KiB strings, 128 MiB decoded extrema despite a lookup below the serialized limit), an integer-driver alternative case, cold/cache-hit budget checks, wide fixed-width bounds, and overflow coverage. The original implementation failed the regression; the fix passes all 501 Rust tests, Rustfmt, and Clippy with warnings denied. README and this PR description document the limitation. CI is rerunning.

@lance-gatekeeper lance-gatekeeper 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.

✅ Gate recommendation: approve.

Verified the decoded-memory follow-up: fixed-width lookup buffers are checked before decoding, and cached lookups consume the same cumulative allowance. String/binary estimates remain unknown; caller-selected execution remains available.

The opt-in selector reduces unnecessary candidate work while preserving fragment scope, the complete filter, deletion handling, and LIMIT/OFFSET. The new memory regressions and focused scalar tests pass.

@lance-gatekeeper lance-gatekeeper Bot added the K-approved Latest Gatekeeper recommendation permits acceptance. label Oct 26, 2026

@yanghua yanghua 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.

+1

@yanghua
yanghua merged commit a51ab47 into lance-format:main Oct 26, 2026
10 checks passed
Gabriel39 added a commit to Gabriel39/incubator-doris that referenced this pull request Oct 26, 2026
### What problem does this PR solve?

Lance profiles do not expose Session cache-miss observation windows, scalar-index task grouping cannot compare runtime costs, and Foyer memory capacity is tied to the data-cache read block size.

This PR targets **branch-4.2** and adds:

- `LanceSessionIndexCacheMisses` and `LanceSessionMetadataCacheMisses`, with availability and scope information. They report BE-wide Session deltas observed during a reader's dataset-open windows, including opening. Concurrent readers can overlap; these are not query-exclusive counts and must not be summed as an exact query total.
- The startup BE option `enable_lance_scalar_index_auto_selection` (default `true`). FE keeps coverage-based task grouping and does not estimate selectivity or add a session variable. In BE, lance-c compares current BTree page min/max and null-count metadata for segments covering the entire task. Estimated work is matching pages times rows per page; a broad estimate can select a scoped full scan. Serialized lookup bytes, decoded buffers and probe work are bounded. Unknown estimates retain the existing segment path. The current native API cannot bound variable-width expansion, so string/binary costs remain unknown: retain such a grouping index or skip such an alternative before opening its lookup. Bitmap/LabelList remain supported on their existing path and are not cost-ranked; vector/FTS semantics are unchanged.
- `lance_data_cache_memory_capacity_bytes`. Default `0` preserves the previous one-configured-block memory tier, including nondefault block sizes. Explicit capacities configure Foyer independently of block and disk capacities; negative values are rejected before unsigned conversion.

The native selector and decoded-memory fix are now merged upstream in [lance-format/lance-c#98](lance-format/lance-c#98). This PR pins upstream main commit `a51ab47a9c814a746cb1714c99fad93dd3a188b8`, refreshes the source archive checksum, and removes the local runtime-selection patch together with its application/checksum handling. The pre-existing Foyer patch is unchanged and applies with zero fuzz.

Runtime decisions are exposed through Profile counters. Usage, cost-model limits and cache-counter scope are documented in `docs/lance-cache-and-index-selection.md`.

### Release note

Add Lance Session cache-miss diagnostics, bounded BE runtime BTree selection controlled by a BE configuration, and independently configurable Foyer memory capacity.

### Validation

- On the newly downloaded upstream archive plus the unchanged Foyer patch, `cargo test --offline --locked` passed all 515 tests, including 82 unit tests and 385 C API tests. The normal command ignores three C/C++ executable compilation tests. Coverage includes the compressed long-string allocation regression, decoded-budget limits, competing indices, scan fallback, fragment scope, NULL/deletion/stable-ID behavior and LIMIT/OFFSET.
- C and C++ API sources passed syntax-only compilation against the newly patched headers.
- Verified archive MD5, clean zero-fuzz patch application, cached no-op, stale-checksum source replacement, and equality of the resulting tested source. Selector/scanner/segment sources match merged upstream exactly.
- All nine affected C++ files passed the repository formatting runner with clang-format 16.0.6. Shell syntax and staged whitespace checks passed.
- Existing FE validation passed 74 focused tests and FE Checkstyle; this dependency synchronization changes no FE code.
- Full Doris BE build and unit-suite validation remain for CI and have not been verified locally.

### Check List (For Author)

- Test
    - [x] Unit Test
    - [x] Manual test: native source/patch lifecycle and header compatibility checks above.
    - [ ] Regression test
- Behavior changed:
    - [x] Yes. BE runtime BTree selection is enabled by default and can be disabled. Foyer's default memory behavior is preserved.
- Does this need documentation?
    - [x] Yes. Usage and native memory limits are documented.

### Check List (For Reviewer who merge this PR)

- [ ] Confirm the release note
- [ ] Confirm test cases
- [ ] Confirm document
- [ ] Add branch pick label
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

K-approved Latest Gatekeeper recommendation permits acceptance.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants