Repository navigation
feat: add opt-in runtime BTree segment selection - #98
Conversation
|
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. |
There was a problem hiding this comment.
✅ 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.
### 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
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_selectionandScanner::scalar_index_auto_selectioncompare 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.--all-targets -- -D warnings.b0a79c5), all three C/C++ compilation/execution tests andRUSTDOCFLAGS="-D warnings" cargo doc --offline --locked --no-depspassed. The decoded-memory follow-up changes private Rust logic/tests and README only; upstream CI reruns the full matrix.