100 Commits
Author SHA1 Message Date
Luis Cossío 37e4b3cc3d [live-reload] hotfix: infallible to_owned in DiskCache (#10429) 2026-09-03 12:45:58 +02:00
Luis Cossío 44bf7f3486 [updater] Batch upsert quantized vectors (#10417)
* upsert quantized vectors in batch

* fix bounds
2026-09-03 12:45:57 +02:00
Luis CossíoandTim Visée 9e4748ba01 [edge] open and reload IO don't block search pool (#10366)
* existing segments: wait for IO outside of search pool

* new segments: wait for IO outside of search pool

* extract reload into separate function

* Update lib/edge/Cargo.toml

---------

Co-authored-by: Tim Visée <tim+github@visee.me>
2026-09-03 12:45:57 +02:00
Luis Cossío d7e41f35ed [UIO] Segment live_preload waits for all IO before returning (#10357)
* `LiveReload::live_preload` returns futures

* await reopens and reloads concurrently
2026-09-03 12:45:57 +02:00
Luis CossíoandClaude Fable 5 7cf6d2b2fe [UIO] make UniversalRead::live_preload async (#10356)
* rename `reopen`->`live_reload` and `schedule_reopen`->`live_preload`

* `UniversalRead::live_preload` returns a shared future

* assert snapshot-miss eagerly on `live_preload`

`live_reload` cannot see the failed preload: its blocking fallback
re-resolves the length from the remote and succeeds. The error
surfaces at preload time, as callers (`ok_not_found`) expect.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-03 12:45:57 +02:00
Luis Cossío 5f2985ef6a [UIO] CachedFs waits for scheduled files to resolve + misc (#10353)
* [CachedFs] new `schedule` and `wait_all` primitives

* [AppendableIdTracker] don't reopen if just opened

* eager NotFound in `schedule_open`

* add traces for async reads

* finish `preopen`/`preload` with `wait_all`

* lock all segments in parallel for `live_reload`

* LIST before everything

to do: we don't have whole-fetch in async mode. to prevent sequential
`len`, we won't overlap static files with LIST.

* `wait_all` returns nothing
2026-09-03 12:45:56 +02:00
Luis Cossío 9cfbe82b07 schedule_open returns nothing (#10355) 2026-09-03 12:45:54 +02:00
Luis CossíoandClaude Fable 5 f6e0566f38 [UIO] UniversalReadFs::open_async (#10352)
* `UniversalReadFs::open_async`

* `schedule_open` polls once

Scheduled opens must start eagerly: sync backends complete their
`open_async` on the first poll, preserving the prefetch contract
(handles outlive later file deletions/replacements). Moved down from
the integration branch so this PR stays green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-03 12:45:54 +02:00
Luis Cossío 0e68451875 [UIO] renames + enforce LiveReload::live_preload (#10351)
* make LiveReload::live_preload required

* rename `schedule_prefetch`->`schedule_open`

* rename `reschedule_prefetch`->`reschedule_open`
2026-09-03 12:45:53 +02:00
Luis Cossío 52d72d94dc [UIO] impl IoUringFile::read_bytes_async (#10288)
* bridge async with a dedicated `tokio_uring` thread

* impl `IoBufMut` for `AVec`

* [AI] Add tests

* [AI] handle O_DIRECT

odirect test
2026-09-03 12:45:53 +02:00
Luis Cossío 7a1e1f7684 [UIO] impl DiskCache::read_bytes_async (#10259)
* [AI] TDD: new tests

* impl `read_bytes_async` for simplediskcache
2026-09-03 12:45:51 +02:00
Luis Cossío 4e6ca7d05a [UIO] UniversalRead::read_bytes_async stubs (#10258)
* impl `read_bytes_async` stubs

* clippy

* map err to `TaskPanicked` when appropriate
2026-09-03 12:45:51 +02:00
Luis Cossío 76d18c319c [LiveReload] Preload indexes (#10229)
* impl live_preload for payload indexes

enable live_preload for bool and null indexes

* (not) impl live_preload for `VectorIndexReadEnum`

* impl live_preload for `ReadOnlyPayloadStorage`
2026-09-03 12:42:24 +02:00
5f952a62d9 [LiveReload] Preload ReadOnlyFlags (#10269)
* impl `live_preload` for `ReadOnlyFlags`

* fix: apply CodeRabbit auto-fixes

Fixed 1 file(s) based on 1 unresolved review comment.

Co-authored-by: CodeRabbit <noreply@coderabbit.ai>

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
2026-09-03 12:42:24 +02:00
Luis Cossío a9dcb654e0 fix preload for InMemoryBitvecFlags (#10268) 2026-09-03 12:42:23 +02:00
Luis Cossío e1fa82c4a8 [LiveReload] Preload all vector storages (#10228)
* impl live_preload for read-only vector storages

* impl live_preload for read-only quantized vectors
2026-09-03 12:42:23 +02:00
Luis Cossío 2489b5d462 [LiveReload] Preload chunked vectors (#10225)
* add InMemoryBitvecFlags::live_preload

* impl live_preload for ReadOnlyChunkedVectors

* heal-resilient reload

* no panic, service error
2026-09-03 12:42:23 +02:00
Luis Cossío 0ee359a924 [LivePreload] Preload id tracker (#10222)
* impl live_preload for read-only id trackers

* remove `raw_fs` argument

* fixup! use AtomicRefCell
2026-09-03 12:42:23 +02:00
Luis Cossío d0583725b5 [LiveReload] Prepare segment preload (#10221)
* genericize live_reload fs parameters

* impl live_preload for ReadOnlySegment

* split edge refresh into preload and apply passes

* only rotate file infos after successful reload
2026-09-03 12:41:05 +02:00
4c75ca4f2b Add writer for stored bitmask type (#10107)
* Extract stored bitmask encoding into `bitmask_file_bytes`

Also single-source the u32 position-space bound as `MAX_LOGICAL_LEN`.

* Add `MutableStoredBitmask`, collecting bitmask changes in RAM

Materializes via the existing reader without keeping the file handle open, tracks diverged positions, and atomically rewrites the whole file on save - skipping the write when nothing changed.

* Rename payload to bits

* Use changed boolean

* Remove now obsolete test

* Borrow the bitmap in bitmask encoding via Cow, avoiding a clone on save

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: generall <andrey@vasnetsov.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-03 12:41:04 +02:00
Luis Cossío da14759506 add etag to FileInfo (#10190) 2026-09-03 12:39:01 +02:00
Luis Cossío f07c820308 [LiveReload] impl LiveReload::live_preload for Blobstore (#10039)
* impl for Gridstore

* impl for Logstore

* fix path in `Tracker::preopen`

* take non-mut `&self`

* support UnchangedOpen in `live_reload`
2026-09-03 12:39:00 +02:00
Luis Cossío 85ea29c220 schedule_reopen takes &self (#10192) 2026-09-03 12:39:00 +02:00
Luis Cossío 82ce673c5e [LiveReload] Add live_preload (#10036)
* LiveReload: change associated type, add `live_preload`

* adjust existing trait implementations
2026-09-03 12:39:00 +02:00
Luis Cossío b06814f009 [CachedFs] Unchanged file open is no-op (#10050)
* add `UniversalIoError::UnchangedOpen`

* add & impl `CachedReadFs::reschedule_prefetch`

* clippy

* add `OkUnchanged` helper

* propagate scheduling errors immediately

* drop lock before reacquiring it

* clear prefetched_files on new snapshot

* only avoid prefetch on full FileInfo equality

* `UnchangedOpen` maps to `Cancelled`

* match-all match
2026-09-03 12:39:00 +02:00
8e90ff9ca6 [UpdateOnly] implement UpdateOnlyChunkedVectors (#10114)
* AI + manual: impl `UpdateOnlyChunkedVectors`

* AI: simplify

* graceful handling of unexpected file lengths

fix test

* incorporate updates from #10119

* drop the unused status read on open

The vector count loaded at open was never consulted: every batch carries the
offset it starts at, and the chunks are reconciled against that offset. Drop
the field and the read, and fold both watermark writes into `save_len`.

A corrupt status file no longer blocks opening the writer — the first batch
overwrites it.

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

* fix clippy: ensure_chunk_lengths no longer needs &mut self

Dropping the status field left it with nothing to mutate. `append_many` keeps
`&mut self` — nothing in this module is exported, so the lint reaches it too,
but the exclusive borrow is what enforces the single-writer contract the
appends rest on.

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

---------

Co-authored-by: generall <andrey@vasnetsov.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 12:36:33 +02:00
Luis Cossío 809e152007 [DiskCache] No-growth reopen is no-op (#10049)
* add `ScheduleReopen::Unchanged` to avoid retrying to reopen blockingly

* wrap `ScheduledReopen` in `Option`
2026-09-03 12:36:29 +02:00
Luis Cossío 20d693fd6c chore: fix dead code lints on macos (#10045) 2026-08-04 11:18:45 +02:00
Luis Cossío 22ac6bcb91 remove Clone implementation for MmapFile (#10047) 2026-08-04 11:18:45 +02:00
Luis Cossío ed3325a115 [UIO] 2-stage DiskCache::reopen (#10031)
* AI: implement 2 stage reopen

* manual: simplification refactor

* AI: simplify further

* upd trait interface

* use closure instead of `&CachedReadFs`

* open a new remote for the tail fetch

* rename to `cached_file_info`

* only resize after fallible op

* use consistent remote openoptions
2026-08-04 11:18:44 +02:00
Luis Cossío 84b2738823 flush after prefilling deleted vectors (#9992) 2026-08-04 11:18:44 +02:00
Luis Cossío f853a93a12 [CI] Enforce no default impl for batch methods (#9939)
* [AI] Add ast-grep rule to avoid default batch trait methods

* reword

* fix `FullTextIndexRead::check_match_batch`

* move to `tools/ast-grep/`

* Add tests

* pin ast-grep version

* fix spelling
2026-08-04 11:18:42 +02:00
Luis CossíoandClaude Fable 5 9a224a1d67 Mandatory batch impls (#9935)
* make `EncodedStorage::for_each_batch` mandatory

* make `DenseVectorStorageRead::for_each_in_dense_batch` mandatory

* make `DenseTQVectorStorage::for_each_in_dense_batch` mandatory

* make `DenseTQVectorStorage::read_dense_tq_bytes` mandatory

* make `QueryScorer::score_stored_batch` mandatory

...and implement for tq multivectors

* [AI] make `IdTrackerRead::internal_versions_batch` mandatory

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* [AI] make `IdTrackerRead::external_ids_batch` mandatory

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* [AI] make `DiskMappingsSource::resolve_internal_batch` mandatory

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-04 11:18:42 +02:00
Luis Cossío eaa505ecdc [io-bridge] split large reads into unordered chunks (#9896)
* read large files in unordered chunks

* use S3 error

* use a vec of ranges to track scattering

* self nits

* use less concurrent chunks
2026-08-04 11:18:42 +02:00
Luis Cossío 2e5bb066d1 make VectorStorageRead::read_vector_bytes mandatory (#9893)
Additionally:
- homogenize enum dispatch
- impl batch multivector read bytes
2026-08-04 11:18:41 +02:00
Luis Cossío f189ef5cf3 Misc nits (#9894)
* use `WithVector::is_enabled`

* suppress unused var lint

* fix non linux "useless mut" lint
2026-08-04 11:18:40 +02:00
553b6d6988 [edge] Actually use batched vector reads (#9855)
* override default impls

* Fix clippy needless_borrow in batched dense read ops

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: root <111755117+qdrant-cloud-bot@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-04 11:17:04 +02:00
Luis Cossíoandxzfc b2031d8aac [UIO] Increase PHF prefetch (#9842)
* increase phf prefetch

* no duplicate comment

Co-authored-by: xzfc <5121426+xzfc@users.noreply.github.com>

---------

Co-authored-by: xzfc <5121426+xzfc@users.noreply.github.com>
2026-08-04 11:17:03 +02:00
594745f9c0 [edge] Add readonly ram sparse index (#9841)
* Add Ram variant, loads from vector storage

* impl live reload

* preopen sparse storage when using ram index

* Batch storage reads when building sparse RAM index

Rebuild `build_ram_index` around a single `read_vectors::<Sequential>`
pass instead of a per-point `get_vector_opt` loop, so Gridstore
coalesces the IO into block reads instead of a round-trip per point.
Benefits both the read-only mutable-RAM rebuild and the writable
`SparseVectorIndex::plan` build path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: generall <andrey@vasnetsov.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-04 11:17:03 +02:00
Luis Cossío 6b9616dff5 remove expect (#9839) 2026-08-04 11:17:03 +02:00
c00506bf46 [DiskCache] Dedup overlapping remote requests (#9838)
* [AI] piggyback on in-flight requests

* cleanup tests

* Insert in-flight fetch through Slab's VacantEntry (#9840)

Replace the peek-key-then-insert pattern with slab's vacant_entry() API:
inserting through the reserved entry guarantees the fetch lands on the
key the remote read was tagged with, instead of relying on nothing
touching the slab between the peek and the insert. A failed remote
schedule still leaves no trace, as VacantEntry allocates nothing until
insert.

get_or_init_remote_pipeline now takes the field instead of &mut self so
the VacantEntry can hold a disjoint borrow of in_flight across the
schedule call.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Andrey Vasnetsov <andrey@vasnetsov.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-04 11:17:03 +02:00
Luis Cossío c7e0deed7c exhaustive match on async-like backends (#9837) 2026-08-04 11:17:03 +02:00
Luis Cossío 3aaa127682 [UIO] Add and use Populate::Partial variant (#9769)
* add (and handle) `Populate::Partial`

* use partial populate

* no OOB error
2026-08-04 11:17:00 +02:00
138e24cd55 [UIO] Better populate of bool and null indexes (#9768)
* propagate `populate` to bool and null `preopen`

* derive populate from payload schema

* Fix broken doc reference: restore payload_schema identifier

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: root <111755117+qdrant-cloud-bot@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-04 11:17:00 +02:00
Luis Cossío 516c59a824 [UIO] implement ReadOnlyFullTextIndex::preopen (#9765)
* prepare universalhashmap for partial populate

* [AI] implement `ReadOnlyFullTextIndex::preopen`
2026-08-04 11:17:00 +02:00
Luis Cossío de8e57b150 [AI] implement ReadOnlyGeoIndex::preopen (#9744) 2026-08-04 11:17:00 +02:00
Luis Cossío 83378d5e52 [AI] update the dispatches for ReadOnlyFieldIndex methods (#9766) 2026-08-04 11:17:00 +02:00
Luis Cossío 2e29e1561b [AI] implement ReadOnlyNumericIndex::preopen (#9743)
[AI] implement `ReadOnlyNumericIndex::preopen`
2026-08-04 11:16:59 +02:00
Luis Cossío 91ea0cda75 [UIO] Preopen map index (#9742)
* impl `OnDiskMapIndex::preopen`

* Prepare for preopen all payload indexes

* impl `ReadOnlyAppendableMapIndex::preopen`

* also use it for int and uuid index

* respect low memory mode
2026-08-04 11:16:59 +02:00
Luis Cossío f8ba0fbb01 [AI + manual] add preopen functions to id tracker (#9729) 2026-08-04 11:16:59 +02:00
Luis Cossío 604c4bdc0a add preopen functions to gridstore (#9728)
Additionally, propagate Populate for Tracker
2026-08-04 11:16:59 +02:00
Luis Cossío 2417721102 Pass file size to DiskCacheFs::open (#9730) 2026-08-04 11:16:59 +02:00
Luis Cossío b5a12f1234 [AI + manual] cleanup (#9727)
- get rid of `from_file` abstractions
- renames:
  - `CachedFs` to be the implementation
  - `CachedReadFs` to be the trait
2026-08-04 11:16:59 +02:00
Luis Cossío c5c46d4184 [DiskCache] refactor State into single enum (#9651)
* [AI + manual] refactor State into single enum

* use `let .. else`

* use explicit matches
2026-08-04 11:16:57 +02:00
09f74749b2 [UIO] Include file size in UniversalReadFileOps::list_files (#9675)
* [AI + manual] Include file size in `UniversalReadFileOps::list_files`

* Use dedicated `ListedFile` struct instead of `(PathBuf, u64)` pair

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

---------

Co-authored-by: generall <andrey@vasnetsov.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-04 11:16:57 +02:00
Luis Cossío e609e9372a expect unnecessary wraps (#9676) 2026-08-04 11:16:57 +02:00
Luis Cossío 889dd49f5e simplify DiskCache::read_whole (#9642)
Rely on the existing initialization logic when prefilling for
`read_whole`
2026-08-04 11:16:56 +02:00
Luis Cossío 135298abde Add s3_proxy.sh tool to simulate an S3 bucket locally (#9589) 2026-08-04 11:16:55 +02:00
Luis Cossío 6f42aec534 [UIO] Explicit Populate in R/O vector storages 2026-08-04 11:16:55 +02:00
Luis Cossío 2ed5a33f27 [DiskCache] Proper background populate on reopen (#9519)
* hold init_guard during reopen

* add `from` parameter to `OwnedReadPipeline::schedule_whole`

* add `OwnedReadPipeline::into_inner` to extract inner file

* implement proper background populate on `reopen`

* clippy

* coderabbit

* explicit owned uring pipeline destructuring

* refactor to join local and remote into one state

* @ffuugoo's nits
2026-08-04 11:16:54 +02:00
Luis Cossío c26d94e1c6 Add facet accuracy test (#9549)
* [AI] add facet accuracy test

* [manual] simplify

* [AI] delete and overwrite some points

* [AI] use immutable segments too

* lower zipf skew
2026-08-04 11:16:54 +02:00
Luis Cossíoandgenerall ce31866411 [UIO] Batched OnDiskMapIndex::for_values_map (#9505)
* override `for_values_map` for `OnDiskMapIndex`

* refactor and apply to filter as id iterator

* add comment

---------

Co-authored-by: generall <andrey@vasnetsov.com>
2026-08-04 11:16:53 +02:00
Luis Cossío 23c724408a [UIO] Batching for OnDiskMapIndex::values_iter (#9491) 2026-08-04 11:16:53 +02:00
c359fbb86e Perf improvements for facets (#9457)
* feat(facet): add sampling strategy for high-cardinality fields

For approximate facet queries the current per-segment implementation
walks every distinct value in the field index — O(unique_values_count)
even when the user asks for a tiny top-K. On UUID-style fields with
millions of unique values this dominates the request latency even
after #9208 capped the cross-shard payload.

This commit adds a parallel sampling strategy that runs in O(limit)
instead of O(unique_values_count):

1. Phase 1 — iterative novelty sampling. Stream point IDs in random
   order (filtered if requested), look up each point's value via the
   facet index, and collect distinct values into a candidate set until
   `limit * 10` (min 1000) candidates have been gathered. Uses a
   batch size of 32 to amortise the inner `for_points_values` call,
   and bails out early after 128 consecutive empty batches when the
   long tail is too thin to keep finding novel values.

2. Phase 2 — exact-count post-pass. For each candidate value, compose
   `field == value` with the user filter and count via the payload
   index. This guarantees the returned counts are exact (matching the
   semantics of the full-scan path); only the *set* of returned values
   is approximate.

The two strategies live side by side; `SegmentReadView::approximate_facet`
picks between them per-request based on
`unique_values_count > limit * FACET_FULL_SCAN_FACTOR` (FACTOR = 4).
Below that, the existing scan path runs unchanged — it'd visit most of
the index either way, and the post-pass adds no value.

The Monte-Carlo simulation behind this design (see thread context for
Zipf-distributed fields with cardinality up to 10^5 in ~1000 samples,
and trivially-correct results on UUID-style fields where every value
has count 1.

Adds a new `unique_values_count` method on the `FacetIndex` trait
(implemented for `MapIndex`, `ReadOnlyMapIndex`, `BoolIndex`,
`ReadOnlyBoolIndex`, and the `FacetIndexEnum` dispatcher) so the
strategy switch can run without touching the index.

Co-authored-by: Cursor <cursoragent@cursor.com>

* [AI] simplify, use single file

[AI] better selection of filtering approach

fmt

[AI] simplify, use single file

* manual simplification

* [AI] implement candidate-based lookups

[AI] 🧹

* precollect filter into bitmap

* avoid sampling with restrictive filter

* fix rebase + clippy

* refactor tests

* no duplicate values in map index

* polish comments

---------

Co-authored-by: root <111755117+qdrant-cloud-bot@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-04 11:16:53 +02:00
Luis Cossío dd5271bc6d Explicit Populate in payload storage (and ` (#9514)
`GridstoreReader`)

- Make the entrypoint of payload storage choose the `Populate` variant
- Mutable payload indexes now populate gridstore blockingly before using
  it to load
- Propagate `Populate` into `flags` module
- Add `UniversalRead::populate_auto` to know whether a backend chooses
  to populate or not when `Populate::Auto`
2026-08-04 11:16:53 +02:00
Luis Cossío 02eba0ac5a Add facets bench (#9456) 2026-08-04 11:16:51 +02:00
Luis Cossío 84ccdb37fc Standardize full text index (#9387)
* rename to OnDiskFullTextIndex

* rename module to `on_disk_text_index`

* rename to OnDiskPostings

* rename module to `on_disk_inverted_index`

* rename to OnDiskInvertedIndex

* remove `Box`ing

* get rid of `is_on_disk` field, propagate Populate

* add separate OnDisk and Immutable readonly variants

* rename in tests too

* nits
2026-08-04 11:16:49 +02:00
Luis Cossío 8beb3a08cf [UIO] Sorted offsets in LiveReload (#9385) 2026-08-04 11:16:49 +02:00
Luis Cossío e602e6a798 Standardize on-disk geo index & friends (#9339)
* rename GeoMapIndex -> GeoIndex

* rename on_disk module

* fmt

* remove `Box`ing

* add separate immutable and on-disk read-only variants

* propagate Populate, respect `on_disk`, remove `is_on_disk`

* rename to `new_immutable` and fix tests

* more geomap -> geo renaming

* more adhoc `open_*` suffixes
2026-08-04 11:16:48 +02:00
Luis Cossío c0fb2ed172 de-duplicate ingestion method for geo-index (#9322) 2026-08-04 11:16:47 +02:00
Luis Cossío 7f436572c1 chore: Refactor mutable map index inner (#9319)
* replace `ingest` with `add_many_to_map`

* rename to InMemoryMapIndex

* rename module

* fmt

* clippy

* don't index if there's no values
2026-08-04 11:16:47 +02:00
Luis Cossío c7f8bb2cea Standardize map index (#9305)
* rename to OnDiskMapIndex

* rename module

* remove `is_on_disk` field from `OnDiskMapIndex`

* add separate Immutable and OnDisk ReadOnlyMapIndex

* rename StoredPointToValues to OnDiskPointToValues

* rename module to `on_disk_point_to_values`

* rename to `build_from_iter` and add `populate` arg

* fix test
2026-08-04 11:16:47 +02:00
Luis Cossío 0329fb7a86 Rename indexes variants (#9281) + fixup for #9294
* rename IndexSelector variants

* rename MapIndex::Mmap to MapIndex::OnDisk

* rename NumericIndex::Mmap to NumericIndex::OnDisk

* rename BoolIndex::Mmap to BoolIndex::Mutable

* rename FullTextIndex::Mmap to FullTextIndex::OnDisk

* rename GeoMapIndex::Storage to GeoMapIndex::OnDisk

Also rename a few functions and test variants

* Standardize numeric index (#9294)

* remove `UniversalNumericIndex.is_on_disk` field

* have separate Immutable and OnDisk variants for ReadOnlyNumericIndexInner

* rename UniversalNumericIndex -> OnDiskNumericIndex

* rename module to `on_disk_numeric_index`

* actually load as immutable if on_disk=false

* fixup! Standardize numeric index (#9294)

Make builder also return proper Immutable variant

* review nits
2026-08-04 11:16:47 +02:00
Luis Cossío d1d30b9efb [UIO] More read_json_via (#9270) 2026-06-03 14:57:11 +02:00
Luis Cossío 8c6f6a267c [UIO] Open UniversalMapIndex with S::Fs (#9256)
* open UniversalMapIndex with `S::Fs`

* Not found is Ok(None)
2026-06-03 14:55:06 +02:00
Luis Cossío 12cc1e50e6 [UIO] Universal load numeric index (#9234)
* rename module, use read_via

* propagate S to `UniversalNumericIndex<T, S>`
2026-06-03 14:53:11 +02:00
Luis Cossío 623063282a [UIO] Load GraphLinks with any backend (#9214)
* load with any universal io

* Don't populate with sequential advice

* use appropriate fs for `exists`

* use `read_whole_via` more

* TODO

* clear ram cache after read_whole
2026-06-03 14:53:11 +02:00
d808afb086 [#9159 alternative] Oversample with approx facet (#9208)
* Oversample with approx facet

* return early for limit=0

* Document distributed limit flow on Collection::facet

Add an ASCII diagram showing how the facet limit is oversampled once on
the entry node and how peer nodes enter via facet_internal without
re-oversampling.

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

---------

Co-authored-by: generall <andrey@vasnetsov.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 14:53:11 +02:00
Luis Cossío 44446128a2 [DiskCache tests] Fix reopen on windows (#9189)
* reactivate tests

* try to release remote
2026-06-03 14:49:46 +02:00
Luis Cossío f3f197ecb3 [DiskCache] use and impl schedule_whole (#9185)
* impl schedule_whole for DiskCache

* use schedule_whole for background prefill

* ignore unit arg in bench

* bundle R bounds into trait
2026-06-03 14:49:46 +02:00
Luis Cossío b6708cf510 flush BitmaskGaps before reopening (#9186) 2026-06-03 14:49:45 +02:00
Luis Cossío 24823310d8 [UIO] Use UniversalRead::reopen (#9127)
* use universal reopen

* remove unused fs args
2026-06-03 14:47:42 +02:00
Luis Cossío b82f627a60 bump ecow to 0.3.0 (#9182) 2026-06-03 14:47:42 +02:00
Luis Cossío 117a2a93fd [UIO] Impl DiskCache::reopen (#9143) 2026-06-03 14:47:01 +02:00
Luis Cossío 2843d89443 [UIO] Introduce OwnedReadPipeline::schedule_whole (#9128) 2026-06-03 14:47:01 +02:00
Luis Cossío 6a1c6047f7 Merge pull request #9097
* impl async prefill

* use enum in `init_lock`

* init from scratch if channel closes

* cfg tests with prefill

* dirty update to pipe2

* cleanup & deduplicate code

* fix prefill pipeline

* self nits

* fix rebase

* separate file for `LocalState`

* more doc comments

* use acq/rel ordering

* handle empty byte range conversion

* todo reopen

* review: remove redundant functions

* review: T: + Send

* fmt
2026-06-03 14:47:00 +02:00
Luis Cossío 6d65fddb74 [UIO] Introduce UniversalRead::reopen (#9123)
* add `UniversalRead::reopen` fn

* thx coderabbit
2026-06-03 14:45:25 +02:00
37e9400c2e Introduce simple disk cache (#8792)
* [AI + manual] initial impl

[AI] read_batch which actually batches

manual nits

[AI] better handling of local and remote paths

manual refactor, respect open options

don't delete local file

dumbify read_batch

we want to refactor it anyway

simplify

rename to `DiskCache` in `simple_disk_cache` module

* refactor to use always use ReadPipeline

pass meta to remote pipeline

* nits

* run tests for more Remotes

* fix no more <T> in UniversalRead

* fmt

* chore(deps): unify roaring as workspace dep, move duplicate to dev-deps

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

---------

Co-authored-by: generall <andrey@vasnetsov.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 10:46:31 +02:00
Luis Cossío 6d96cbcb53 [UIO] Generic quantized storage (#9059)
* use generic in QuantizedMmapStorage

* rename to QuantizedStorage

* be explicit about S

* rename builder to `QuantizedStorageBuilder`

* rename file to `quantized_storage.rs`
2026-05-22 10:43:06 +02:00
Luis Cossío 075dc0d247 accept Cow in quantization_preprocess (#9061) 2026-05-22 10:42:58 +02:00
Luis Cossío 6598a47933 make quantized_vector_size method static (#9060) 2026-05-22 10:42:50 +02:00
Luis Cossío 35ea107d02 Propagate S to ImmutableIdTracker (#9047) 2026-05-22 10:41:09 +02:00
4ef1417443 [UIO] Introduce Populate enum (#8946)
* introduce `Populate` enum

* fix: apply CodeRabbit auto-fixes

Fixed 6 file(s) based on 6 unresolved review comments.

Co-authored-by: CodeRabbit <noreply@coderabbit.ai>

* fix coderabbit fix

* Use from rather than into

* add `Auto` variant

* fix rebase

* fix rebase again

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
Co-authored-by: timvisee <tim@visee.me>
2026-05-22 10:40:33 +02:00
Luis Cossío ade4ace555 uring only for linux (#9046) 2026-05-22 10:40:22 +02:00
Luis Cossío a0de218411 [UIO] Additional UniversalRead/Write usage simplifications (#8961)
* bytemuck::Pod already implies 'static

* no supertraits

* regions gaps with TypedStorage

* StoredBitSlice with TypedStorage

* bytemuck for PostingsHeader

* bytemuck for TrackerHeader

* bytemuck for MmapRange

* bytemuck for stored_point_to_values::Header
2026-05-22 10:31:05 +02:00
9d1bfc0f99 [gridstore, UIO] Simplify OptionalPointer (#8945)
* derive bytemuck::Pod on `OptionalPointer`, get rid of legacy transmute

* remove one layer of Option in `get_raw`

* keep `UniversalRead<u8>` bound, do bytemuck conversion at call site (#8953)

Drop the `+ UniversalRead<OptionalPointer>` / `+ UniversalWrite<OptionalPointer>`
trait bounds added in the previous two commits. Reads and writes of
`OptionalPointer` and `TrackerHeader` now go through `bytemuck::from_bytes` /
`bytemuck::bytes_of` on the existing `u8` byte slices instead.

This keeps the unsafe-transmute removal but avoids the turbofish noise
(`UniversalWrite::<u8>::flusher(&self.storage)`, `<S as UniversalRead<u8>>::open`,
…) that two `T`s on `S` forced everywhere.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* extra refactoring after #8955

* explicit zeroed instantiation

---------

Co-authored-by: Andrey Vasnetsov <andrey@vasnetsov.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 10:30:38 +02:00
Luis Cossío abe1b39a83 use UniversalReadFileOps::exists for status file (#8929) 2026-05-08 16:14:23 +02:00
5eac2d5092 [UIO] Genericize ChunkedVectors.status (#8928)
* genericize ChunkedVectors.status, remove `Sized` bound

* check exists with `UniversalReadFileOps`

* Inline UioChunkedVectors bound, drop the alias (#8952)

The empty trait + blanket impl was a stable-Rust trait-alias workaround
that hid a fairly short bound (UniversalWrite<T> + UniversalWrite<Status>
+ Send + 'static) at the cost of an indirection readers had to mentally
unwind. Spelling it out at the three sites that need it is shorter overall
and immediately tells the reader what is required.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Andrey Vasnetsov <andrey@vasnetsov.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 16:14:22 +02:00
Luis Cossío 62898d69e4 match exhaustively on match variants (#8947) 2026-05-08 13:48:30 +02:00
Luis Cossío b674ad495b [UIO] Generic Roaring/Bitvec flags (#8896)
* propagate to BufferedDynamicFlags

* use duplicate for tests

* propagate to Bitvec/Roaring flags

* propagate to RoaringFlags

* fixup! propagate to BufferedDynamicFlags

* propagate to BitvecFlags
2026-05-08 13:48:27 +02:00