Commit Graph

416 Commits

Author SHA1 Message Date
Roman Titov
bcdf2d2577 Replace MmapUniversal with MmapFile (#8505) 2026-03-25 19:37:45 +01:00
Luis Cossío
3af9349e83 impl populate for CachedSlice (#8462)
* impl populate

* nits
2026-03-25 13:59:02 -03:00
Roman Titov
a7550f893f Universal MmapFile that can read any T (#8493) 2026-03-25 16:57:53 +01:00
Luis Cossío
7f106ce397 impl UniversalRead for local disk cache (#8368)
* checkout from `ssd-cache-wip`

* impl UniversalRead for CachedFile

* use `CachedSlice` for `UniversalRead` implementation

* block exhaustion test

* fix block exhaustion error

* fix alignment on owned path

* merge CachedFile into CachedSlice

* propagate io errors

* self-review

- log warning if long wait
- Option for RequestState
- clippy

* handle coderabbit's comments

* fix num read bytes check

* don't allocate twice for cache misses

* not for windows, sorry

* no more dead code

* use atomic for file id

* update cache controller description

* rebase fixes

* Self: Sized

* Use imports

* Remove with_global (unused)

* Add TODO

* move common::disk_cache -> universal_io::disk_cache

---------

Co-authored-by: xzfc <xzfcpw@gmail.com>
2026-03-25 11:39:26 -03:00
qdrant-cloud-bot
401cb7f12c Add #[must_use] to RAII guard and task handle types (#8499)
* Add `#[must_use]` to RAII guard and task handle types

These types rely on being held for a scope — dropping them immediately is
almost always a bug:

- `StoppingGuard`: sets `is_stopped` flag on drop
- `UpdateGuard`: decrements update counter on drop
- `UpdatesGuard`: releases mutex preventing concurrent updates on drop
- `ClockGuard`: marks clock as inactive on drop
- `IsAliveGuard`: releases liveness lock on drop
- `ScopeTrackerGuard`: decrements scope counter on drop
- `CancellableAsyncTaskHandle`: detaches task on drop
- `StoppableTaskHandle`: may abort task on drop (AbortOnDropHandle)

Made-with: Cursor

* Remove redundant function-level #[must_use] now covered by type

With ScopeTrackerGuard marked #[must_use] at the type level, the
function-level attributes on measure_scope(), measure(),
track_create_snapshot_request(), and count_snapshot_creation() are
redundant and trigger clippy::double_must_use.

Made-with: Cursor

---------

Co-authored-by: Cursor Agent <agent@cursor.com>
2026-03-25 14:51:38 +01:00
Excellencedev
3230f1d952 Implement Per collection metrics for Promethus (#8214)
* Implement Per-Collection Prometheus Metrics

* Update config/config.yaml

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update tests/per_collection_metrics_test.sh

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update tests/per_collection_metrics_test.sh

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* fix ci

* comment

* Update tests/per_collection_metrics_test.sh

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* adress revew

* fix: linter

* refactor: cardinality limit, anonymize fix, cleanup

- Add max_per_collection_metrics config (default 256) to bound
  per-collection label cardinality
- Fix anonymize: strip per_collection_responses entirely instead of
  leaking hashed collection names
- Move CollectionName into requests_telemetry, remove
  telemetry_context.rs
- Add unit tests for cardinality limits
- Add integration test assertions for default mode
- Regenerate OpenAPI spec
- Document why internal gRPC doesn't attach CollectionName

* avoid cloning

* fix: address pr reviews

* fix: linter

* fix: linter

* feat: enforce {name} in actix api

* chore: remove empty line

* feat: add actix pre-commit hook

* feat: potential enforce tonic collection name

* feat: use proc_macro instead

* feat: use collection_name instead of name

* feat: add tonic telemetry tests

* fix: linter

* fix: linter

* chore: remove unneccessary clone

* fix: use collection_name everywhere

* fix: python tests and openapi

* chore: simplify collection_name tests

* actix collection_name enforcign: use relative path in test + avoid grep

* fix: remove macro

* chore: add some comment to telemetry_wrapper

* fix: clippy

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Daniel Boros <dancixx@gmail.com>
Co-authored-by: generall <andrey@vasnetsov.com>
2026-03-23 15:19:02 +01:00
Andrey Vasnetsov
454f11b515 Fix io_uring reads: use byte offset (#8470)
* [AI] use byte offset instead of element range

* [AI] change ElementOffset -> ByteOffset
2026-03-20 21:03:23 +01:00
Roman Titov
04e9bbc4b2 Dense vector storage based on universal I/O IoUringFile (#8452) 2026-03-20 11:16:12 +01:00
xzfc
7bd5759105 Touch-up UIO traits (#8457)
* UIO traits: require Sized

I don't think we ever going to add unsized implementation. So, lets,
require it on the whole trait rather than on individual methods.

* Move `AccessPattern` to the `common` package

Was: segment::vector_storage::vector_storage_base::AccessPattern
Now: common::generic_consts::AccessPattern

* UIO traits: replace `bool` with `AccessPattern`
2026-03-19 20:28:21 +00:00
Luis Cossío
ad8334928d Use UniversalIo for MmapBitSlice (#8339)
* feat(universal_io): add storage-agnostic BitSliceStorage with read/write support`

Add BitSliceStorage<S> generic over
UniversalRead<u64>/UniversalWrite<u64>,
providing bit-level read and write operations over u64-element storage.

Read operations (S: UniversalRead<u64>):
- read_all: returns Cow<BitSlice> (zero-copy for mmap, owned for others)
- get_bit: single bit read via u64 element fetch
- read_bit_range: arbitrary bit range read

Write operations (S: UniversalWrite<u64>):
- set_bit / replace_bit: single bit write (skips write if unchanged)
- write_bit_range: arbitrary bit range write from BitSlice source
- fill_bit_range: fill range with a value
- set_bits_batch: batch individual bit updates coalesced by element
- flusher: flush underlying storage

* refactor: replace MmapBitSlice with BitSliceStorage in MmapBitSliceBufferedUpdateWrapper

Migrate all deleted-flag bitslice storage from the legacy MmapBitSlice
(Deref-based mmap wrapper) to BitSliceStorage<MmapUniversal<u64>>
(storage-agnostic universal IO backend).

Updated consumers:
- MmapMapIndex
- MmapNumericIndex
- MmapGeoMapIndex
- MmapInvertedIndex (fulltext)
- ImmutableIdTracker
- Benchmark

Additionally:
- Add BitSliceStorage::create(path, num_bits) to encapsulate
  file creation + sizing + open (eliminates duplicated size math
  across 5 call sites)
- Add MmapBitSliceStorage type alias for
BitSliceStorage<MmapUniversal<u64>>
- Add count_ones() convenience method
- Use set_bits_batch() in wrapper flusher and all build paths
  instead of per-bit set_bit() loops (coalesces u64 read-modify-writes)
- Fix silent error swallowing in wrapper get(): log + debug_assert
  on I/O errors instead of .ok().flatten()
- Remove dead bitmap_mmap_size function from immutable_id_tracker

* use bitvec's approach for offset

* less api

* misc improvements

* refactor write method

* move to segment crate

* handle creation of file outside of StoredBitSlice logic

* fix codespell

* document bitwise calculations

* coalesce more updates into a single write, batch all writes

* use native `extend_from_bitslice` instead of iterating.

* clarity refactor

* clippyyy

* use `u64` as BitStore everywhere

* assume iterator is sorted

* only use chunk_by for generating runs

* fix update wrapper flusher

* review fixes

* larger set bits batch test

* remove reintroduced file

* oops, fix new test

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: generall <andrey@vasnetsov.com>
2026-03-19 12:26:47 -03:00
qdrant-cloud-bot
09b3ff00cb refactor(edge): split EdgeShard load into new() and load() (#8326)
* refactor(edge): split EdgeShard load into new() and load()

- new(path, config): create edge shard only when path has no existing segments
- load(path, config?): load from existing files; config optional (load from
  edge_config.json or infer from segments)
- Helpers: has_existing_segments, ensure_dirs_and_open_wal, resolve_initial_config,
  load_segments, ensure_appendable_segment
- Python: EdgeShard.create_new(path, config); __init__ still calls load()
- Examples use new() for creation and load(path, None) for reopen
- Error instead of panic on config mismatch; explicit load/create in Python
- fix: use OptimizerThresholds.deferred_internal_id for dev compatibility

Made-with: Cursor

* rollback threshold changes

* fix(edge): address dancixx review comments

- Persist inferred config in load() when edge_config.json does not exist
  (SaveOnDisk::new only saves when file exists)
- Python: add #[new] delegating to load() for backward compatibility
- qdrant_edge.pyi: Optional return types for deleted_threshold,
  vacuum_min_vector_number, default_segment_number; add __init__ doc

Made-with: Cursor

* Revert "fix(edge): address dancixx review comments"

This reverts commit 370e21a6c74c41210e1658f89814395cb86ed81c.

* fix: optional returns

* fix: save config it is not exist

* fix: save data on disk always

* fix: python examples

* chore: remove edge.close()

* fix: wal lock

* fix: rename of EdgeShardConfig -> EdgeConfig

---------

Co-authored-by: Cursor Agent <agent@cursor.com>
Co-authored-by: Andrey Vasnetsov <andrey@vasnetsov.com>
Co-authored-by: Daniel Boros <dancixx@gmail.com>
2026-03-18 15:25:01 +01:00
Roman Titov
a3d4570a12 Better IoUringFile errors (#8418) 2026-03-17 10:13:42 +01:00
qdrant-cloud-bot
6ddb60e5b5 Log individual payload index load time per field (#8390)
* Log individual payload index load time per field

Add per-field timing to `load_all_fields()` in `StructPayloadIndex`,
using the existing `LOAD_TIMING_LOG_TARGET` infrastructure. Each
payload field index that takes >= 5ms to load now gets its own log
line, making it easy to identify slow-loading fields.

Made-with: Cursor

* Move `log_load_timing` to common module and use everywhere

Move the `log_load_timing` helper from `segment_constructor_base` into
`common::defaults` so it can be reused across crates. Convert all raw
`log::debug!(target: LOAD_TIMING_LOG_TARGET, ...)` call sites to use
the shared function, giving consistent formatting and min-duration
suppression everywhere.

Made-with: Cursor

---------

Co-authored-by: Cursor Agent <agent@cursor.com>
2026-03-13 18:38:04 +01:00
qdrant-cloud-bot
2406dd9562 Suppress noisy zero-duration load timing logs (#8388)
Add a `log_load_timing` helper that checks elapsed time against a 5ms
threshold before logging. Sub-component loads faster than this round to
"0.00s" in the {:.2} format and are pure noise. The per-segment "total
loaded" line remains unconditional.

Made-with: Cursor

Co-authored-by: Cursor Agent <agent@cursor.com>
2026-03-13 12:47:19 +01:00
Andrey Vasnetsov
07cff7387f log cpu utilization (#8342)
* [AI] init implementaions of CPU usage reatio logging

* [AI] better function to access ratio

* fmt

* Update slow_requests_log.rs

* more logging
2026-03-13 08:44:13 +01:00
Roman Titov
a3786cfacc Improvements for IoUringFile (#8300)
* Improvements for `IoUringFile`

- handle read and write requests more explicitly
- assert against partial reads/writes

* Implement `read_multi`/`write_multi`

* Implement `populate` and `clear_ram_cache`

* Rename internal structures as `IoUringSomething`

* [ai] Make `UniversalRead`/`UniversalWrite` impl generic over `T`

* Handle `io_uring` initialization error

* Check that `io_uring` is initialized and supported, when opening `IoUringFile`

* fixup! Check that `io_uring` is initialized and supported, when opening `IoUringFile`

Fix typo

* test for reading u64 from file with uring

* fmt

* clippy

* fix(io_uring): allocate Vec<MaybeUninit<T>> for reads to fix alignment for T (#8353)

* fix(io_uring): allocate Vec<MaybeUninit<T>> for reads to fix alignment for T

Refactor IoUringState::read to take generic T and item_offset/items_length
instead of byte_offset/byte_length. Allocate Vec<MaybeUninit<T>> so the
kernel writes into correctly aligned memory, then convert to Vec<T> in
finalize. Fixes bytemuck::cast_vec alignment panic for types like u64.

Made-with: Cursor

* refactor(io_uring): only make read/write methods generic, not state or runtime

- IoUringState and IoUringRuntime no longer generic over T
- read<T>() allocates Vec<MaybeUninit<T>>, transmutes to Vec<MaybeUninit<u8>> for storage
- finalize returns ReadBuffer; callers use .into_vec::<T>() to get Vec<T>
- Write path unchanged (callers pass bytes via bytemuck::cast_slice)

Made-with: Cursor

* Cleanup

* review n1

* review n2

* fmt

---------

Co-authored-by: Cursor Agent <agent@cursor.com>
Co-authored-by: Roman Titov <ffuugoo@users.noreply.github.com>
Co-authored-by: Andrey Vasnetsov <andrey@vasnetsov.com>

* use AHash instead of just hash

* add ahash to deps

---------

Co-authored-by: Andrey Vasnetsov <andrey@vasnetsov.com>
Co-authored-by: qdrant-cloud-bot <111755117+qdrant-cloud-bot@users.noreply.github.com>
Co-authored-by: Cursor Agent <agent@cursor.com>
2026-03-12 22:40:59 +01:00
qdrant-cloud-bot
f90be3fc10 Add optional extended load timing logging for storage components (#8363)
* Add debug-level load timing logs for storage components

Add log::debug! calls with target "qdrant::load_timing" that report
how long each storage component takes to load during startup:

- Total shard load time (including WAL replay)
- Total segment load time + load_state
- Per-segment: payload_storage, id_tracker, payload_index
- Per-vector: vector_storage (dense/sparse), quantized_vectors,
  vector_index (dense/sparse)

These logs are at DEBUG level so they are silent by default. Enable
selectively via log_level config or env, for example:

  log_level: "INFO,qdrant::load_timing=debug"

No config fields, no global flags, no function signature changes.

Made-with: Cursor

* Use seconds with 10ms resolution for load timing logs, extract log target constant

Switch from `{:.2}ms` with `as_secs_f64() * 1000.0` to `{:.2}s` with
`as_secs_f64()` for cleaner output. Move the "qdrant::load_timing"
log target string to a LOAD_TIMING_LOG_TARGET constant in each file.

Made-with: Cursor

* Move LOAD_TIMING_LOG_TARGET to common::defaults

Single definition shared by segment and collection crates, avoiding
duplication of the log target string.

Made-with: Cursor

---------

Co-authored-by: Cursor Agent <agent@cursor.com>
2026-03-12 14:42:18 +01:00
Andrey Vasnetsov
64cc539f9b strictly read only mmap (#8350)
* [AI] Implement generic to make pure read-only universal io mmap version

* fmt

* [AI + manual nits] /simplify (#8351)

---------

Co-authored-by: Luis Cossío <luis.cossio@outlook.com>
2026-03-12 13:18:15 +01:00
Andrey Vasnetsov
317a8d3929 openning read-only mmap should not require creation and append mode (#8372) 2026-03-12 12:21:59 +01:00
Jojii
e7e5f4cc4f Filter deferred points: facets (#8313)
* Filter deferred: facets

* Add test (facet)

* Review remark

* Review remarks

* Fix unique_values returning values even if occurring only in deferred
points

* Clippy

* Rename Filter=>Exclude
2026-03-11 17:31:53 +01:00
Jojii
f91e2179d5 Disable deferred point filtering in resharding (#8310)
* Add ignore_deferred flag for internal scroll API

* Ignore deferred points in ProxyShard::update

* Use enum instead of bool as type in function parameters

* Use consistent parameter name

* fmt

* Apply suggestions from code review

Co-authored-by: Tim Visée <tim+github@visee.me>

* More explicit naming

* Add DeferredBehavior to `count()` and disable filtering in stream_records

* Don't filter `retrieve()` everywhere

* Use consistent filter behavior in read operations

* Add TODO for ignored parameter in remote_shard

* Rebase fixes

---------

Co-authored-by: timvisee <tim@visee.me>
Co-authored-by: Tim Visée <tim+github@visee.me>
2026-03-11 16:13:26 +01:00
Andrey Vasnetsov
b14e0be4ed Restrict snaphsot recovery to snapshots directory, hide file contents (#8341)
* restrict recovery from snapshot folder

* fmt

* clippy

* Extend test, path must be inside snapshots directory

* In release builds, hide detailed tar error message to not leak contents

* Change from bad request to forbidden

* fix(auth_tests): place snapshot in peer snapshots dir for recover_collection_snapshot

The recover from snapshot API now requires file:// paths to be inside the
configured snapshots directory. Write the test snapshot into the peer's
snapshots directory instead of a temp file so the request is allowed.

Made-with: Cursor

---------

Co-authored-by: timvisee <tim@visee.me>
Co-authored-by: Tim Visée <tim+github@visee.me>
Co-authored-by: Cursor Agent <agent@cursor.com>
2026-03-10 17:13:32 +01:00
qdrant-cloud-bot
d9702ef30f chore: remove unused dependencies reported by cargo-machete (#8347)
- gridstore: remove memmap2
- common: remove ahash
- edge: remove serde_json from dependencies
- examples (lib/edge/publish/examples): remove anyhow

Made-with: Cursor

Co-authored-by: Cursor Agent <agent@cursor.com>
2026-03-10 11:05:30 +01:00
Luis Cossío
c7eadf49de Refactor PointToValues to use UniversalRead (#8298)
* refactor PointToValues to use UniversalRead

It includes a significant change to `MmapValue` trait to be able to
handle Cow reads, instead of just references.

* fix str parsing

* fix incorrect path

* use fallible casting

* clippy

* No eager allocation

this also makes it so that borrowed strs can keep happening :heart-eyes:

* Lifetime cleanup

---------

Co-authored-by: Roman Titov <ffuugoo@users.noreply.github.com>
2026-03-06 16:11:13 -03:00
Roman Titov
d53c712c50 IoUringFile based on universal I/O interface (#8292) 2026-03-05 18:19:37 +01:00
Andrey Vasnetsov
7083e7f2b6 gridstore live reload (#8287)
* [manual] live reload functions for gridstore-read

* [AI] tests for life reload

* fmt

* Address CodeRabbit review (PR 8287)

- gridstore/mod: acquire pages read lock once in files() loop
- universal_io/mmap: use fs_err::exists() to propagate IO errors
- pages: fix live_reload last_page_id underflow when pages is empty

Made-with: Cursor

* review changes

* thx coderabbit

---------

Co-authored-by: Cursor Agent <agent@cursor.com>
Co-authored-by: Luis Cossío <luis.cossio@outlook.com>
2026-03-04 18:30:21 +01:00
Andrey Vasnetsov
77b296d579 storage-backend dependent file listing (#8286) 2026-03-04 14:18:58 +01:00
dependabot[bot]
dda856a4ec build(deps): bump tango-bench from 0.6.0 to 0.7.2 (#8272)
* build(deps): bump tango-bench from 0.6.0 to 0.7.2

Bumps [tango-bench](https://github.com/bazhenov/tango) from 0.6.0 to 0.7.2.
- [Release notes](https://github.com/bazhenov/tango/releases)
- [Commits](https://github.com/bazhenov/tango/compare/v0.6.0...v0.7.2)

---
updated-dependencies:
- dependency-name: tango-bench
  dependency-version: 0.7.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Fix benchmark compilation error

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: timvisee <tim@visee.me>
2026-03-03 14:50:50 +01:00
Andrey Vasnetsov
b6c1987038 MultiFile Universal UI in gridstore pages (#8256)
* [manual] implement Pages for read-only

* Simpler `ReadMulti`/`WriteMulti` interface (#8263)

* [manual] Dumbify `ReadMulti`/`WriteMulti` interface

* fixup! [manual] Dumbify `ReadMulti`/`WriteMulti` interface

🤷‍♀️

* fixup! [manual] Dumbify `ReadMulti`/`WriteMulti` interface

Remove `VecMultiUniversalIo`

* [manual] review fix + silplify json load option

* [AI] split read and write + revome *Multi traits

* [AI] implement write for pages

* [AI] integrate pages into gridstore

---------

Co-authored-by: Roman Titov <ffuugoo@users.noreply.github.com>
2026-03-03 14:50:44 +01:00
Andrey Vasnetsov
9b344197a5 feat(universal_io): MultiUniversalRead trait and VecMultiUniversalRead implementation (#8255)
* feat(universal_io): add MultiUniversalRead trait and VecMultiUniversalRead impl

- Add SourceId, MultiUniversalRead<T> trait with read_batch_multi, source_len,
  populate, clear_ram_cache (latter two with default no-op).
- Add UniversalIoError::InvalidSourceId for invalid source id in batch reads.
- Add VecMultiUniversalRead<T, S>: minimal implementation over Vec<S: UniversalRead<T>>
  with attach(source) -> SourceId for adding sources at runtime.
- Add test vec_multi_universal_read_batch_and_attach using MmapUniversal.
- Handle InvalidSourceId in segment OperationError From<UniversalIoError>.

Implements the interface and minimal mmap-based implementation from
docs/design/multi-file-universal-io-plan.md (multi-source universal I/O).

Made-with: Cursor

* refactor(universal_io): move multi-source interface to separate file, drop plan from PR

- Add universal_io/multi_universal_read.rs with SourceId, MultiUniversalRead,
  VecMultiUniversalRead and test; re-export from mod.rs.
- Remove docs/design/multi-file-universal-io-plan.md from the branch.

Made-with: Cursor

* refactor(universal_io): require populate/clear_ram_cache; add new, attach, len, is_empty to trait

- MultiUniversalRead: remove default impls for populate() and clear_ram_cache();
  they are now required.
- Add to trait: new(), len(), is_empty() (default), attach() (default Err).
- Introduce associated type Source for attach; add AttachUnsupported<T>
  placeholder for impls that do not support dynamic attach.
- VecMultiUniversalRead: type Source = S; implement all trait methods.
- Re-export AttachUnsupported from universal_io.

Made-with: Cursor

* refactor(universal_io): remove AttachUnsupported; require attach for all impls

- Drop AttachUnsupported placeholder type and its UniversalRead/Send impls.
- Make attach() a required method on MultiUniversalRead (no default).
- Doc: all implementations must support attaching sources dynamically.
- Remove AttachUnsupported from re-exports.

Made-with: Cursor

* refactor(universal_io): attach by path, new(options), split vec impls, add MultiUniversalWrite

MultiUniversalRead:
- Remove type Source; attach(path, options) opens by path and returns SourceId.
- new(options: OpenOptions) for creating an empty multi-source view.
- Move VecMultiUniversalRead to vec_multi_universal_read.rs.

MultiUniversalWrite (new):
- Trait: new(options), len(), is_empty(), attach(path, options),
  write_batch_multi((SourceId, offset, data)...), source_len, flusher(),
  populate(), clear_ram_cache().
- VecMultiUniversalWrite in vec_multi_universal_write.rs; flusher()
  runs all source flushers.

Re-export MultiUniversalWrite, VecMultiUniversalWrite from universal_io.

Made-with: Cursor

* universal_io: MultiUniversalWrite extends MultiUniversalRead

Make MultiUniversalWrite<T>: MultiUniversalRead<T> like UniversalWrite
extends UniversalRead. Remove duplicated methods (new, len, is_empty,
attach, source_len, populate, clear_ram_cache) from the write trait;
keep only write_batch_multi and flusher. VecMultiUniversalWrite now
impl MultiUniversalRead and MultiUniversalWrite separately.

Made-with: Cursor

* [manual] final fixes

---------

Co-authored-by: Cursor Agent <agent@cursor.com>
2026-03-02 18:19:56 +01:00
Andrey Vasnetsov
82c49df611 feat(universal_io): add read_whole and read_json_via for config files (#8251)
* feat(universal_io): add read_whole and read_json_via for config files

- Extend UniversalRead with read_whole() for single-access whole-file read
- Default impl uses len() + read(0..len()); MmapUniversal overrides with one slice
- Add UniversalIoError::SerdeJson for JSON deserialization errors
- Add read_json_via<S,T>(path, options) in common::universal_io
- Gridstore: use read_json_via for config in read_config_and_tracker
- Segment: use read_json_via in ChunkedVectors::load_config, handle NotFound
- Segment: extend From<UniversalIoError> for OperationError with SerdeJson variant

Made-with: Cursor

* feat(universal_io): add UniversalIoError::NotFound for file-not-found

- Add NotFound { path } variant so callers can match without io::ErrorKind
- MmapUniversal::open maps io::ErrorKind::NotFound to NotFound { path }
- ChunkedVectors::load_config matches NotFound => Ok(None)
- OperationError From<UniversalIoError> handles NotFound

Made-with: Cursor

* style: apply cargo fmt

Made-with: Cursor

* fix(common): satisfy clippy explicit_auto_deref in read_json_via

Use &bytes instead of &*bytes; auto-deref handles Cow

Made-with: Cursor

---------

Co-authored-by: Cursor Agent <agent@cursor.com>
2026-03-02 16:38:17 +01:00
Luis Cossío
5403ea68ab Chunked vectors with UniversalWrite storage (#8233)
* use CowMultiVector as return type from storages

* add advice to OpenOptions

* Implement ChunkedVectors with generic storage

* rename ChunkedVectors->VolatileChunkedVectors and ChunkedMmapVectors-> ChunkedVectors

* propagate everywhere

fix tests

* [auto] rename BytesRange -> ElementsRange

* [auto] rename BytesOffset -> ElementOffset

* coderabbit nits

---------

Co-authored-by: generall <andrey@vasnetsov.com>
2026-02-27 12:47:15 -03:00
Luis Cossío
21890d2738 use Cow<'_, [T]> as return type in dense vector storages (#8221)
* use `Cow<'_, [T]>` as return type in vector storages

* extract complex type

* clippy

* improve maybe_uninit_fill_from

* don't commit to graphlinks yet
2026-02-26 13:30:21 -03:00
Andrey Vasnetsov
03c0fa2cf7 Universal IO: gridstore pages (#8223)
* [manunal] Gridstore page use universal IO

* fmt

* Apply review feedback for universal IO gridstore pages (#8230)

* Apply review feedback from PR #8223

- Use `super::Result` import in mmap.rs instead of fully-qualified `crate::universal_io::Result`
- Restructure ValuePointer destructuring in get_value and delete_value to
  first match Some(pointer), then destructure separately

* Replace Either<E, GridstoreError> with E: From<GridstoreError> in Gridstore::iter

Use a trait bound instead of Either to combine callback and gridstore
errors, allowing `?` to work directly on GridstoreError. This simplifies
callers by removing Either matching and io::Error conversion workarounds.

---------

Co-authored-by: qdrant-claw <qdrant-claw@users.noreply.github.com>

---------

Co-authored-by: qdrant-claw <qdrant-claw@users.noreply.github.com>
2026-02-25 19:46:00 +01:00
dependabot[bot]
18a7587d4b build(deps): bump rand_distr from 0.5.1 to 0.6.0 (#8148)
* build(deps): bump rand_distr from 0.5.1 to 0.6.0

Bumps [rand_distr](https://github.com/rust-random/rand_distr) from 0.5.1 to 0.6.0.
- [Release notes](https://github.com/rust-random/rand_distr/releases)
- [Changelog](https://github.com/rust-random/rand_distr/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-random/rand_distr/compare/0.5.1...0.6.0)

---
updated-dependencies:
- dependency-name: rand_distr
  dependency-version: 0.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Migrate main code base to rand 0.10

* Migrate tests

* Migrate benches

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: timvisee <tim@visee.me>
2026-02-25 14:15:04 +01:00
Luis Cossío
e28543a604 use UniversalRead in ImmutableDenseVectors (#8210)
* use UniversalRead in ImmutableDenseVectors

...renamed from MmapDenseVectors

* remove madvise arg

* async_raw_scorer
2026-02-24 13:55:16 -03:00
Andrey Vasnetsov
949376f240 universal io mmap (#8196)
* [manunal] WIP: universal mmap interface

* [AI + manual]  implement proper error handelling, with `thiserror` instead of `pub type
  UniversalIoError = std::io::Error;` and implement all `todo!()` in MmapUniversal

* [AI]  implement UniversalWrite for MmapUniversal

* review fix: error wording

* review nits

---------

Co-authored-by: Luis Cossío <luis.cossio@outlook.com>
2026-02-24 11:54:57 +01:00
dependabot[bot]
9e5a5f8f8a build(deps): bump serial_test from 3.3.1 to 3.4.0 (#8204)
Bumps [serial_test](https://github.com/palfrey/serial_test) from 3.3.1 to 3.4.0.
- [Release notes](https://github.com/palfrey/serial_test/releases)
- [Commits](https://github.com/palfrey/serial_test/compare/v3.3.1...v3.4.0)

---
updated-dependencies:
- dependency-name: serial_test
  dependency-version: 3.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-24 09:31:36 +01:00
Roman Titov
9cad4a4381 Universal I/O (#8183)
* WIP: `UniversalRead`/`UniversalWrite` traits

* fixup! WIP: `UniversalRead`/`UniversalWrite` traits

Relax `Sized` requirement on `UniversalRead`
2026-02-20 18:15:01 +01:00
Roman Titov
dca4ccc6d9 Use FsType::ExFat on non-Linux, so that Rust won't complain (#8186) 2026-02-20 11:43:29 +01:00
Tim Visée
c73cf450e8 Bump dev version to 1.17.1-dev (#8161) 2026-02-19 13:36:14 +01:00
xzfc
4cabb7fd8e Merge io and memory into common (#8155)
* Unify parking_lot/arc_lock feature

* Move lib/common/{io,memory}/* -> lib/common/common/*

- Mmap-related items are grouped into `common::mmap` sub-module:
  - `memory/src/chunked_utils.rs`      -> `common/src/mmap/chunked.rs`
  - `memory/src/madvise.rs`            -> `common/src/mmap/advice.rs`
  - `memory/src/mmap_ops.rs`           -> `common/src/mmap/ops.rs`
  - `memory/src/mmap_type_readonly.rs` -> `common/src/mmap/mmap_readonly.rs`
  - `memory/src/mmap_type.rs`          -> `common/src/mmap/mmap_rw.rs`
- Filesystem-related items are grouped into `common::fs` sub-module:
  - `common/src/fs.rs`          -> `common/src/fs/sync.rs`
  - `io/src/file_operations.rs` -> `common/src/fs/ops.rs`
  - `io/src/move_files.rs`      -> `common/src/fs/move.rs`
  - `io/src/safe_delete.rs`     -> `common/src/fs/safe_delete.rs`
  - `memory/src/checkfs.rs`     -> `common/src/fs/check.rs`
  - `memory/src/fadvise.rs`     -> `common/src/fs/fadvise.rs`
- Rest is moved straight into `common`:
  - `io/src/storage_version.rs` -> `common/src/storage_version.rs`

The old `io` and `memory` are now hollow crates that re-export items
from `common`. These hollow crates will be removed in next commits.

* Replace uses of `io` and `memory` with new paths in `common`

Since `io` and `memory` are just re-exports of `common`, these
replacements are no-op.

* Remove `io` and `memory` crates
2026-02-17 10:58:59 +01:00
Leo Henon
d0e2f9471b Fix flaky test_rate_more_per_minute (#8135)
Co-authored-by: lhenon999 <77656081+lhenon999@users.noreply.github.com>
2026-02-15 16:49:42 +01:00
Andrey Vasnetsov
171b7cffba distributed optimization info (#8120)
* [AI] initial implementation

* [AI] review fixes

* [manaul] some small fix

* [AI] fix issue about serializing optional fields

* upd schema

* [manual] review fixes

* [manual] only ask updatable replicas about optimizations
2026-02-13 13:58:17 +01:00
dependabot[bot]
c418fb61b3 build(deps): bump flate2 from 1.1.8 to 1.1.9 (#8085)
Bumps [flate2](https://github.com/rust-lang/flate2-rs) from 1.1.8 to 1.1.9.
- [Release notes](https://github.com/rust-lang/flate2-rs/releases)
- [Commits](https://github.com/rust-lang/flate2-rs/compare/1.1.8...1.1.9)

---
updated-dependencies:
- dependency-name: flate2
  dependency-version: 1.1.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-10 07:54:34 +01:00
xzfc
6bced54ca1 Chore: promote dependencies to workspace level (#8061)
* Promote `env_logger` to workspace dependency

* Promote `anyhow` to workspace dependency

* Promote `rmp-serde` to workspace dependency

* Promote `tinyvec` to workspace dependency

* Promote `async-trait` to workspace dependency

* Promote `url` to workspace dependency

* Promote `self_cell` to workspace dependency

* Promote `cc` to workspace dependency

* Promote `bitpacking` to workspace dependency
2026-02-05 16:02:28 +01:00
Lior.Chen
bc3f9a1c60 feat: Optimize the collection loading process during startup (#8053)
* feat: optimize the collection loading process during startup

* simplify config options

* chore: update config.yaml

* Remove global state

* Disallow zero values

* Remove intermediate map

* Rename ConcurrentLoadConfig to LoadConcurrencyConfig

* Flatten config

* Accept string inputs from environment variables

* Fix test compilation

---------

Co-authored-by: generall <andrey@vasnetsov.com>
Co-authored-by: chenliang <chenliang@chenliangdeMacBook-Air.local>
Co-authored-by: timvisee <tim@visee.me>
2026-02-04 17:35:33 +01:00
Andrey Vasnetsov
f403f6d00d Streaming snapshot unpacking (#8025)
* download tar

* compute sha256 for stream download

* wip: propagate unpacking into down to the logic, todo: validation

* unpacked snapshot validation

* Minor tweaks

* Fix typo

* validation during unpack

* cancellation token

* update docstring

* remove redundant dep

* Rearrange unpack functions

- Rename `safe_unpack.rs` into `tar_unpack.rs` so it would be listed
  near `tar_ext.rs` in IDEs.
- Replace calls like `ar = open_snapshot_archive(…); safe_unpack(ar, …);`
  with a single call to `tar_unpack_file(…)`.
- Put calls to `Archive::new(); Archive::set_overwrite(false);` inside
  `tar_unpack_reader` (was `safe_unpack`). So, now it is the only place
  that does `set_overwrite`.

* Let clippy complain if tar::Archive::unpack used

* Mock snapshot download URL

Instead of downloading from storage.googleapis.com every time the test
runs, put small snapshot file to the repo.

The snapshot file is created using this command:

    curl -s \
      https://storage.googleapis.com/qdrant-benchmark-snapshots/test-shard.snapshot \
    | tar \
      --delete segments/4ea958d8-0b64-4312-9a53-0cd857e93535.tar \
      --delete segments/65ac6276-8cca-4f5c-b767-9722190cee8b.tar \
      > lib/storage/src/content_manager/snapshots/test-shard.snapshot

File contents:

    $ tar tf lib/storage/src/content_manager/snapshots/test-shard.snapshot
    wal/
    wal/closed-255
    newest_clocks.json
    replica_state.json
    shard_config.json
    
    $ du -sh lib/storage/src/content_manager/snapshots/test-shard.snapshot
    12K	lib/storage/src/content_manager/snapshots/test-shard.snapshot
    
    $ sha256sum < lib/storage/src/content_manager/snapshots/test-shard.snapshot       
    5d94eac5c1ede3994a28bc406120046c37370d5d45b489a0d2252531b4e3e1f2  -

---------

Co-authored-by: timvisee <tim@visee.me>
Co-authored-by: xzfc <xzfcpw@gmail.com>
2026-02-03 12:19:18 +01:00
Ivan Boldyrev
984a9e8bd1 Fix gridstore Option unsoundness (#8011)
* Implement `Optional<T>` type

Define a type with the same presumed layout as `Option<T>`, but with defined behavior.

* Make `transmute_*` functions unsafe

The functions `memory::mmap_ops::transmute_*` are inherently unsafe, but
are not marked as are.  Their usage is documented, but it is not always clear
if the code is correct.

* Add `CsrHeader` to resolve another unsoundness

Tuples have no defined layout.
2026-02-03 17:57:09 +07:00
Ivan Pleshkov
9f57ecd6af reduce update signal size by optimizing hw (#8003) 2026-01-28 10:27:36 +01:00