feat: expose staged index segment transactions#3
Draft
ragnorc wants to merge 428 commits into
Draft
Conversation
ragnorc
force-pushed
the
ragnorc/two-phase-index-segments
branch
from
June 23, 2026 16:31
53aa593 to
5cfc088
Compare
|
Important This PR touches the Lance format specification. Substantive changes to the format specification — the If this is a meaningful format change:
|
…pdate (lance-format#7410) With stable row ids, a full-row `merge_insert` moves updated rows to a new fragment while preserving their row ids. The RewriteRows index maintenance (`register_pure_rewrite_rows_update_frags_in_indices`) then extends a scalar index's fragment bitmap to the new fragment unless the index covers a modified field. `merge_insert` built that modified-field set from the top-level `schema().fields`, so a **nested**-field index's leaf id was absent. The index was therefore wrongly extended over the rewritten fragment, which was then treated as indexed and never re-scanned — so updated rows were silently dropped from queries on that index (false negatives). The fix builds the set with `fields_pre_order()` so nested leaf field ids are included. Flat schemas are unaffected (`fields_pre_order()` equals the top-level list when there are no children). Adds two regression tests in `dataset_merge_update.rs`: a flat-index control and a nested-field guard that asserts the rewritten fragment is still data-scanned (via the `fragments_scanned` metric) so the updated value is found.
…e-format#7415) This PR picks up on the work in lance-format#7403 by @dantasse. `create_branch` on managed tables calls `create_table_version` before `_refs/branches/<branch>.json` exists, and `resolve_branch_location` requires that ref. Bootstrap commit fails. `create_table_version` now goes through `resolve_branch_for_commit` instead check the ref first, and if it's missing only allow the commit when the branch chain has no versions yet. Versions without a ref still get rejected (zombie). `create_branch("exp")` used to fail on the first commit because `create_table_version` looked for `_refs/branches/exp.json` before phase 2 wrote it. ``` # branching isn't atomic so: phase 1: manifest → create_table_version (ref doesn't exist yet ← was failing here) phase 2: write _refs/branches/exp.json ``` ### Testing - Added `test_create_branch_on_managed_dataset_succeeds`. - Zombie case is already in `test_branch_ops_reject_zombie_branch`. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
…at#7311) ## Summary Adds a generic `SpillStore` — reclaimable RAII scratch storage for intermediate state that overflows memory (e.g. index-build posting lists, shuffle runs, BTree pages). Mechanism only; consumer migration (IVF shuffler) is a follow-up. - `SpillStore` / `SpillFile` (lance-io): `create_spill_file()` vends a write-once RAII handle whose `writer()` / `reader()` hand back `Box<dyn Writer>` / `Box<dyn Reader>`, so callers feed spill files straight into `FileWriter::try_new` and a v2 `FileReader` without leaking an `ObjectStore` + path. Dropping the handle deletes the file and releases its bytes back to the store's budget. - `LocalSpillStore`: writes to an OS temp directory; `with_cap` enforces an optional byte budget shared across all handles, returning a typed `Error::DiskCapExceeded` instead of silently filling the disk. Enforcement lives entirely in the spill store — the spill file decorates the writer with a `QuotaWriter` (reserve-on-write, release-on-drop-by-stat) rather than threading a field through `ObjectStore` and every provider, so it works for any backend the store opens. - `From<io::Error>` recovers a wrapped lance `Error`, so typed errors such as `DiskCapExceeded` survive the `AsyncWrite` boundary. - `ScanScheduler::open_reader` builds a `FileScheduler` over an already-open `Reader` (no path/size lookup), bridging a bare spill reader into the v2 reader path. - `Session` gains a `spill_store` field (default: uncapped `LocalSpillStore`), a `with_spill_store()` builder for injection, and a `spill_store()` accessor. Closes lance-format#7300 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… LanceFileSession (lance-format#7426) ## Summary Adds three primitives to `LanceFileSession` (`lance.file`), each a binding-exposure of an existing `object_store` capability (Rust → PyO3 → Python). They let callers route listing, deletion, and ranged reads through the dataset's `object_store` session instead of PyArrow's `AzureFileSystem`. ## APIs added ### 1. `list_with_delimiter(path=None) -> ListResult` Non-recursive, path-segment-delimited list of a single directory level. Unlike the existing recursive `list()`, it returns only the immediate children of `path`: child "directories" as `common_prefixes` and direct child files as `objects` (a `ListResult` dataclass), all session-relative. ```python result = session.list_with_delimiter("subdir") result.common_prefixes # immediate child "directories" result.objects # immediate child files ``` - `lance-io`: add `ObjectStore::list_with_delimiter(prefix) -> Result<ListResult>`. ### 2. `delete_file(path) -> None` Delete an object by session-relative path. Deleting a missing path is a **no-op (idempotent)** rather than an error, so it is safe on best-effort cleanup paths. ```python session.delete_file("subdir/checkpoint.lance") # removes it session.delete_file("already_gone.lance") # no-op, no error ``` ### 3. `read_range(path, offset, length) -> bytes` Single ranged read of a byte slice from a file. Reading a missing object raises `OSError`, consistent with `download_file`. ```python data = session.read_range("data/file-0.lance", offset=1024, length=256) # -> bytes ``` ## Why this matters These remove the remaining PyArrow `AzureFileSystem` / `filesystem_from_uri` dependencies from geneva's checkpoint and range-blob paths: - The recursive-only `list()` forced the flat-namespace bf-dir enumeration fallback to scan the whole checkpoint subtree; `list_with_delimiter` makes that fallback bounded again. - `LanceFileSession` had no delete, so checkpoint purge fell back to PyArrow and silently became a no-op on flat / unreachable-DFS accounts; `delete_file` makes purge functional and blob-only. - The range-blob reader's last PyArrow handle is replaced by `read_range`. ## Testing - `test_session_list_with_delimiter` — non-recursive semantics (base level returns immediate files + child dirs but not their contents; subdir descends one level; trailing-slash equivalence; empty for a missing prefix). - `test_session_delete_file` — top-level delete, nested delete, idempotent no-op on missing. - `test_session_read_range` — mid-file range, start-of-file, up-to-end, zero-length read, and `OSError` on a missing object. - All existing session tests pass. - `cargo fmt`; `cargo clippy -- -D warnings` clean (default features). 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…at#7405) It was timing out on every merge to main. Move it from python.yml to the nightly workflow with a 360-minute timeout; it now builds the reader wheel in-job since needs: can't span workflows. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e-format#7429) A fragment's RowIdSequence can hold segments whose ranges overlap but whose selected ids are disjoint -- compaction concatenates the surviving and rewritten halves of an updated fragment this way. From applied each segment's bitmap/hole removals to one shared map, so a later segment's removals cleared ids an earlier overlapping segment owned, dropping live rows from the result. Build each segment in isolation and union it in. Surfaced via COUNT(*) pushdown over stable row ids returning 0 after update + compact + filter. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
DataFusion, `object_store`, and `prost-derive` already resolved to itertools 0.14, while our workspace pin held a second 0.13 copy in the dependency graph. Bumping the pin to 0.14 collapses the duplicate; only `criterion` retains 0.13, and that is dev/bench-only so it does not affect shipped crates. No source changes were needed — we use no items removed between 0.13 and 0.14. Verified with `cargo check --workspace --tests --benches` and `cargo clippy --all --tests --benches -- -D warnings`. --- Context: this came out of a dependency-graph audit. A `rand` 0.9 → 0.10 bump was also considered, but dropped — `rand` 0.9 is held alive by DataFusion (and goosefs-sdk/hf-hub/jsonb), so bumping our code to 0.10 would not eliminate the duplicate, only move which copy our code uses. Worth revisiting once DataFusion moves to `rand` 0.10. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
## Summary Adds row-level **DELETE** to the in-memory WAL (`mem_wal`), expressed as **tombstone (sentinel) rows**: a delete appends the primary key plus `_tombstone = true` (null elsewhere). A tombstone is the newest value for its PK — it wins newest-per-PK resolution (suppressing the older real row) and is then silently dropped from query results. This is the lance (OSS) half of the WAL delete feature. The sophon half (compaction hard-delete + serving) consumes this API separately. ## What's here - **`_tombstone` column** — lance-owned, non-nullable `Boolean` appended to the memtable/generation schema only (never the base table). `ShardWriter::open` extends the caller's base schema internally; callers keep passing the base schema and never name the column. - **Write path** — `ShardWriter::put` injects `_tombstone = false`; new **`ShardWriter::delete(keys)`** builds the tombstone batch from a key-only input. Legacy WAL entries (written before this change) are back-filled on replay. - **HNSW null-vector fix** — mem_wal's HNSW rejected null vectors outright, so it couldn't handle a nullable vector column at all (a pre-existing bug, independent of deletes). `append_batch` now skips null rows like the base-table index, compacting survivors while preserving their original offsets in `row_ids` so search still resolves to the right row. - **Read paths** - *Filtered read*: fold `NOT _tombstone` into the predicate per WAL arm (only when the source carries the column), so it runs before the per-source pushdown limit and post-dedup in the active arm. - *Point lookup*: carry `_tombstone` through and filter **after** `CoalesceFirstExec` (per-source filtering would let the coalesce fall through and resurrect the row); the plan-free BTree fast path short-circuits a tombstone hit to not-found. - *Vector / FTS*: no filter needed — a null-payload tombstone is never an index candidate; the deleted real row is suppressed by the existing block-list (cross-gen) / `NewestPkFilter` (within active). ## Notable design points - Fold/carry `_tombstone` **only when the source schema has the column** — back-compat for legacy generations and existing tests for free. - `_tombstone` is non-nullable so the point-lookup base arm can synthesize a matching `Literal(false)` for `CoalesceFirstExec`'s exact-schema check. - Delete requires non-PK columns to be nullable (a tombstone nulls them); surfaced as a clear error otherwise. - Caveat documented in code: a delete-heavy burst between compactions can under-deliver a `LIMIT`/top-k query (uncompensated block-list drops) — recall degradation, not wrong content; self-limiting as compaction drains tombstones. ## Testing 19 new tests; full `mem_wal` suite green (417 passed, 0 failed). `cargo fmt` + `cargo clippy -p lance --tests` clean. Coverage: filtered-read masking + filter + limit, point-lookup resurrection guards (fast + plan paths), delete-then-reinsert, delete-of-absent, HNSW null-vector handling (mid-batch + all-null), write-path injection/round-trip and validation. Draft — opening for early review. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nce-format#7433) lance-format#7373 made a DataReplacement conflict with any concurrent Update/Delete that touched a target fragment. That is too conservative: a delete that only adds a deletion vector leaves the positional column file aligned, and build_manifest rebases the replacement onto the current fragment, preserving the deletion vector. A DataReplacement vs a concurrent Delete/Update now conflicts only when: - the target fragment is removed outright -- returns a non-retryable IncompatibleTransaction so the caller can drop it and commit the rest; - a row-rewriting (RewriteRows) update moves the matched rows out to new fragments the positional file does not cover; or - the update rewrote one of the replaced fields in place (fields_modified). Deletion-vector-only deletes/updates and non-overlapping column rewrites are now allowed.
## Performance Improvement ### What is the performance issue or bottleneck? Recent FTS changes added AND/fuzzy bookkeeping to shared OR query paths. Ordinary non-fuzzy OR searches were still allocating and checking grouped expansion state, maintaining matched-position tracking in posting-list loading, and recording AND-only WAND metrics even though those values are only meaningful for conjunction/grouped expansion paths. ### How does this PR improve performance? This PR keeps ordinary OR on the lightweight path: - only allocates and updates required/matched position tracking for AND or phrase queries - skips grouped-position `HashSet` creation and per-frequency grouped checks unless grouped fuzzy expansions are present - records AND-only WAND metrics only for `Operator::And` Grouped fuzzy AND rescoring remains on its existing grouped path so matched-expansion scoring stays correct. ### Benchmark or measurement results No benchmark was run in this PR. The change is intentionally scoped to hot-path bookkeeping removal; benchmark validation can run later on the existing benchmark branch/VM if needed. ## Bug Fix ### What is the bug? The ordinary OR path was paying for bookkeeping intended for AND/fuzzy/grouped expansion correctness. ### What issues or incorrect behavior does the bug cause? It caused avoidable overhead in OR query execution and showed up as a small regression in OR benchmark configurations after the recent FTS changes. ### How does this PR fix the problem? The OR path now bypasses the AND/fuzzy-only bookkeeping while preserving the grouped expansion branch used by fuzzy AND. ## Validation - `cargo fmt --all` - `cargo fmt --all --check` - `git diff --check` - `CARGO_TARGET_DIR=/tmp/lance-target-fts-or-hotpath cargo clippy -p lance-index --tests -- -D warnings` - `CARGO_TARGET_DIR=/tmp/lance-target-fts-or-hotpath cargo test -p lance-index scalar::inverted::wand::tests -- --nocapture` - `CARGO_TARGET_DIR=/tmp/lance-target-fts-or-hotpath cargo test -p lance-index scalar::inverted::index::tests::test_bm25_search_uses_global_idf -- --nocapture` - `CARGO_TARGET_DIR=/tmp/lance-target-fts-or-hotpath cargo test -p lance-index scalar::inverted::index::tests::test_and_query_returns_empty_when_exact_term_missing -- --nocapture` - `CARGO_TARGET_DIR=/tmp/lance-target-fts-or-hotpath cargo test -p lance-index scalar::inverted::index::tests::test_fuzzy_and -- --nocapture`
…ce-format#7375) ## Summary This PR adds a lightweight file metadata index and a data-reader path that can load column metadata on demand for supported narrow projections. Dataset fragment reads now use this path for sparse top-level V2.1+ physical column projections and continue to fall back to full metadata when the projection is unsupported or the caller needs file-wide metadata. The problem is that the current full-metadata path decodes every column's metadata before reading data. On ultra-wide files, a one-column scan or point lookup pays metadata cost proportional to the total column count. ## Performance Benchmarked on EC2 instance `xuanwo-lance-lazy-metadata-bench` with a 65,000-column Lance file and random single-column point lookups. The full-metadata reader is the `origin/main` behavior. Results below are mean latency. | Storage | origin/main full metadata cold | this PR lazy metadata cold | Speedup | Metadata read bytes | IOPS | | --- | ---: | ---: | ---: | ---: | ---: | | Local EBS | 159.1 ms | 19.3 ms | 8.24x | 10.388 MiB -> 0.998 MiB | 4 -> 5 | | S3 us-east-2 | 368.4 ms | 144.4 ms | 2.55x | 10.388 MiB -> 1.057 MiB | 4 -> 5 | Hot reopen reads are effectively unchanged because metadata/data page reads are already cached: Local EBS was 10.4 ms -> 9.6 ms, and S3 was 36.3 ms -> 35.7 ms. The S3 cold path now issues only one additional request versus full metadata, not two, because dataset-known schema and row count are passed into the metadata-index reader instead of re-reading the schema global buffer. ## Validation Validated with targeted lazy metadata tests, the dropped-column statistics regression, `cargo fmt --all`, and full Rust clippy with tests and benches enabled.
## Summary Add Java and PyLance APIs for cleanup explain. - Add PyLance `Dataset.explain_cleanup_old_versions(...)` with cleanup explanation result types - Export PyLance cleanup explanation types from the top-level `lance` package - Add Java `dataset.cleanup(policy).explain()` / `.execute()` operation API - Keep Java `cleanupWithPolicy(policy)` as a compatibility wrapper for execute - Add JNI conversions and tests for cleanup explanations ## Testing - `cargo fmt --all` - `cargo fmt --manifest-path ./java/lance-jni/Cargo.toml --all` - `cargo check --manifest-path python/Cargo.toml` - `cargo check --manifest-path ./java/lance-jni/Cargo.toml` - `./mvnw compile` - `./mvnw -Dtest=CleanupTest test` - `uv run --frozen --python 3.12 pytest python/tests/ test_dataset.py::test_explain_cleanup_old_versions`
Pins goosefs-sdk to 0.1.5 because 0.1.6 fails to compile on Linux due to
a missing std::ffi::CString import. This pin can be removed once
upstream publishes a fixed release.
```
error[E0433]: cannot find type `CString` in this scope
--> /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/goosefs-sdk-0.1.6/src/cache/store/uring/store.rs:314:23
|
314 | let cstring = CString::new(path)
| ^^^^^^^ use of undeclared type `CString`
```
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Updated GooseFS integration to use a compatible SDK version, improving
reliability for supported storage operations.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: zhangyue19921010 <zhangyue.1010@bytedance.com>
…nce-format#7624) Built on the MAXSCORE work merged in lance-format#7603; this is the next PR in the Lucene-parity series. This PR now includes the 2/3-clause SIMD and frequency-bound work previously stacked as lance-format#7625. ## Performance issue Top-k AND and phrase queries previously leapfrogged doc-at-a-time through boxed `PostingIterator::next` calls. Phrase checks additionally decoded a whole 256-doc position block per candidate and allocated cursor vectors per candidate. ## What changed - Add a bulk conjunction path for compressed top-k AND and phrase queries. It keeps the classic block-max window pruning semantics while intersecting decompressed posting slices in batches. - Specialize the 2- and 3-clause merge kernels. x86_64 uses runtime-dispatched AVX2 catch-up scans, with scalar kernels on other CPUs. - Add a frequency-bucketed score-bound LUT so lead docs that cannot beat the threshold are rejected before follower advances. - Keep a generic merge kernel for 4+ clauses when bulk mode is explicitly forced. - Score candidates in two passes so document-length loads are issued back-to-back. - Decode only the PackedDelta position groups needed by the current phrase candidate instead of the whole position block. - Use an allocation-free exact-phrase check and recycled owned position buffers. - Return contextual errors for malformed packed-position data instead of panicking. ## Runtime selection `LANCE_FTS_BULK_AND` accepts: - `auto` (default): use bulk for 2/3 effective posting clauses and classic for all other widths. - `on` or legacy `1`: force bulk for every eligible compressed AND/phrase query; 4+ clauses use the generic kernel. - `off` or legacy `0`: always use the classic conjunction loop. The clause count is measured after tokenization, deduplication, and expansion. Invalid values warn and fall back to `auto`. The 4/5-clause experiments did not show a consistent specialized-kernel win, and generic bulk regressed against classic in several tiers. Therefore no 4/5-clause specialized kernels are retained and `auto` remains limited to 2/3 clauses. ## Measured performance Per-branch-tip wheels, 1000 queries × 8 concurrent. AND used the warm, fully prewarmed 200M-doc V3/256 index; phrase used the 50M-doc V3/256 positions index. | query | previous stack base | combined PR | |---|---:|---:| | AND 3w k10 @200M | 70 qps | **172 qps (2.46×)** | | AND 3w k100 @200M | 33 qps | **86 qps (2.61×)** | | phrase 3w k10 @50m | 19 qps | **35 qps (1.84×)** | | phrase 2w k10 @50m | 59 qps | **102 qps (1.73×)** | The AWS 4/5-clause follow-up used an `r7i.24xlarge`, the 200M English-only MMLB dataset, a V3/256 index with 338 partitions, k=100, c32, a 600 GB cache, and synchronous full prewarm. ## Compatibility This is a query-time implementation change. It does not change the FTS index format or index version. ## Verification - Bulk/classic/auto parity and dispatch coverage for 2 through 6 clauses, including nonzero phrase slop. - PackedDelta per-doc seek parity across group boundaries and tails. - PackedDelta and VarintDocDelta cursor-recycling coverage. - Malformed packed-position data returns a recoverable search error. - Final WAND suite: 80 passed. - `cargo test -p lance-index`, `cargo clippy --all --tests --benches -- -D warnings`, and `cargo fmt --all -- --check` passed across the reviewed stack. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Performance Improvements** * Added a faster bulk AND/phrase execution path for compressed postings with block-level skipping and slice intersection; availability controlled via `LANCE_FTS_BULK_AND` (auto by default). * Improved phrase verification to decode only required positions per matching document. * Optimized packed-delta position seeking using cached group state and efficient tail handling. * **Bug Fixes** * Ensured bulk AND/phrase results match classic behavior, including filtering and pruning semantics. * Improved rejection of malformed packed-position data. * **Tests** * Added seek accuracy coverage across group boundaries and tail cases, plus malformed-group rejection and cursor independence checks. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Yang Cen <yang@lancedb.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
## Summary
Creates a new `lance-index-core` crate containing the abstract trait
layer for scalar indices. The goal is to allow index plugin authors to
implement `ScalarIndex` without depending on the full `lance-index`
crate (~17 dependencies vs 75+).
### What moved
The following types were extracted from `lance-index` into
`lance-index-core` and re-exported from their original locations for
backward compatibility:
- **Traits**: `Index`, `IndexParams`, `ScalarIndex`, `AnyQuery`,
`IndexStore`, `IndexReader`, `IndexWriter`, `RowIdRemapper`,
`MetricsCollector`
- **Supporting types**: `IndexType`, `SearchResult`, `CreatedIndex`,
`UpdateCriteria`, `OldIndexDataFilter`, `ScalarIndexParams`,
`BuiltinIndexType`, `TrainingCriteria`, `TrainingOrdering`, `IndexFile`
The concrete query type implementations (`SargableQuery`,
`LabelListQuery`, `TextQuery`, `BloomFilterQuery`,
`FullTextSearchQuery`, `TokenQuery`, `GeoQuery`) stay in `lance-index`.
### Dependency graph
```
lance-index-core (new, ~17 deps — traits only)
↑
lance-index (concrete impls, re-exports everything for compat)
↑
lance (unchanged relationship)
lance-table (no new dependency on lance-index-core)
```
---
## Non-trivial changes
### `TrainingCriteria`/`TrainingOrdering` lifted out of the plugin
registry
These two types were defined in `lance-index::scalar::registry`
alongside the plugin infrastructure. Because `UpdateCriteria` — a field
on the `ScalarIndex` trait — references `TrainingCriteria`, both types
need to live in `lance-index-core`. They are re-exported from their
original path for backward compatibility:
```rust
// lance-index/src/scalar/registry.rs
pub use crate::scalar::{TrainingCriteria, TrainingOrdering};
```
### Newtype wrappers for `IndexReader` in `lance_format.rs`
`IndexReader` was previously implemented directly on
`lance_file::previous::reader::FileReader` and
`lance_file::reader::FileReader`. With `IndexReader` now in
`lance-index-core`, both the trait and the target types are foreign to
`lance-index`, violating the orphan rule. Two private newtype wrappers
resolve this:
```rust
struct PreviousIndexReader(PreviousFileReader);
struct CurrentIndexReader(CurrentFileReader);
```
These implement `IndexReader` locally and are boxed as `Arc<dyn
IndexReader>` before being returned from `open_index_file`. No public
API changed.
The existing blanket `impl IndexWriter for PreviousFileWriter<M>` was
also removed for the same reason (foreign trait over a foreign generic
type). The one call site that used it was updated to call
`PreviousFileWriter::write` and `finish_with_metadata` directly.
### Newtype wrappers for system-index types (`FragReuseIndex`,
`MemWalIndex`)
`FragReuseIndex` and `MemWalIndex` are defined in `lance-table`.
Previously, `lance-index` implemented `Index` and `RowIdRemapper`
directly on those types — valid because both trait and impl were in
`lance-index`. With the traits now in `lance-index-core`, the orphan
rule requires the impl to live in the trait crate or the type crate. To
avoid adding a `lance-table → lance-index-core` dependency,
`lance-index` instead defines two newtype wrappers:
```rust
// lance-index/src/frag_reuse.rs
pub struct FragReuseIndexHandle(pub Arc<FragReuseIndex>);
impl Index for FragReuseIndexHandle { ... }
impl RowIdRemapper for FragReuseIndexHandle { ... }
// lance-index/src/mem_wal.rs
pub struct MemWalIndexHandle(pub Arc<MemWalIndex>);
impl Index for MemWalIndexHandle { ... }
```
Call sites in `lance` that previously cast `Arc<FragReuseIndex>`
directly to `Arc<dyn Index>` now wrap it in the handle first.
`lance-table` retains zero knowledge of `lance-index-core`.
### Dual `IndexFile` types with explicit conversion functions
`IndexFile` (`path: String`, `size_bytes: u64`) exists in both
`lance-index-core::scalar` (for plugin use) and `lance-table::format`
(for protobuf serialization and manifest tracking). The two are kept as
independent structs — a `From` impl is not possible due to the orphan
rule, and having `lance-table` re-export from `lance-index-core` would
introduce the unwanted dependency. Two free functions in `lance-index`
bridge them at the boundary:
```rust
pub fn index_files_to_table(files: Vec<lance_index_core::scalar::IndexFile>)
-> Vec<lance_table::format::IndexFile>
pub fn table_files_to_index(files: Vec<lance_table::format::IndexFile>)
-> Vec<lance_index_core::scalar::IndexFile>
```
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added a shared core API for scalar/vector index implementations,
including unified index typing, search results, storage interfaces, and
row-id remapping.
* Introduced a standardized metrics collection interface for
indexing/query operations.
* **Bug Fixes**
* Improved index build/optimization write paths and related test write
flows for more reliable batch persistence.
* Normalized index file metadata handling across creation, merging,
remapping, and manifest generation.
* **Refactor**
* Consolidated public index and metrics APIs via core re-exports, using
handle wrappers for better consistency.
* **Tests**
* Updated and expanded index-creation/optimization tests to align with
the revised persistence and wrapping behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
## Summary - fail RLE miniblock encoding when chunk selection cannot advance instead of returning partial buffers with the original value count - assert that raw encoded run lengths match each declared chunk count across U8, U16, and U32 run-length widths - retain the exact-prefix implementation introduced in lance-format#7376; this does not change the file format ## Context `LANCE_MINIBLOCK_MAX_VALUES=1` is currently accepted, but a multi-value RLE page cannot represent a non-final one-value chunk because `log_num_values == 0` identifies the final chunk. The encoder previously broke out of the loop and returned incomplete data while retaining the original `num_values`. ## Test plan - [x] `cargo test -p lance-encoding` (430 passed, 5 ignored) - [x] `cargo fmt --all -- --check` - [x] `cargo clippy --all --tests --benches -- -D warnings` Made with [Cursor](https://cursor.com) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Prevented incomplete or invalid compressed data from being returned when encoding cannot make progress. * Improved error reporting with contextual details for encoding failures. * Fixed run-length handling across the 2,048-value boundary. * **Tests** * Added coverage for multiple run-length widths and zero-progress encoding scenarios. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Cursor <cursoragent@cursor.com>
) ## What & why The write-path reserved-column-name guard only rejected three of the five system column names (`_rowid`, `_rowaddr`, `_rowoffset`), missing `_row_created_at_version` and `_row_last_updated_at_version`. Those two were introduced by the row-tracking feature and the guard was never extended. System columns are virtual — they are injected into scan results at read time and never stored in the physical data. `Projection::to_schema` appends `_rowid`, `_rowaddr`, and the two row-version columns to a projection, and the scanner's `filterable_schema` sets all of those flags on every filtered scan. So a user data column literally named `_row_created_at_version` passed ingest, then collided with the appended system field the next time the dataset was scanned with a filter, hitting `to_schema`'s `extend(...).unwrap()` and panicking. Rare (the column name has to match exactly) but reachable on well-formed, in-memory data — not a corrupt-file case. ## How & why it is correct Replace the hardcoded three-name check with `is_system_column`, which covers all five names. This rejects the collision at the write boundary with a clear error instead of letting it surface as a later panic, so `to_schema`'s existing contract genuinely holds. `is_system_column` is a strict superset of the old three names, so the previously-rejected names still error exactly as before. ## Compatibility No behavior change for legitimate writes. The row-version columns are virtual and never part of a stored schema, so no valid write payload contains them; the only schema that reaches this guard is the user-provided data schema in `InsertBuilder`. Internal writers (merge-insert, update) build their schemas from `dataset.schema()` and bypass this guard entirely. The only newly-rejected input is a user explicitly naming a physical column after a system column — which is precisely the case that used to panic on read. ## Test plan - New parameterized test `rejects_reserved_system_column_names` covering all five system column names; the two version-column cases fail against the old three-name guard and pass with the fix. - `cargo test -p lance --lib`, `cargo clippy -p lance --tests -- -D warnings`, `cargo fmt --all -- --check` are green. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Dataset ingestion now rejects user-provided field names that conflict with reserved system columns. * Validation covers the complete set of system column names, including row-version fields. * Added coverage to ensure conflicting field names consistently fail ingestion. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
`geodatafusion 0.5.0` supports DataFusion 54, which was the last remaining blocker. This bumps DataFusion 53 → 54, and transitively `geodatafusion` 0.4 → 0.5, `sqlparser` 0.61 → 0.62, and `substrait` 0.62 → 0.63. Arrow stays at 58. ### Migration for DataFusion 54 breaking changes - `as_any` was removed from the `ExecutionPlan`, `PhysicalExpr`, `ScalarUDFImpl`, `TableProvider`, `SchemaProvider`, `CatalogProvider`, and `CatalogProviderList` traits. The impls are dropped and downcasting now uses the inherent `dyn Trait::downcast_ref`/`is` (e.g. `plan.downcast_ref::<T>()` instead of `plan.as_any().downcast_ref::<T>()`). - `ExecutionPlan::partition_statistics` now returns `Arc<Statistics>`. - `Expr::Cast`/`TryCast` hold a `FieldRef` instead of a `DataType`. - `AnalyzeExec::new` gained a `metric_categories` argument and `MetricType::SUMMARY` was renamed to `MetricType::Summary`. - sqlparser 0.62 wraps the LIKE escape character in `ValueWithSpan`. - substrait 0.63 adds `RexType::Lambda`/`LambdaInvocation` variants and replaces extension URIs with URNs. - Migrated the deprecated `SimplifyContext::default()` builder to `SimplifyContext::builder().build()`. `create_aggregate_expr_and_maybe_filter` is retained under `#[allow(deprecated)]`; migrating to `LoweredAggregateBuilder` is left as follow-up. ### Verification - `cargo clippy --all --tests --benches -- -D warnings` clean, plus `-p lance-datafusion --features substrait`. - `lance-datafusion` substrait tests pass (exercising the URN and `RexType` changes). - Python (`pylance`) and Java (`lance-jni`) bindings compile; all three lockfiles refreshed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added support for DataFusion 54 and GeoDataFusion 0.5. * Enabled higher-order functions in SQL planning. * **Bug Fixes** * Improved compatibility with updated DataFusion casting and execution-plan/statistics behavior. * Updated Substrait conversion to use the current extension identifier format. * Added validation to reject unsupported lambda expressions in filter conditions. * **Tests** * Refreshed planning/explain and count-pushdown assertions to match the latest behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nce-format#7536) > **Supersedes lance-format#7409.** Relocated into `lance-format/lance` and **properly stacked on the OSS-1322 branch** so the diff is now exactly this PR's change (no spec/1322/1323 commits to scroll past). Resolves the `take` random-access path against data overlay files, replacing the temporary "overlays not supported" error from OSS-1322. Implements OSS-1324. Because `take` and scan share `FragmentReader`'s read path, the merge is wired there once: each row is addressed by its physical offset (from `ReadBatchParams::to_offsets_total`) and resolved against the overlays that cover its field. This also enables the scan-path merge that the OSS-1322 PR stubbed out. ### How it works - **Lazy, rank-pushed reads.** Each contributing overlay file is opened once (projected to the covered ∩ requested fields) and **no value bytes are read up front**. Requested offsets are routed to the newest covering overlay at their coverage rank, and only the touched ranks are fetched — via the existing `take` primitive — so overlay reads are O(requested rows), not O(coverage). - **Concurrent IO.** Base and overlay reads are issued together, then the column is assembled with `interleave` (rank → fetch-position remap). - The merge runs on physical rows in read order, **before** deletion filtering, so: - deletions take precedence (an overlay value computed for a deleted row is dropped with the row), - NULL overrides apply (a covered offset with a NULL value resolves to NULL, distinct from fall-through), - fields resolve independently. - Sparse per-field overlays read each field's value column independently, so unequal-length value columns (OSS-1323) need no rectangular batch. Rank-based addressing only (rank on the coverage bitmap + a value fetch; no offset key column, no binary search). Overlays on nested (non-top-level) fields are not yet matched and are left for follow-up. There's a `TODO(overlay perf)` on reader priority to settle with a benchmark. ### Tests take covered/uncovered offsets; multiple overlays (newest wins); per-field coverage with unequal-length columns; NULL override; overlay on a deleted row (inert); multi-fragment scan — each over v2.0 and v2.1. Plus unit tests for the routing/assembly core, and an IO guard (`test_take_reads_only_needed_overlay_ranks`) asserting a 2-row take over a fully-covering 100k-row overlay reads ~one miniblock, not the whole value column. ### Stacking Stacked on the **OSS-1322** branch (`will/oss-1322-write-data-overlay-and-scan-data`). Merge that first; this PR's base retargets to `main` automatically when it lands. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added support for reading dataset fragments that include overlay files. * Overlay values are resolved and merged into results during both scans and targeted row reads, honoring newest-wins semantics across nested fields. * Optimizes reads by fetching only overlay data needed for the requested rows and projections. * **Bug Fixes** * Fragments with overlays are no longer rejected, and overlay resolution remains correct when overlays overlap deleted rows. * **Tests** * Added end-to-end overlay read coverage for scan/take, including multi-batch slicing and edge cases. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Unreleased version after creating v9.0.0-rc.1
Wrap the `MatchQuery` query argument in brackets so metric suffixes do not look like part of the query term. Example verbose metrics output changes from: ```text MatchQuery: column=content_text, query=government: elapsed=342ms ``` to: ```text MatchQuery: column=content_text, query=[government]: elapsed=342ms ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Style** * Updated Full Text Search (FTS) execution-plan display for `MatchQuery` to show the query value in square brackets (e.g., `query=[...]`) for the default/verbose formats. * **Tests** * Updated FTS-related execution-plan expectation strings and snapshots to match the new `MatchQuery` bracketed formatting across legacy and non-legacy scenarios. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
…back (lance-format#7807) ## Summary When no preset base scorer is injected, the FTS exec nodes (`MatchQueryExec` / `BooleanQueryExec` / `PhraseQueryExec`) build one via `build_global_bm25_scorer` over every segment they search — per-segment corpus stats plus per-term doc-frequency reads. That cost was folded invisibly into the node's `elapsed`, which makes it impossible to tell "search was slow" from "scorer construction was slow" in EXPLAIN ANALYZE. Adds a `scorer_build_ms` gauge to `FtsIndexMetrics` timing exactly that fallback at all three call sites. It reads 0 when a caller supplies a preset scorer (e.g. a distributed engine shipping corpus-wide stats via `with_base_scorer`), so the gauge also doubles as a visible marker of which scoring path a node took. ## Test plan - `cargo check -p lance` / `cargo fmt` clean - Verified end-to-end in a distributed setup (100M-row dataset, 4 index segments over 4 workers): with no preset scorer each worker's `MatchQuery` line reports its own `scorer_build_ms` (non-zero on cold caches, ~0 warm); with a preset scorer the gauge stays 0 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added performance metrics to track the time required to build BM25 scorers during full-text search. * **Monitoring** * Search execution metrics now report scorer construction duration when a fallback scorer is created. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…e-format#7772) A minimal, first slice of overlay-aware compaction (OSS-1326), stacked on lance-format#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 lance-format#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](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a configurable overlay-count trigger for compaction; fragments exceeding the limit are compacted into standalone form. * Default limit is 10 overlays per fragment, configurable via `lance.compaction.max_overlays_per_fragment` (set to `none` to disable). * **Bug Fixes** * Prevented stale index coverage from returning outdated values after overlay/materialization compaction. * Improved accuracy by pruning rewritten fragment coverage from older index versions, causing queries to fall back to scans where needed. * **Tests** * Added coverage for the overlay trigger and index-staleness pruning behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nce-format#7816) ## Summary Reading a segment's tokenizer configuration previously required a full `InvertedIndex` open — partition construction, token dictionaries loaded into memory, and an index-cache entry — even when the caller only needs the params, e.g. to tokenize query text identically to the index or to inspect how an index was built. Measured on one segment of a large text dataset: **7.7 MB resident and a full open, for a few hundred bytes of config**. The manifest's `InvertedIndexDetails` is not a substitute: it is a lossy copy of the params that cannot carry `custom_stop_words` (unbounded user data that doesn't belong in the manifest) nor the json doc type, so a tokenizer rebuilt from it can silently diverge from the index's real tokenizer. This PR adds a params-only read path: - `InvertedIndex::load_params(store)` — reads the params JSON from the metadata file's schema metadata; falls back to the legacy tokens-file location, mirroring `load_legacy_index`. No partitions, no dictionaries, nothing cached. - `load_segment_params(dataset, segment)` — dataset-level helper via `LanceIndexStore::from_dataset_for_existing`, re-exported from `index::scalar`. The returned params are the complete serialized struct, byte-faithful — `custom_stop_words` and `lance_tokenizer` included. The metadata file is read through the session file-metadata cache the store wires up, so repeated calls are memory hits and a later full open of the same segment reuses the read. Measured (one segment, local FS, cold process): | | latency | resident | |---|---|---| | `load_segment_params` | 0.76 ms | 293 B | | full index open | 17.7 ms (with the file cache already warm) | 7.68 MB pinned in the index cache | ## Test plan - New test `test_load_segment_params_full_fidelity`: builds an FTS index with `custom_stop_words` (exactly the field `InvertedIndexDetails` cannot carry) and asserts `load_segment_params` equals the fully opened segment's `params()` field-for-field - `cargo check -p lance -p lance-index` / fmt clean 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Exposes the `DataOverlay` commit operation to Python so overlays can be created and committed from Python (needed to benchmark and use data overlay files without dropping into Rust). Mirrors the existing `DataReplacement` binding. ## What's here - `LanceOperation.DataOverlay` with `DataOverlayFile` and `DataOverlayGroup`. A `DataOverlayFile` carries the value `DataFile` plus **exactly one** of `shared_offsets` (dense coverage shared by every field) or `field_offsets` (sparse, one offset set per field). The commit stamps `committed_version`; passing both/neither coverage is rejected with a clear error. - PyO3 conversions (both directions) in `python/src/transaction.rs`. - Fills in the `overlays` field on the Python `FragmentMetadata -> Fragment` conversion, which OSS-1322 left unset (Python metadata doesn't carry overlays — they're committed via `DataOverlay`). ## Tests `test_data_overlay_*` in `test_dataset.py`: dense round-trip resolves on read; newest overlay wins; sparse per-field coverage resolves fields independently; and the exactly-one-coverage validation rejects both/neither. ## Stacking Stacked on **lance-format#7536** (OSS-1324 read path) — base retargets automatically when it lands. Next PR (benchmarks) stacks on this. > Note: the `python/src/fragment.rs` one-liner arguably belongs in the OSS-1322 PR (lance-format#7535), which added `Fragment.overlays` without updating the binding; included here so the stack compiles. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added cell-level `DataOverlay` operations to append fragment overlays without rewriting entire fragments. - Supports dense and sparse overlays, with newest overlapping values taking precedence. - Persists overlay metadata on fragments and carries optional committed version stamps. - **Bug Fixes** - Preserves overlay metadata correctly through JSON serialization and Python↔Rust round-trips. - **Tests** - Added end-to-end coverage for dense/sparse overlays, precedence behavior, metadata round-tripping, and offset validation. - **Documentation/Chores** - Updated fragment metadata `repr` expectations and improved default `max_bytes_per_file` for fragment writing. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ance-format#7815) Bumps the cargo group with 9 updates in the / directory: | Package | From | To | | --- | --- | --- | | [bytemuck](https://github.com/Lokathor/bytemuck) | `1.25.0` | `1.25.1` | | [clap](https://github.com/clap-rs/clap) | `4.6.1` | `4.6.2` | | [rand](https://github.com/rust-random/rand) | `0.9.4` | `0.10.1` | | [uuid](https://github.com/uuid-rs/uuid) | `1.23.4` | `1.24.0` | | [xxhash-rust](https://github.com/DoumanAsh/xxhash-rust) | `0.8.16` | `0.8.17` | | [cc](https://github.com/rust-lang/cc-rs) | `1.2.66` | `1.2.67` | | [sha2](https://github.com/RustCrypto/hashes) | `0.10.9` | `0.11.0` | | [hmac](https://github.com/RustCrypto/MACs) | `0.12.1` | `0.13.0` | | [syn](https://github.com/dtolnay/syn) | `2.0.118` | `2.0.119` | Updates `bytemuck` from 1.25.0 to 1.25.1 <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/Lokathor/bytemuck/blob/main/changelog.md">bytemuck's changelog</a>.</em></p> <blockquote> <h2>1.25.1</h2> <ul> <li><a href="https://redirect.github.com/Lokathor/bytemuck/pull/348">Don't impl core::error::Error on spirv</a>, which was causing a build error on that target.</li> <li>Added a reminder on <code>try_cast_slice</code> that empty slices must still be aligned.</li> </ul> <h2>1.25</h2> <ul> <li><a href="https://redirect.github.com/Lokathor/bytemuck/pull/333">Remove extern "stdcall" fn ptr impls on non-x86-32 windows.</a></li> <li><a href="https://redirect.github.com/Lokathor/bytemuck/pull/344">Fix nightly_portable_simd after LaneCount removal.</a></li> </ul> <h2>1.24</h2> <ul> <li><a href="https://redirect.github.com/Lokathor/bytemuck/pull/322">use new stable avx512 types from rust 1.89</a></li> <li><a href="https://redirect.github.com/Lokathor/bytemuck/pull/317">impl AnyBitPattern for [MaybeUninit<T: AnyBitPattern>; N]</a></li> <li>bump <code>derive</code> minimum version.</li> </ul> <h2>1.23.2</h2> <ul> <li>bump <code>derive</code> minimum version.</li> </ul> <h2>1.23.1</h2> <ul> <li>Added a windows-only <code>ZeroableInOption</code> impl for "stdcall" functions.</li> </ul> <h2>1.23</h2> <ul> <li><code>impl_core_error</code> crate feature adds <code>core::error::Error</code> impl.</li> <li>More <code>ZeroableInOption</code> impls.</li> </ul> <h2>1.22</h2> <ul> <li>Add the <code>pod_saturating</code> feature, which adds <code>Pod</code> impls for <code>Saturating<T></code> when <code>T</code> is already <code>Pod</code>.</li> <li>A bump in the minimum <code>bytemuck_derive</code> dependency from 1.4.0 to 1.4.1 to avoid a bug if you have a truly ancient <code>cargo.lock</code> file sitting around.</li> <li>Adds <code>Send</code> and <code>Sync</code> impls to <code>BoxBytes</code>.</li> </ul> <h2>1.21</h2> <ul> <li>Implement <code>Pod</code> and <code>Zeroable</code> for <code>core::arch::{x86, x86_64}::__m512</code>, <code>__m512d</code> and <code>__m512i</code> without nightly. Requires Rust 1.72, and is gated through the <code>avx512_simd</code> cargo feature.</li> <li>Allow the use of <code>must_cast_mut</code> and <code>must_cast_slice_mut</code> in const contexts. Requires Rust 1.83, and is gated through the <code>must_cast_extra</code> cargo feature.</li> <li>internal: introduced the <code>maybe_const_fn</code> macro that allows defining some function to be const depending upon some <code>cfg</code> predicate.</li> </ul> <h2>1.20</h2> <ul> <li>New functions to allocate zeroed <code>Arc</code> and <code>Rc</code>. Requires Rust 1.82</li> <li><code>TransparentWrapper</code> impls for <code>core::cmp::Reverse</code> and <code>core::num::Saturating</code>.</li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/Lokathor/bytemuck/commit/cabc8e75899e67ad274959b6de27653558d01296"><code>cabc8e7</code></a> chore: Release bytemuck version 1.25.1</li> <li><a href="https://github.com/Lokathor/bytemuck/commit/2d4d8ca457a552bd9310a5a10327151a705b9f0b"><code>2d4d8ca</code></a> changelog</li> <li><a href="https://github.com/Lokathor/bytemuck/commit/946e7a905f7651b1dfb568d5045b8bcff1b56f26"><code>946e7a9</code></a> chore: Release bytemuck_derive version 1.11.0</li> <li><a href="https://github.com/Lokathor/bytemuck/commit/8a8f7cf4c9988a668336a792c78bbbb0a52ed8ef"><code>8a8f7cf</code></a> changelog derive</li> <li><a href="https://github.com/Lokathor/bytemuck/commit/ee6742e68cdb2a7058196a1cf13834ffbdac40d3"><code>ee6742e</code></a> changelog</li> <li><a href="https://github.com/Lokathor/bytemuck/commit/e2d1c7f2e9797d4b2f3de5a16b6a70879e53a8d8"><code>e2d1c7f</code></a> Don't impl core::error::Error on spirv (<a href="https://redirect.github.com/Lokathor/bytemuck/issues/348">#348</a>)</li> <li><a href="https://github.com/Lokathor/bytemuck/commit/7dd71742f67630949e5e9a21bb74469a69c1bce2"><code>7dd7174</code></a> make the note more terse</li> <li><a href="https://github.com/Lokathor/bytemuck/commit/24b1b71c599eb1e2b70b6059b851d4c264991e71"><code>24b1b71</code></a> Update Rust version in CI workflow to 1.71.0</li> <li><a href="https://github.com/Lokathor/bytemuck/commit/f0dfc1bc6ef0de704ffd0b64231f9c3565d65ac6"><code>f0dfc1b</code></a> docs: note that an empty slice must still satisfy target alignment in cast_sl...</li> <li>See full diff in <a href="https://github.com/Lokathor/bytemuck/compare/v1.25.0...v1.25.1">compare view</a></li> </ul> </details> <br /> Updates `clap` from 4.6.1 to 4.6.2 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/clap-rs/clap/releases">clap's releases</a>.</em></p> <blockquote> <h2>v4.6.2</h2> <h2>[4.6.2] - 2026-07-15</h2> <h3>Fixes</h3> <ul> <li><em>(help)</em> Say <code>alias</code> when there is only one</li> </ul> </blockquote> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/clap-rs/clap/blob/master/CHANGELOG.md">clap's changelog</a>.</em></p> <blockquote> <h2>[4.6.2] - 2026-07-15</h2> <h3>Fixes</h3> <ul> <li><em>(help)</em> Say <code>alias</code> when there is only one</li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/clap-rs/clap/commit/0fe0be302726f4253b9bee27eed48438c92917aa"><code>0fe0be3</code></a> chore: Release</li> <li><a href="https://github.com/clap-rs/clap/commit/480af9d045453f4ab96d9bdd4d4b9f5aab3c272f"><code>480af9d</code></a> docs: Update changelog</li> <li><a href="https://github.com/clap-rs/clap/commit/2b3ddd0294a147d1eda917cb303243bcde0c12ee"><code>2b3ddd0</code></a> Merge pull request <a href="https://redirect.github.com/clap-rs/clap/issues/6340">#6340</a> from liskin/fix-completion-escape</li> <li><a href="https://github.com/clap-rs/clap/commit/7ffe7399ff032cc247eb0449cf8fcdfbfe55a4ec"><code>7ffe739</code></a> fix(complete): Do not suggest options after "--"</li> <li><a href="https://github.com/clap-rs/clap/commit/d47fc4f8a5e9fcc16d0cae15b51e6eb1a8ed5832"><code>d47fc4f</code></a> test(complete): Options suggested after escape (<code>--</code>)</li> <li>See full diff in <a href="https://github.com/clap-rs/clap/compare/clap_complete-v4.6.1...clap_complete-v4.6.2">compare view</a></li> </ul> </details> <br /> Updates `rand` from 0.9.4 to 0.10.1 <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/rust-random/rand/blob/master/CHANGELOG.md">rand's changelog</a>.</em></p> <blockquote> <h2>[0.10.1] — 2026-02-11</h2> <p>This release includes a fix for a soundness bug; see <a href="https://redirect.github.com/rust-random/rand/issues/1763">#1763</a>.</p> <h3>Changes</h3> <ul> <li>Document panic behavior of <code>make_rng</code> and add <code>#[track_caller]</code> (<a href="https://redirect.github.com/rust-random/rand/issues/1761">#1761</a>)</li> <li>Deprecate feature <code>log</code> (<a href="https://redirect.github.com/rust-random/rand/issues/1763">#1763</a>)</li> </ul> <p><a href="https://redirect.github.com/rust-random/rand/issues/1761">#1761</a>: <a href="https://redirect.github.com/rust-random/rand/pull/1761">rust-random/rand#1761</a> <a href="https://redirect.github.com/rust-random/rand/issues/1763">#1763</a>: <a href="https://redirect.github.com/rust-random/rand/pull/1763">rust-random/rand#1763</a></p> <h2>[0.10.0] - 2026-02-08</h2> <h3>Changes</h3> <ul> <li>The dependency on <code>rand_chacha</code> has been replaced with a dependency on <code>chacha20</code>. This changes the implementation behind <code>StdRng</code>, but the output remains the same. There may be some API breakage when using the ChaCha-types directly as these are now the ones in <code>chacha20</code> instead of <code>rand_chacha</code> (<a href="https://redirect.github.com/rust-random/rand/issues/1642">#1642</a>).</li> <li>Rename fns <code>IndexedRandom::choose_multiple</code> -> <code>sample</code>, <code>choose_multiple_array</code> -> <code>sample_array</code>, <code>choose_multiple_weighted</code> -> <code>sample_weighted</code>, struct <code>SliceChooseIter</code> -> <code>IndexedSamples</code> and fns <code>IteratorRandom::choose_multiple</code> -> <code>sample</code>, <code>choose_multiple_fill</code> -> <code>sample_fill</code> (<a href="https://redirect.github.com/rust-random/rand/issues/1632">#1632</a>)</li> <li>Use Edition 2024 and MSRV 1.85 (<a href="https://redirect.github.com/rust-random/rand/issues/1653">#1653</a>)</li> <li>Let <code>Fill</code> be implemented for element types, not sliceable types (<a href="https://redirect.github.com/rust-random/rand/issues/1652">#1652</a>)</li> <li>Fix <code>OsError::raw_os_error</code> on UEFI targets by returning <code>Option<usize></code> (<a href="https://redirect.github.com/rust-random/rand/issues/1665">#1665</a>)</li> <li>Replace fn <code>TryRngCore::read_adapter(..) -> RngReadAdapter</code> with simpler struct <code>RngReader</code> (<a href="https://redirect.github.com/rust-random/rand/issues/1669">#1669</a>)</li> <li>Remove fns <code>SeedableRng::from_os_rng</code>, <code>try_from_os_rng</code> (<a href="https://redirect.github.com/rust-random/rand/issues/1674">#1674</a>)</li> <li>Remove <code>Clone</code> support for <code>StdRng</code>, <code>ReseedingRng</code> (<a href="https://redirect.github.com/rust-random/rand/issues/1677">#1677</a>)</li> <li>Use <code>postcard</code> instead of <code>bincode</code> to test the serde feature (<a href="https://redirect.github.com/rust-random/rand/issues/1693">#1693</a>)</li> <li>Avoid excessive allocation in <code>IteratorRandom::sample</code> when <code>amount</code> is much larger than iterator size (<a href="https://redirect.github.com/rust-random/rand/issues/1695">#1695</a>)</li> <li>Rename <code>os_rng</code> -> <code>sys_rng</code>, <code>OsRng</code> -> <code>SysRng</code>, <code>OsError</code> -> <code>SysError</code> (<a href="https://redirect.github.com/rust-random/rand/issues/1697">#1697</a>)</li> <li>Rename <code>Rng</code> -> <code>RngExt</code> as upstream <code>rand_core</code> has renamed <code>RngCore</code> -> <code>Rng</code> (<a href="https://redirect.github.com/rust-random/rand/issues/1717">#1717</a>)</li> </ul> <h3>Additions</h3> <ul> <li>Add fns <code>IndexedRandom::choose_iter</code>, <code>choose_weighted_iter</code> (<a href="https://redirect.github.com/rust-random/rand/issues/1632">#1632</a>)</li> <li>Pub export <code>Xoshiro128PlusPlus</code>, <code>Xoshiro256PlusPlus</code> prngs (<a href="https://redirect.github.com/rust-random/rand/issues/1649">#1649</a>)</li> <li>Pub export <code>ChaCha8Rng</code>, <code>ChaCha12Rng</code>, <code>ChaCha20Rng</code> behind <code>chacha</code> feature (<a href="https://redirect.github.com/rust-random/rand/issues/1659">#1659</a>)</li> <li>Fn <code>rand::make_rng() -> R where R: SeedableRng</code> (<a href="https://redirect.github.com/rust-random/rand/issues/1734">#1734</a>)</li> </ul> <h3>Removals</h3> <ul> <li>Removed <code>ReseedingRng</code> (<a href="https://redirect.github.com/rust-random/rand/issues/1722">#1722</a>)</li> <li>Removed unused feature "nightly" (<a href="https://redirect.github.com/rust-random/rand/issues/1732">#1732</a>)</li> <li>Removed feature <code>small_rng</code> (<a href="https://redirect.github.com/rust-random/rand/issues/1732">#1732</a>)</li> </ul> <p><a href="https://redirect.github.com/rust-random/rand/issues/1632">#1632</a>: <a href="https://redirect.github.com/rust-random/rand/pull/1632">rust-random/rand#1632</a> <a href="https://redirect.github.com/rust-random/rand/issues/1642">#1642</a>: <a href="https://redirect.github.com/rust-random/rand/pull/1642">rust-random/rand#1642</a> <a href="https://redirect.github.com/rust-random/rand/issues/1649">#1649</a>: <a href="https://redirect.github.com/rust-random/rand/pull/1649">rust-random/rand#1649</a> <a href="https://redirect.github.com/rust-random/rand/issues/1652">#1652</a>: <a href="https://redirect.github.com/rust-random/rand/pull/1652">rust-random/rand#1652</a> <a href="https://redirect.github.com/rust-random/rand/issues/1653">#1653</a>: <a href="https://redirect.github.com/rust-random/rand/pull/1653">rust-random/rand#1653</a> <a href="https://redirect.github.com/rust-random/rand/issues/1659">#1659</a>: <a href="https://redirect.github.com/rust-random/rand/pull/1659">rust-random/rand#1659</a> <a href="https://redirect.github.com/rust-random/rand/issues/1665">#1665</a>: <a href="https://redirect.github.com/rust-random/rand/pull/1665">rust-random/rand#1665</a> <a href="https://redirect.github.com/rust-random/rand/issues/1669">#1669</a>: <a href="https://redirect.github.com/rust-random/rand/pull/1669">rust-random/rand#1669</a> <a href="https://redirect.github.com/rust-random/rand/issues/1674">#1674</a>: <a href="https://redirect.github.com/rust-random/rand/pull/1674">rust-random/rand#1674</a> <a href="https://redirect.github.com/rust-random/rand/issues/1677">#1677</a>: <a href="https://redirect.github.com/rust-random/rand/pull/1677">rust-random/rand#1677</a> <a href="https://redirect.github.com/rust-random/rand/issues/1693">#1693</a>: <a href="https://redirect.github.com/rust-random/rand/pull/1693">rust-random/rand#1693</a> <a href="https://redirect.github.com/rust-random/rand/issues/1695">#1695</a>: <a href="https://redirect.github.com/rust-random/rand/pull/1695">rust-random/rand#1695</a> <a href="https://redirect.github.com/rust-random/rand/issues/1697">#1697</a>: <a href="https://redirect.github.com/rust-random/rand/pull/1697">rust-random/rand#1697</a></p> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/rust-random/rand/commit/27ff4cb7ced3122a1f677fc248c1a07e59ddc8cd"><code>27ff4cb</code></a> Prepare v0.10.1: deprecate feature <code>log</code> (<a href="https://redirect.github.com/rust-random/rand/issues/1763">#1763</a>)</li> <li><a href="https://github.com/rust-random/rand/commit/98d06386dc4e1d1c89a91f4e483d571921c29ecf"><code>98d0638</code></a> make_rng: document panic and add #[track_caller] (<a href="https://redirect.github.com/rust-random/rand/issues/1761">#1761</a>)</li> <li><a href="https://github.com/rust-random/rand/commit/54e5eaaa7ac11af3aa60b5ccc486182189e6f9ef"><code>54e5eaa</code></a> Fix doc error (<a href="https://redirect.github.com/rust-random/rand/issues/1758">#1758</a>)</li> <li><a href="https://github.com/rust-random/rand/commit/1ce4c080186730595a8d464591d17aac22a42252"><code>1ce4c08</code></a> Bump itoa from 1.0.17 to 1.0.18 in the all-deps group (<a href="https://redirect.github.com/rust-random/rand/issues/1756">#1756</a>)</li> <li><a href="https://github.com/rust-random/rand/commit/ccb734b9c22891a19f11be125c2f09a43809b08e"><code>ccb734b</code></a> docs: fix typo in doc comment (<a href="https://redirect.github.com/rust-random/rand/issues/1754">#1754</a>)</li> <li><a href="https://github.com/rust-random/rand/commit/357eb7de9c9c80184449e8b515c821e48cf4df74"><code>357eb7d</code></a> Bump libc from 0.2.182 to 0.2.183 in the all-deps group (<a href="https://redirect.github.com/rust-random/rand/issues/1753">#1753</a>)</li> <li><a href="https://github.com/rust-random/rand/commit/5e77fe5d61b886988cae67b6d8fb09e405845c63"><code>5e77fe5</code></a> Fix trait references in documentation (<a href="https://redirect.github.com/rust-random/rand/issues/1752">#1752</a>)</li> <li><a href="https://github.com/rust-random/rand/commit/da891850ab2b38f4322ec140ae29d305dfb162c3"><code>da89185</code></a> Bump the all-deps group with 3 updates (<a href="https://redirect.github.com/rust-random/rand/issues/1751">#1751</a>)</li> <li><a href="https://github.com/rust-random/rand/commit/50516ff45c3675d9c2d247e70bc8db691ed8366d"><code>50516ff</code></a> Bump the all-deps group with 2 updates (<a href="https://redirect.github.com/rust-random/rand/issues/1749">#1749</a>)</li> <li><a href="https://github.com/rust-random/rand/commit/fd71de97fdc7050b9a2d8384f5f8afce7d991ca3"><code>fd71de9</code></a> Bump the all-deps group with 2 updates (<a href="https://redirect.github.com/rust-random/rand/issues/1747">#1747</a>)</li> <li>Additional commits viewable in <a href="https://github.com/rust-random/rand/compare/0.9.4...0.10.1">compare view</a></li> </ul> </details> <br /> Updates `uuid` from 1.23.4 to 1.24.0 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/uuid-rs/uuid/releases">uuid's releases</a>.</em></p> <blockquote> <h2>v1.24.0</h2> <h2>What's Changed</h2> <ul> <li>feat(fmt): support encoding into MaybeUninit buffers by <a href="https://github.com/weifanglab"><code>@weifanglab</code></a> in <a href="https://redirect.github.com/uuid-rs/uuid/pull/892">uuid-rs/uuid#892</a></li> <li>Prepare for 1.24.0 release by <a href="https://github.com/KodrAus"><code>@KodrAus</code></a> in <a href="https://redirect.github.com/uuid-rs/uuid/pull/896">uuid-rs/uuid#896</a></li> </ul> <h2>New Contributors</h2> <ul> <li><a href="https://github.com/weifanglab"><code>@weifanglab</code></a> made their first contribution in <a href="https://redirect.github.com/uuid-rs/uuid/pull/892">uuid-rs/uuid#892</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/uuid-rs/uuid/compare/v1.23.5...v1.24.0">https://github.com/uuid-rs/uuid/compare/v1.23.5...v1.24.0</a></p> <h2>v1.23.5</h2> <h2>What's Changed</h2> <ul> <li>doc: Fix broken link by <a href="https://github.com/frostyplanet"><code>@frostyplanet</code></a> in <a href="https://redirect.github.com/uuid-rs/uuid/pull/891">uuid-rs/uuid#891</a></li> <li>perf: Optimize UUID hex parsing and formatting by <a href="https://github.com/geeknoid"><code>@geeknoid</code></a> in <a href="https://redirect.github.com/uuid-rs/uuid/pull/894">uuid-rs/uuid#894</a></li> <li>Prepare for 1.23.5 release by <a href="https://github.com/KodrAus"><code>@KodrAus</code></a> in <a href="https://redirect.github.com/uuid-rs/uuid/pull/895">uuid-rs/uuid#895</a></li> </ul> <h2>New Contributors</h2> <ul> <li><a href="https://github.com/geeknoid"><code>@geeknoid</code></a> made their first contribution in <a href="https://redirect.github.com/uuid-rs/uuid/pull/894">uuid-rs/uuid#894</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/uuid-rs/uuid/compare/v1.23.4...v1.23.5">https://github.com/uuid-rs/uuid/compare/v1.23.4...v1.23.5</a></p> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/uuid-rs/uuid/commit/6a8aeab3d02838f6fef71e69cdfda963e8c4158b"><code>6a8aeab</code></a> Merge pull request <a href="https://redirect.github.com/uuid-rs/uuid/issues/896">#896</a> from uuid-rs/cargo/v1.24.0</li> <li><a href="https://github.com/uuid-rs/uuid/commit/e6db8ec0879fc9e703efc1911512c111f86e540d"><code>e6db8ec</code></a> prepare for 1.24.0 release</li> <li><a href="https://github.com/uuid-rs/uuid/commit/606f2365c706ccd0309d3263b381f5378b004e4d"><code>606f236</code></a> Merge pull request <a href="https://redirect.github.com/uuid-rs/uuid/issues/892">#892</a> from weifanglab/main</li> <li><a href="https://github.com/uuid-rs/uuid/commit/ab848dbdf652c91af3ed5a413d3edd74bc2ebcfb"><code>ab848db</code></a> feat(fmt): support encoding into MaybeUninit buffers</li> <li><a href="https://github.com/uuid-rs/uuid/commit/5dc6b3d1a995e6244a386740588c8d094ca30690"><code>5dc6b3d</code></a> Merge pull request <a href="https://redirect.github.com/uuid-rs/uuid/issues/895">#895</a> from uuid-rs/cargo/v1.23.5</li> <li><a href="https://github.com/uuid-rs/uuid/commit/5a7dfe50e2a2cf41a9d4330e00971e891bcb990f"><code>5a7dfe5</code></a> prepare for 1.23.5 release</li> <li><a href="https://github.com/uuid-rs/uuid/commit/9b4bfc8fe359e24638eccf6c6be424c25ad6ba8c"><code>9b4bfc8</code></a> Merge pull request <a href="https://redirect.github.com/uuid-rs/uuid/issues/894">#894</a> from geeknoid/main</li> <li><a href="https://github.com/uuid-rs/uuid/commit/5acc5a550ef1ccec951f1d2618b33e1171a88b9e"><code>5acc5a5</code></a> perf: Optimize UUID hex parsing and formatting</li> <li><a href="https://github.com/uuid-rs/uuid/commit/6fa1a1e38afa7536bad4cd0febf689338f65c220"><code>6fa1a1e</code></a> feat(fmt): support encoding into MaybeUninit buffers</li> <li><a href="https://github.com/uuid-rs/uuid/commit/1e5d8679542d2bb15412a86839006dc01f680a51"><code>1e5d867</code></a> Merge pull request <a href="https://redirect.github.com/uuid-rs/uuid/issues/891">#891</a> from frostyplanet/doc</li> <li>Additional commits viewable in <a href="https://github.com/uuid-rs/uuid/compare/v1.23.4...v1.24.0">compare view</a></li> </ul> </details> <br /> Updates `xxhash-rust` from 0.8.16 to 0.8.17 <details> <summary>Commits</summary> <ul> <li>See full diff in <a href="https://github.com/DoumanAsh/xxhash-rust/commits">compare view</a></li> </ul> </details> <br /> Updates `cc` from 1.2.66 to 1.2.67 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/rust-lang/cc-rs/releases">cc's releases</a>.</em></p> <blockquote> <h2>cc-v1.2.67</h2> <h3>Other</h3> <ul> <li>Fix clippy warning (<a href="https://redirect.github.com/rust-lang/cc-rs/pull/1788">#1788</a>)</li> <li>Regenerate target info (<a href="https://redirect.github.com/rust-lang/cc-rs/pull/1785">#1785</a>)</li> <li>Add support for <code>aarch64-unknown-linux-pauthtest</code> target (<a href="https://redirect.github.com/rust-lang/cc-rs/pull/1713">#1713</a>)</li> <li>Fix nightly compilation error (<a href="https://redirect.github.com/rust-lang/cc-rs/pull/1783">#1783</a>)</li> </ul> </blockquote> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/rust-lang/cc-rs/blob/main/CHANGELOG.md">cc's changelog</a>.</em></p> <blockquote> <h2><a href="https://github.com/rust-lang/cc-rs/compare/cc-v1.2.66...cc-v1.2.67">1.2.67</a> - 2026-07-11</h2> <h3>Other</h3> <ul> <li>Fix clippy warning (<a href="https://redirect.github.com/rust-lang/cc-rs/pull/1788">#1788</a>)</li> <li>Regenerate target info (<a href="https://redirect.github.com/rust-lang/cc-rs/pull/1785">#1785</a>)</li> <li>Add support for <code>aarch64-unknown-linux-pauthtest</code> target (<a href="https://redirect.github.com/rust-lang/cc-rs/pull/1713">#1713</a>)</li> <li>Fix nightly compilation error (<a href="https://redirect.github.com/rust-lang/cc-rs/pull/1783">#1783</a>)</li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/rust-lang/cc-rs/commit/fa031a077aca2b19a31de895eafb17e965f35c89"><code>fa031a0</code></a> chore(cc): release v1.2.67 (<a href="https://redirect.github.com/rust-lang/cc-rs/issues/1789">#1789</a>)</li> <li><a href="https://github.com/rust-lang/cc-rs/commit/842aab16d28c77b30157f97bef777f8a643652b5"><code>842aab1</code></a> Bump taiki-e/install-action from 2.81.8 to 2.82.8 (<a href="https://redirect.github.com/rust-lang/cc-rs/issues/1786">#1786</a>)</li> <li><a href="https://github.com/rust-lang/cc-rs/commit/e2f07d0d68e3698d59d5e96d53c78f30e83ac845"><code>e2f07d0</code></a> Fix clippy warning (<a href="https://redirect.github.com/rust-lang/cc-rs/issues/1788">#1788</a>)</li> <li><a href="https://github.com/rust-lang/cc-rs/commit/8ced615d2c5349dfcb75ab064dfc600490264d89"><code>8ced615</code></a> Regenerate target info (<a href="https://redirect.github.com/rust-lang/cc-rs/issues/1785">#1785</a>)</li> <li><a href="https://github.com/rust-lang/cc-rs/commit/2943b5251297b6dabaf3f8e4c7d32560f69415f6"><code>2943b52</code></a> Add missing todo for deprecated API</li> <li><a href="https://github.com/rust-lang/cc-rs/commit/43ae1bf3ff399a7954c5b34cef7dc04d511f360e"><code>43ae1bf</code></a> Add support for <code>aarch64-unknown-linux-pauthtest</code> target (<a href="https://redirect.github.com/rust-lang/cc-rs/issues/1713">#1713</a>)</li> <li><a href="https://github.com/rust-lang/cc-rs/commit/a5c584a1fa50ee93c7db9c3ff85b04a36df9100b"><code>a5c584a</code></a> Fix nightly compilation error (<a href="https://redirect.github.com/rust-lang/cc-rs/issues/1783">#1783</a>)</li> <li>See full diff in <a href="https://github.com/rust-lang/cc-rs/compare/cc-v1.2.66...cc-v1.2.67">compare view</a></li> </ul> </details> <br /> Updates `sha2` from 0.10.9 to 0.11.0 <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/RustCrypto/hashes/commit/ffe093984c004769747e998f77da8ff7c0e7a765"><code>ffe0939</code></a> Release sha2 0.11.0 (<a href="https://redirect.github.com/RustCrypto/hashes/issues/806">#806</a>)</li> <li><a href="https://github.com/RustCrypto/hashes/commit/8991b65fe400c31c4cc189510f86ae642c470cd9"><code>8991b65</code></a> Use the standard order of the <code>[package]</code> section fields (<a href="https://redirect.github.com/RustCrypto/hashes/issues/807">#807</a>)</li> <li><a href="https://github.com/RustCrypto/hashes/commit/3d2bc57db40fd6aeb25d6c6da98d67e2784c2985"><code>3d2bc57</code></a> sha2: refactor backends (<a href="https://redirect.github.com/RustCrypto/hashes/issues/802">#802</a>)</li> <li><a href="https://github.com/RustCrypto/hashes/commit/faa55fb83697c8f3113636d88070e5f5edc8c335"><code>faa55fb</code></a> sha3: bump <code>keccak</code> to v0.2 (<a href="https://redirect.github.com/RustCrypto/hashes/issues/803">#803</a>)</li> <li><a href="https://github.com/RustCrypto/hashes/commit/d3e6489e56f8486d4a93ceb7a8abf4924af1de7b"><code>d3e6489</code></a> sha3 v0.11.0-rc.9 (<a href="https://redirect.github.com/RustCrypto/hashes/issues/801">#801</a>)</li> <li><a href="https://github.com/RustCrypto/hashes/commit/bbf6f51ff97f81ab15e6e5f6cf878bfbcb1f47c8"><code>bbf6f51</code></a> sha2: tweak backend docs (<a href="https://redirect.github.com/RustCrypto/hashes/issues/800">#800</a>)</li> <li><a href="https://github.com/RustCrypto/hashes/commit/155dbbf2959dbec0ec75948a82590ddaede2d3bc"><code>155dbbf</code></a> sha3: add default value for the <code>DS</code> generic parameter on <code>TurboShake128/256</code>...</li> <li><a href="https://github.com/RustCrypto/hashes/commit/ed514f2b34526683b3b7c41670f1887982c3df64"><code>ed514f2</code></a> Use published version of <code>keccak</code> v0.2 (<a href="https://redirect.github.com/RustCrypto/hashes/issues/799">#799</a>)</li> <li><a href="https://github.com/RustCrypto/hashes/commit/702bcd83735a49c928c0fc24506924f5c0aa22af"><code>702bcd8</code></a> Migrate to closure-based <code>keccak</code> (<a href="https://redirect.github.com/RustCrypto/hashes/issues/796">#796</a>)</li> <li><a href="https://github.com/RustCrypto/hashes/commit/827c043f82d57666a0b146d156e91c39535c1305"><code>827c043</code></a> sha3 v0.11.0-rc.8 (<a href="https://redirect.github.com/RustCrypto/hashes/issues/794">#794</a>)</li> <li>Additional commits viewable in <a href="https://github.com/RustCrypto/hashes/compare/sha2-v0.10.9...sha2-v0.11.0">compare view</a></li> </ul> </details> <br /> Updates `hmac` from 0.12.1 to 0.13.0 <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/RustCrypto/MACs/commit/0236c8eb50098dd7f277a71ab89caaeb1e7314df"><code>0236c8e</code></a> hmac v0.13.0 (<a href="https://redirect.github.com/RustCrypto/MACs/issues/263">#263</a>)</li> <li><a href="https://github.com/RustCrypto/MACs/commit/b895e50c852f58727b2fa6a480c4ec68cf99025f"><code>b895e50</code></a> Migrate tests to the new blobby format (<a href="https://redirect.github.com/RustCrypto/MACs/issues/264">#264</a>)</li> <li><a href="https://github.com/RustCrypto/MACs/commit/3d1440b379457f680c58bc1ec0e2f8714a72df7e"><code>3d1440b</code></a> Workspace-level lint configuration (<a href="https://redirect.github.com/RustCrypto/MACs/issues/261">#261</a>)</li> <li><a href="https://github.com/RustCrypto/MACs/commit/11d4f3624f3dfe95d57cfb8a3173d7071eb5a1b3"><code>11d4f36</code></a> hmac: use release versions of <code>dev-dependencies</code> (<a href="https://redirect.github.com/RustCrypto/MACs/issues/260">#260</a>)</li> <li><a href="https://github.com/RustCrypto/MACs/commit/c40b82b2ac40bc0260d0c35d6a518f97e72411e5"><code>c40b82b</code></a> hmac: bump <code>sha2</code> dev-dependency to v0.11 (<a href="https://redirect.github.com/RustCrypto/MACs/issues/259">#259</a>)</li> <li><a href="https://github.com/RustCrypto/MACs/commit/1fa0781413e3d07d18a9bb622f096754640dee53"><code>1fa0781</code></a> Cut rc.5 prereleases (<a href="https://redirect.github.com/RustCrypto/MACs/issues/258">#258</a>)</li> <li><a href="https://github.com/RustCrypto/MACs/commit/a0082655c09ffe682a10640cbaefb67c8175010e"><code>a008265</code></a> hmac v0.13.0-rc.6 (<a href="https://redirect.github.com/RustCrypto/MACs/issues/256">#256</a>)</li> <li><a href="https://github.com/RustCrypto/MACs/commit/da485cd7baf0b7f5e501f5b42644bf9ddd428c6b"><code>da485cd</code></a> Use <code>(Reset)MacTraits</code> (<a href="https://redirect.github.com/RustCrypto/MACs/issues/254">#254</a>)</li> <li><a href="https://github.com/RustCrypto/MACs/commit/2c51e3b76e6f50c13d85577c3faac7df66e24306"><code>2c51e3b</code></a> hmac: derive <code>Clone</code> instead of relying on <code>(Reset)MacTraits</code> (<a href="https://redirect.github.com/RustCrypto/MACs/issues/253">#253</a>)</li> <li><a href="https://github.com/RustCrypto/MACs/commit/669d805394f5f4d0dc07ded010c0df9a3ab01629"><code>669d805</code></a> Relax <code>Clone</code> bounds (<a href="https://redirect.github.com/RustCrypto/MACs/issues/250">#250</a>)</li> <li>Additional commits viewable in <a href="https://github.com/RustCrypto/MACs/compare/hmac-v0.12.1...hmac-v0.13.0">compare view</a></li> </ul> </details> <br /> Updates `syn` from 2.0.118 to 2.0.119 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/dtolnay/syn/releases">syn's releases</a>.</em></p> <blockquote> <h2>2.0.119</h2> <ul> <li>Preserve attributes on tail-call expressions in statement position (<a href="https://redirect.github.com/dtolnay/syn/issues/1994">#1994</a>)</li> <li>Parse field-representing types builtin in type position (<a href="https://redirect.github.com/dtolnay/syn/issues/1996">#1996</a>)</li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/dtolnay/syn/commit/3295f9e9841785ac88a5e558c884854d5fb7d67f"><code>3295f9e</code></a> Release 2.0.119</li> <li><a href="https://github.com/dtolnay/syn/commit/6ae9c18793d029bdad068a796e11c0d276d346d1"><code>6ae9c18</code></a> Merge pull request <a href="https://redirect.github.com/dtolnay/syn/issues/1996">#1996</a> from dtolnay/fieldrepresenting</li> <li><a href="https://github.com/dtolnay/syn/commit/8ebd96350c7f7f52f762a735d581e589a736d10e"><code>8ebd963</code></a> Parse field-representing types builtin</li> <li><a href="https://github.com/dtolnay/syn/commit/540ccf8298c3e422d672a9793f3e12f638d06691"><code>540ccf8</code></a> Drop unneeded lifetime on covariant Cursor in verbatim::between</li> <li><a href="https://github.com/dtolnay/syn/commit/aa05887100a7c28b3e9a777eb4711e3e7457849b"><code>aa05887</code></a> Merge pull request <a href="https://redirect.github.com/dtolnay/syn/issues/1995">#1995</a> from dtolnay/cursor</li> <li><a href="https://github.com/dtolnay/syn/commit/b7160d353ee7372567b1621f29a42c077c042a69"><code>b7160d3</code></a> Reduce forking for Verbatim construction</li> <li><a href="https://github.com/dtolnay/syn/commit/efdc9255a9668a4bfff8085d114eb95ae6ebd7e9"><code>efdc925</code></a> Merge pull request <a href="https://redirect.github.com/dtolnay/syn/issues/1994">#1994</a> from dtolnay/tailcall</li> <li><a href="https://github.com/dtolnay/syn/commit/de6424cc3b41f3a4d37691c2b5104d9d33eb8ec0"><code>de6424c</code></a> Preserve attribute on tail-call expression in statement position</li> <li><a href="https://github.com/dtolnay/syn/commit/050dd73d9622948e1321e96a5af378ce63506ee2"><code>050dd73</code></a> Stricter const move closure grammar</li> <li><a href="https://github.com/dtolnay/syn/commit/c7d514bd7f1cf7259945feb8b4c29427bbf2df7e"><code>c7d514b</code></a> Merge pull request <a href="https://redirect.github.com/dtolnay/syn/issues/1992">#1992</a> from dtolnay/scanconstmove</li> <li>Additional commits viewable in <a href="https://github.com/dtolnay/syn/compare/2.0.118...2.0.119">compare view</a></li> </ul> </details> <br /> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…n caches (lance-format#7817) ## Problem `read_transaction_by_version` delegated to `checkout_version` + `read_transaction`. A caller requesting one transaction does not need a historical `Dataset`, and the checkout path has session-cache side effects: the historical manifest is inserted into the metadata cache, manifest loading opportunistically decodes and caches the `IndexSection` into the index cache, and the transaction is inserted into the metadata cache. A long-lived process scanning many historical versions fills the shared caches with entries it never reuses (more visible after lance-format#7661). Fixes lance-format#7801. ## Change - `read_version_transaction(version) -> VersionTransaction { version, timestamp, transaction }` (new): resolves the version through the dataset's current branch and `CommitHandler`, decodes the manifest transiently (no cache read or write, no `IndexSection` decode), and reads the inline or external transaction. No historical `Dataset` is constructed. The compact record carries the manifest timestamp so callers don't have to check out for it. - The resolved manifest is validated to belong to the dataset's branch (matching `checkout_by_ref`), so a branch-insensitive commit handler errors instead of returning another branch's transaction. - `read_transaction_by_version` now delegates to it; signature and error semantics unchanged (missing/cleaned-up version is still an error). - `read_transaction` (current version) is unchanged apart from factoring the storage read into a shared helper; it still checks/populates the transaction cache. - `checkout_version` caching behavior is untouched. Known trade-off: an inline transaction costs one extra ranged read vs the old path (the manifest tail is read for the timestamp/offsets, then the transaction message separately). Callers scanning history are expected to memoize per-version results; a combined read is a possible follow-up. ## Tests - `test_read_version_transaction_does_not_populate_caches` — 20-version dataset with a BTree index; reads every version via the new API on a fresh session, asserts results and timestamps equal `checkout_version(v)`, that the session's index and metadata cache entries/bytes do not grow, and that a missing version errors with `Error::NotFound` (the version resolves to a manifest path that does not exist). - `test_read_version_transaction_v1_manifest_naming` — V1-named manifests (asserts the naming premise) resolve and match a checkout. - `test_read_version_transaction_on_branch` — versions resolve against the branch chain and match a full branch checkout. - `test_inline_transaction` (existing) extended: the external-transaction-file fallback is asserted for the direct read using the manifest that test already constructs. `read_version_transaction` carries a compiling doc example. Missing-version behavior of `read_transaction_by_version` itself is already covered by the existing `test_read_transaction_properties`, which now runs through the new implementation. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added an API to retrieve transaction details for a specific dataset version, including the UTC commit timestamp and an optional transaction payload. * **Bug Fixes** * Historical transaction reads avoid unnecessary cache/index updates when no transaction is present. * **Tests** * Added coverage for inline-versus-external transaction fallback, correct behavior without cache pollution, and accurate handling of historical manifest formats across versions and branches. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
…ance-format#7629) Built on lance-format#7624, now merged into main; this is the last PR in the series and contains only the norm-cache and slim-heap changes. ## What Two hot-loop changes, both score-identical (only tie ordering among equal scores can shift with the slimmer heap tuple): - **Per-search norm cache** (Lucene's norm cache): `Scorer::doc_norm` exposes the BM25 doc-length denominator addend, and quantized V3 searches bake the 256 possible addends once per partition-search. The OR streaming loop, OR drain/single-essential completion paths, and bulk conjunction scoring pass then use one byte-norm load plus a cached addend instead of recomputing `k1*(1-b+b*dl/avgdl)` per doc. The factored expressions are evaluated identically, so scores are bit-equal to the uncached path. - **Slim top-k heap**: heap entries stay compact while `(term, freq)` pairs live in at most `k` reusable side slots. Replacements clear and refill the evicted entry's slot while retaining its `Vec` capacity, and final candidates take their surviving slots without copying. This keeps frequency storage bounded at `O(k × clauses)` while avoiding Vec-carrying heap entries. ## Measured vs lance-format#7624 Measured at `aa084bd` before the bounded-slot review follow-up in `5ce91ee`: per-branch-tip wheels, 1000 queries × 8 concurrent. OR and AND used the warm, fully prewarmed 200M-doc V3/256 index; phrase used the 50M-doc V3/256 positions index. The scoring path is unchanged, but the bounded collector still needs a full 200M rerun. | query | lance-format#7624 | this PR | |---|---:|---:| | OR 3w k10 | 230 qps | **316 qps (1.37×)** | | OR 3w k100 | 127 qps | **169 qps (1.34×)** | | AND 3w k10 | 172 qps | 177 qps (+3%) | | AND 3w k100 | 86 qps | 89 qps (+3.5%) | | phrase 3w k10 @50m | 0.2256s | 0.2187s (+3%) | The OR win comes from the streaming loop, where recomputing the BM25 denominator per `clause × doc` was the largest single cost. ## Compatibility The norm cache consumes the existing V3 quantized byte norms and falls back to the existing scorer path otherwise. It does not change the FTS index format or index version. ## Verification - A/B against lance-format#7624: `score_diff=0` across AND (bulk and classic) and phrase query sets. - Equal-score tie ordering may change because of the slimmer heap tuple; 34 such tie reorders were observed in the benchmark set. - Final WAND suite: 82 passed. - `cargo test -p lance-index`: 817 passed, 2 ignored. - `cargo clippy --all --tests --benches -- -D warnings` and `cargo fmt --all -- --check` passed across the reviewed stack. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Performance** * Improved full-text search performance by reducing temporary memory usage during ranked searches. * Added caching to speed up BM25 relevance calculations, especially for top-result and bulk searches. * **Search Quality** * Improved consistency of BM25 scoring across different search paths while preserving document-length considerations. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Yang Cen <yang@lancedb.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
## What is the bug? `test_vector_index_distance_range` requires exact equality between brute-force and indexed `float32` distances. The two paths can use different scalar and runtime-dispatched SIMD reduction orders, producing a few ULPs of rounding difference even when the results are equivalent. ## What issues does this cause? The test fails intermittently on x86 CI across unrelated changes, while the returned IDs, ordering, and distance-range checks all pass. ## How does this PR fix the problem? Use a relative tolerance of `1e-5` for the distance comparison while retaining `atol=0.0`. This matches the expected precision of the alternative `float32` kernels without weakening the range or result checks. ## Validation - `uv run pytest python/tests/test_vector_index.py::test_vector_index_distance_range -q` - `uv run make lint` Co-authored-by: Yang Cen <yang@lancedb.com>
…7820) Make the full and indexed file metadata lookup methods public so callers can reuse the fragment's dataset-level metadata cache instead of loading metadata independently through FileReader. I ran into this while trying to inspect the file footer of a fragment that had already been opened. The obvious option was to call FileReader::read_all_metadata, but that API reads directly from storage and does not reuse the dataset’s file metadata cache. This can result in another metadata I/O even though the fragment has already loaded the same information. FileFragment already has cache-aware helpers for loading the full metadata or metadata index. This change makes get_file_metadata and get_file_metadata_index public so other fragment-level operations can reuse the existing cached metadata instead of reading it again through FileReader. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Exposed file metadata and metadata index access for broader integration and tooling. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Why FTS corpus stats already read and cache each partition's Arrow document-length column and compute its total token count. The first WAND match then copied that entire column into a new Vec and summed it again while building the num-tokens-only DocSet. On large partitions this introduced O(rows) anonymous-page work for every PE/index partition even when no index parts were loaded. ## What Make the num-tokens-only DocSet retain a shared ScalarBuffer view and accept the previously cached total. Full DocSet materialization remains owned for masks, fragment reuse, remapping, and row-id semantics. Total initialization is single-flight, and retained-memory accounting deduplicates the shared Arrow storage. This removes the confirmed first-touch copy and repeated sum without changing WAND behavior or broadening prewarm or cache scope.
…ance-format#7769) Closes lance-format#7770 ## Summary - propagate final WAL flush failures from both MemTable and WAL-only close paths; - propagate frozen MemTable/L0 flush failures; - preserve the first causal error while continuing close-time cleanup; - always shut down background tasks before returning; - document the `ShardWriter::close()` error contract. ## Root cause `ShardWriter::close()` awaited three close-time completion channels but discarded their values. It also ignored failures to send the final WAL flush requests. As a result, close could return `Ok(())` after WAL persistence failure, peer fencing, L0 flush failure, handler shutdown, or completion-channel closure. The WAL-only case was especially unsafe: an unsuccessful append remained only in the in-memory pending queue, which was dropped when the consuming close returned success. ## Control-flow comparison ```mermaid flowchart TD M["MemTable final WAL"] --> C["Close-time completion"] L["Frozen MemTable / L0 flush"] --> C W["WAL-only final WAL"] --> C C --> OLD["Before: discard completion value"] OLD --> OLD_SHUTDOWN["Shutdown tasks"] OLD_SHUTDOWN --> FALSE_OK["Return Ok even after persistence failure"] C --> NEW["After: convert outcome to Result"] NEW --> FIRST["Preserve the first causal error"] FIRST --> DRAIN["Continue draining remaining watchers"] DRAIN --> NEW_SHUTDOWN["Always shut down tasks"] NEW_SHUTDOWN --> RETURN["Return first error, otherwise Ok"] ``` ## Fix The close path now maintains a first-error accumulator and executes the complete shutdown sequence in operational order: 1. request and await the final WAL flush; 2. freeze the active MemTable; 3. await every frozen MemTable flush; 4. shut down all background tasks; 5. return the first error encountered. `WalFlushFailure::into_error()` preserves typed fence reasons. Frozen MemTable results continue to use the existing `DurabilityResult` conversion. A private close-specific helper centralizes WAL completion handling for both writer modes. ## Tests Added failure-focused coverage for: - WAL-only final WAL persistence failure and task shutdown; - MemTable final WAL persistence failure and first-error preservation; - frozen MemTable/L0 fencing failure. The stricter close behavior also exposed three indexed-flush fixtures using `memory://` even though generation flushing reopens the dataset through an independent object-store registry. Those fixtures now use isolated `shared-memory://` authorities so the reopened generation observes the same backing bytes. ## Validation - `cargo test -p lance --lib dataset::mem_wal::write::tests` - 54 passed - `cargo test -p lance --lib dataset::mem_wal` - 501 passed, 1 ignored, 0 failed - `cargo fmt --all -- --check` - `pre-commit run --files rust/lance/src/dataset/mem_wal/write.rs` - `cargo clippy --all --tests --benches -- -D warnings` - `cargo clippy --all-features --tests --benches -- -D warnings` ## Compatibility - no public signature changes; - no Python or Java binding changes; - no persistent-format changes; - no dependency or lockfile changes. ## Implementation notes Beyond propagating the three close-time failures, the close path also: - drains remaining frozen MemTable watchers after a freeze failure so a successor failure is logged without replacing the first causal error (the accompanying comment documents this first-error-preserving drain behavior); - sends the final WAL flush directly on the channel rather than via `WalFlusher::trigger_flush`, which silently returns `Ok` when the flusher's `flush_tx` is unset and would let close acknowledge durability it never achieved — a closed send channel must surface as an error here; - logs the final close error at WARN so a single-stage failure is observable (`merge_close_stage` only logs the secondary error when both stages fail). Maintainers: please consider applying the `critical-fix` label because the old behavior could acknowledge a graceful close after final persistence failed. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved dataset close reliability by preserving the first failure across multi-stage shutdown instead of discarding later outcomes. * Ensured final WAL flush failures are surfaced, including WAL-only closes that drain pending batches before returning. * Propagated memtable freeze/flush watcher results (including fenced/failing flush scenarios) and treated missing watcher completion as an error. * **Tests** * Added/expanded coverage for close error propagation in both MemTable and WAL-only modes, including background task join timing. * Expanded shared in-memory scenarios using unique authorities to improve consistency across reopen/flush behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
) ## Summary - make the Python `PackedBlobWriter` and `DedicatedBlobWriter` wrappers safe to release on a thread other than the one that created them - replace PyO3's `unsendable` restriction with mutex-protected writer ownership - add a production-shaped regression test covering both writer types ## Motivation Geneva's Blob v2 checkpoint path prepares packed blob sidecars on a dedicated checkpoint-writer thread. During a large Azure benchmark, Azure returned `503 ServerBusy` from a multipart block `PUT`. The checkpoint thread propagated that object-store exception to the actor's owner thread. The original exception retained its Python traceback. That traceback retained the checkpoint frame and its local `PackedBlobWriter`, so the last reference to the writer was eventually released by the owner thread rather than the checkpoint thread. Because the binding declared the writer as `#[pyclass(unsendable)]`, PyO3 emitted a second unraisable exception: ```text RuntimeError: lance::blob::PyPackedBlobWriter is unsendable, but is being dropped on another thread ``` This secondary error appeared at unrelated Python stack locations, obscured the original Azure failure, and prevented normal RAII cleanup of the writer on that path. The same ownership problem applies to `DedicatedBlobWriter`, which used the same binding policy. ## Root cause The core Rust blob writers are `Send`, so transferring ownership for destruction is safe. They are not `Sync`, however, because the underlying `dyn Writer` is not shareable for concurrent access. PyO3 0.28 requires ordinary Python classes to be both `Send` and `Sync`; simply removing `unsendable` therefore does not compile. ## Fix Store each core writer as `Mutex<Option<Writer>>` and remove the `unsendable` annotation. The mutex makes the Python wrapper `Send + Sync` while retaining exclusive access to the non-`Sync` writer. Existing mutation and finish semantics remain unchanged: - completed writers still raise the existing `ValueError` - writer ownership is still consumed by `finish` - failed bulk writes still drop their active writer through RAII - a poisoned lock surfaces as a descriptive Python `RuntimeError` There is no public Python API change. ## Regression coverage The new parameterized test models the production lifetime directly: 1. create a packed or dedicated writer on a worker thread 2. raise and hand an exception containing that thread's traceback to the owner thread 3. release the exception and force garbage collection on the owner thread 4. assert that `sys.unraisablehook` receives no cross-thread destruction error The test fails on `main` with the same `PyPackedBlobWriter is unsendable` / `PyDedicatedBlobWriter is unsendable` messages and passes with this change. ## Validation - `uv run make build` - `uv run pytest -q python/tests/test_blob.py -k 'packed_blob_writer or failed_blob_writer'` — 32 passed - `uv run pytest -q python/tests/test_blob.py` — 139 passed - `uv run make lint` — Ruff, Pyright (0 errors), Rust formatting, and Clippy passed - commit hooks — Ruff, Ruff format, Rust formatting, and typos passed
…-format#7821) ## Why Implements the new "Lance Docs" design as the real mkdocs site, so `make serve` / `make build` produce it directly. The design is delivered by a dedicated custom MkDocs theme (`docs/theme/`) whose markup and CSS are ported from the design implementation: header with section tabs, GitHub star count and Discord link, grouped side navigation with ink rules, right-hand scroll-spy table of contents, light/dark mode, and the hero/stats/features homepage with real highlighted code blocks. MkDocs keeps providing what fits the design — the markdown pipeline (admonitions, content tabs, snippets, autolinked URLs), awesome-pages navigation, and the search plugin's index — while the theme owns everything user-facing, including a lightweight search overlay (`/` or `Cmd/Ctrl+K`) and on-demand mermaid rendering. `mkdocs-material` is no longer a dependency. Preview: ```bash cd docs make serve ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Rolled out a custom documentation theme with a refreshed homepage, improved typography, navigation, and responsive layout. * Added light/dark mode switching with saved preference. * Introduced a search overlay with keyboard shortcuts, ranked results, and highlighted matches. * Enhanced docs rendering with copy-to-clipboard code controls, on-demand Mermaid diagrams, horizontal table scrolling, and TOC scroll tracking. * Added a dedicated 404 page and improved GitHub star display with caching/offline fallback. * **Documentation** * Updated the MkDocs configuration and theme setup to use the new theme design. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: prrao87 <35005448+prrao87@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…x/column drops Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- reject staged singleton (frag-reuse/MemWAL) creation over a concurrent removal-only transaction, with a resolver unit test - assert conflict message content in the new staged-transaction tests - reword the staging doc: a deferred-remap rewrite leaves stale fragment coverage until the remap catches up, not duplicate entries Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ame conflicts - validate CreateIndex at manifest build time: indexed fields must still exist in the schema (closes the alter_columns type-cast gap that the pairwise Project check could not see, since a cast commits Merge with a new field id) and non-system removed segments must still be present (closes the mirror race where a drop racing a same-name replacement silently no-ops) - collapse the frag-reuse/MemWAL/regular-name conflict booleans into one symmetric name-intersection rule covering both directions - regression tests: staged commit after an alter_columns cast fails as incompatible; drop_index racing a replacement fails retryably and succeeds on retry; resolver unit test covers all conflict directions Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
DatasetIndexExt::build_existing_index_segments_transactionto prepare anOperation::CreateIndextransaction for existing index segments without committing.commit_existing_index_segmentsto reuse the staged transaction path.CommitBuilder.Tests
cargo fmt --allcargo test -p lance existing_index_segments -- --nocapturecargo test -p lance test_vector_execute_uncommitted_segments_commit_without_staging -- --nocapturecargo checkagainst the new public APIFixes lance-format#6666
Summary by cubic
Adds a staged transaction builder to publish existing index segments without auto-committing, enabling two-phase index creation; refactors the existing commit path to use it. Also pulls in recent search and tokenizer improvements from main.
New Features
DatasetIndexExt::build_existing_index_segments_transaction(index_name, column, segments) -> Transactionfor staging, then commit viaCommitBuilder(andcommit_existing_index_segmentsnow uses this path).query_indexcolumn; fast_search for scalar indexes to scan only indexed fragments; ICU-based FTS tokenizer viabase_tokenizer="icu".Bug Fixes
Written for commit 53aa593. Summary will update on new commits.
Review in cubic