6147 Commits

Author SHA1 Message Date
Tim Visée
db8fa43fcb Bump version to 1.18.3 (#9885) v1.18.3 2026-07-17 12:59:11 +02:00
Tim Visée
0da8d5881e Fix resharding, on queries filter shards on all shard selectors (#9882)
* Fix resharding, on queries filter shards on all shard selectors

* Add failing consensus test: search during resharding with shard keys (#9880)

Reproduces a known bug: after resharding is initialized on a custom
sharded collection with a shard key, searches (with and without the
shard key selector) fail with "does not have enough active replicas",
because the new resharding shard is included in reads before it has
an active replica.

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

* Exempt explicit shard id selection from resharding read filter

Explicit shard id selection is only used by internal per-shard
operations (local shard API, internal gRPC reads), including the
resharding driver reading back migrated points from the new shard.
These must reach the resharding shard before it becomes visible to
user-facing selectors, and filtering them also made per-shard reads
return silently empty results on peers lagging on hashring commits.

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

* Explicitly set resharding filtering per match branch

---------

Co-authored-by: Andrey Vasnetsov <andrey@vasnetsov.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 12:31:49 +02:00
Tim Visée
44ad62f8cd Bump version to 1.18.2 (#9290)
* Bump version to 1.18.2

* Update missed cherry picks
v1.18.2
2026-06-03 17:09:21 +02:00
Tim Visée
2db8e1c7ff Fix OOB heap read crash with malicious snapshot (#9268)
* Validate quantized u8 data size on load

* Validate other quantization types

* Add test

* Use fs_err
2026-06-03 15:18:39 +02:00
Daniel Boros
0f0c27d1e6 feat: wire up read only field indexes (#9224)
* feat: wire up read only field indexes

* feat: add missing indexes

* feat: add try_from impl for TextIndexParams

* refactor: unify read-only field index open and drop RocksDB storage

Merge ReadOnlyFieldIndex::open_gridstore/open_mmap into a single `open`
that picks the appendable vs immutable path from the stored
FullPayloadIndexType::storage_type. The choice is modeled as a ReadMode
(Appendable/Immutable) rather than a concrete backend, since the
read-only stack is generic over UniversalRead and mmap is now just one
implementation of it.

Also remove the unsupported RocksDb variant from payload_config's
StorageType and its now-dead match arms.

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

* fix: linter

* fix: linter

---------

Co-authored-by: generall <andrey@vasnetsov.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 15:18:39 +02:00
Daniel Boros
b0dc310c27 feat: avoid payload-index rebuild (#9138)
* feat: add swap on_disk methods

* fix: linter

* feat: add swap in disk for indexes

* fix: linter

* fix: std::fs::read -> fs_err::read

* feat: add on_disk swap bool & null indexes

* refactor: use macro instead boilerplate

* feat: add better error propagation

* fix: review comments -> remove try_swap.. and mut lock

* fix: linter

* chore: remove try_open_mmap

* fix: match -> ?

* review nits

* fix: seperate edge case for appendable segment

---------

Co-authored-by: Luis Cossío <luis.cossio@outlook.com>
2026-06-03 14:58:27 +02:00
Tim Visée
6a4280ea58 Hotfix: prevent optimizer infinite loop with deferred points and multi vectors (#9285)
* Always optimize deferred points

* Add test (#9288)

* Hotfix: test indexing of deferred multivector under indeixing threshold (#9286)

---------

Co-authored-by: Arnaud Gourlay <arnaud.gourlay@gmail.com>
2026-06-03 14:58:27 +02:00
Andrey Vasnetsov
cc8d1696a9 feat/readonly map index with live reload (#9264)
* live reload function for map index

* fmt
2026-06-03 14:58:26 +02:00
Arnaud Gourlay
0905ef6575 Delete unused HNSW build cache (#9283)
* Delete unused HNSW build cache

* remove seahash dep
2026-06-03 14:58:26 +02:00
dependabot[bot]
831cf980dc build(deps): bump lukemathwalker/cargo-chef (#9272)
Bumps lukemathwalker/cargo-chef from latest-rust-1.95.0-bookworm to latest-rust-1.96.0-bookworm.

---
updated-dependencies:
- dependency-name: lukemathwalker/cargo-chef
  dependency-version: latest-rust-1.96.0-bookworm
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-03 14:58:26 +02:00
dependabot[bot]
111de40297 build(deps): bump actions/checkout from 6.0.2 to 6.0.3 (#9273)
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.2 to 6.0.3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](de0fac2e45...df4cb1c069)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.3
  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-06-03 14:58:26 +02:00
dependabot[bot]
5abe64aef7 build(deps): bump cc from 1.2.62 to 1.2.63 (#9278)
Bumps [cc](https://github.com/rust-lang/cc-rs) from 1.2.62 to 1.2.63.
- [Release notes](https://github.com/rust-lang/cc-rs/releases)
- [Changelog](https://github.com/rust-lang/cc-rs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.62...cc-v1.2.63)

---
updated-dependencies:
- dependency-name: cc
  dependency-version: 1.2.63
  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-06-03 14:58:26 +02:00
dependabot[bot]
2c97c0b9e0 build(deps): bump zerocopy from 0.8.48 to 0.8.50 (#9280)
Bumps [zerocopy](https://github.com/google/zerocopy) from 0.8.48 to 0.8.50.
- [Release notes](https://github.com/google/zerocopy/releases)
- [Changelog](https://github.com/google/zerocopy/blob/main/CHANGELOG.md)
- [Commits](https://github.com/google/zerocopy/compare/v0.8.48...v0.8.50)

---
updated-dependencies:
- dependency-name: zerocopy
  dependency-version: 0.8.50
  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-06-03 14:58:25 +02:00
Andrey Vasnetsov
634d6c5bee Log slow operations during local shard WAL recovery (#9282)
* feat: log slow operations during local shard WAL recovery

Warn when applying a single WAL operation during recovery of a local
shard takes longer than 30s, including the operation type (e.g.
PointOperation::UpsertPoints) so slow recoveries can be diagnosed.

Adds CollectionUpdateOperations::label() returning a human-readable
label including the inner variant.

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

* refactor: reuse audit operation names for slow WAL recovery logging

Move the canonical operation-name mapping next to the
CollectionUpdateOperations definition in the shard crate as an inherent
operation_name() method, and have the audit AuditableOperation impl
delegate to it. The slow WAL recovery warning now reuses these same
names (e.g. upsert_points) instead of a duplicated label mapping.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 14:58:25 +02:00
dependabot[bot]
05d87a6d50 build(deps): bump pyroscope from 2.0.4 to 2.0.6 (#9275)
Bumps [pyroscope](https://github.com/grafana/pyroscope-rs) from 2.0.4 to 2.0.6.
- [Release notes](https://github.com/grafana/pyroscope-rs/releases)
- [Changelog](https://github.com/grafana/pyroscope-rs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/grafana/pyroscope-rs/compare/lib-2.0.4...lib-2.0.6)

---
updated-dependencies:
- dependency-name: pyroscope
  dependency-version: 2.0.6
  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-06-03 14:58:25 +02:00
Marcelo Machuca
38dc80edb2 fix(api): return formula default validation errors (#9271) 2026-06-03 14:58:25 +02:00
dependabot[bot]
1205c7e34f build(deps): bump log from 0.4.30 to 0.4.31 (#9279)
Bumps [log](https://github.com/rust-lang/log) from 0.4.30 to 0.4.31.
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.30...0.4.31)

---
updated-dependencies:
- dependency-name: log
  dependency-version: 0.4.31
  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-06-03 14:58:25 +02:00
dependabot[bot]
2684730f70 build(deps): bump serial_test from 3.4.0 to 3.5.0 (#9276)
Bumps [serial_test](https://github.com/palfrey/serial_test) from 3.4.0 to 3.5.0.
- [Release notes](https://github.com/palfrey/serial_test/releases)
- [Commits](https://github.com/palfrey/serial_test/compare/v3.4.0...v3.5.0)

---
updated-dependencies:
- dependency-name: serial_test
  dependency-version: 3.5.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-06-03 14:58:25 +02:00
dependabot[bot]
612d7a3341 build(deps): bump uuid from 1.23.1 to 1.23.2 (#9277)
Bumps [uuid](https://github.com/uuid-rs/uuid) from 1.23.1 to 1.23.2.
- [Release notes](https://github.com/uuid-rs/uuid/releases)
- [Commits](https://github.com/uuid-rs/uuid/compare/v1.23.1...v1.23.2)

---
updated-dependencies:
- dependency-name: uuid
  dependency-version: 1.23.2
  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-06-03 14:58:24 +02:00
Daniel Boros
e795d40cfb feat: add open for read only numeric index (#9213)
* feat: add open for read only numeric index

* feat: add parent open_*, get_mutability_type for ReadOnlyNumericIndex

* fix: naming and generic fs

* feat: detect not-found via error in numeric index open

* fix: hw counter write -> read
2026-06-03 14:57:11 +02:00
Luis Cossío
d1d30b9efb [UIO] More read_json_via (#9270) 2026-06-03 14:57:11 +02:00
Daniel Boros
0e244662d3 feat: detect not-found via error in bool, null and geo index open (#9265)
* feat: detect not-found via error in bool, null and geo index open

* fix: error on inconsistent storage in bool and null index open

* fix: hw counter write -> read
2026-06-03 14:57:11 +02:00
Daniel Boros
7c4140f2a0 feat: add open for read only fulltext index (#9222)
* feat: add open methods for fulltext

* fix: naming

* feat: detect not-found via error in fulltext index open

* fix: hw counter write -> read
2026-06-03 14:57:11 +02:00
Arnaud Gourlay
0e2ed93172 Remove unecesssary Clippy allows (#9267) 2026-06-03 14:57:10 +02:00
Daniel Boros
2365d1a3ef feat: add_ref_payload_index_io_read_counter (#9266) 2026-06-03 14:57:10 +02:00
Daniel Boros
0033a867cf feat: remove generic in Roaring flag & move into open method (#9236)
* feat: add readonly null index open

* feat: add open method

* refactor: split variants into modules

* chore: remove duplicated impl

* feat: remove generic in Roaring flag move into open method
2026-06-03 14:57:10 +02:00
qdrant-cloud-bot
71574f8d89 test(consensus): de-flake replicate_points_stream_transfer_updates (#9263)
The test relied on concurrent background upserts adding *new* matching
points to the destination shard during the streamed transfer, asserting
the destination count was strictly greater than the original snapshot.

Whether new matching points land in the destination before the transfer
completes is timing-dependent (especially under pytest-xdist load), so
the strict `>` assertion is racy and occasionally fails with equal
counts (e.g. `assert 5031 > 5031`).

Relax to `>=` and keep the strict `dest == src` consistency check, which
is the actual invariant being verified.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 14:57:10 +02:00
Andrey Vasnetsov
7900faf789 Add timeout to streaming shard snapshot writer (#9239)
* Add block level timeout to snapshot stream writer

* Add tooling to exercise streaming snapshot stalls

- tests/manual/slow_snapshot_download.py: slowly / partially download a
  streaming shard snapshot from a URL to exercise sender-side backpressure.
  Supports hold / rst / fin / blackhole termination to simulate a stalled,
  killed, or offline (network-partitioned) consumer against a remote node.
  Stdlib only; read-only against the target.
- tests/consensus_tests/test_streaming_snapshot_receiver_kill.py: throttled
  receiver killed mid-flight + a second receiver, to observe whether the
  sender releases the SegmentHolder lock and recovers.

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

---------

Co-authored-by: timvisee <tim@visee.me>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 14:57:10 +02:00
Daniel Boros
4bf579b1b4 feat: add open for read only map index (#9223)
* feat: add open methods for map

* fix: review comments

* feat: detect not-found via error instead of path.exists in map index open

Replace the path.exists() pre-check in the read-only appendable map index
open path with error-driven detection through ok_not_found(). Generalize
OkNotFound over an IsNotFound trait, implemented for io::Error, mmap::Error,
UniversalIoError, and GridstoreError so a NotFound surfacing through any
layer (including mmap's inner io::Error) maps to Ok(None).

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

* review: revert internal error conversion

---------

Co-authored-by: generall <andrey@vasnetsov.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 14:57:09 +02:00
Andrey Vasnetsov
4efb78545d feat: append-only mutation mode for mutable segments (#9245)
* feat: append-only mutation mode for mutable segments

Adds an opt-in per-segment switch so every mutating op tombstones the old
internal_id and writes the result at a fresh one, instead of overwriting
the underlying vector storage / payload row / field index in place.
Intended for S3-backed storages that prefer pure appends — the on-disk
structures are never rewritten.

Runtime flag, not persisted: `Segment::append_only_mutations` lives next
to `appendable_flag` on the struct, defaults to false in the builder,
and `Segment::is_append_only()` only returns true when the segment is
also appendable (clone-and-tombstone needs growable storages).

The core helper is `Segment::clone_and_mutate_point`: snapshots the
point at `old_id` into owned `NamedVectors` + `Payload`, hands them to
a closure for op-specific in-memory modification, allocates `new_id`,
writes all configured vector storages and the payload at `new_id`, and
repoints the id tracker via `set_link` (which auto-tombstones `old_id`
in the deleted bitslice). Stale slots and field-index postings keyed
by `old_id` are filtered by readers via
`filter_deferred_and_deleted` and reclaimed at optimization.

The routing policy concentrates in one new dispatcher,
`Segment::handle_point_mutate`, which takes two closures (in-place and
snapshot-mutate) and picks the path based on `is_append_only()`. All
six existing entry points in `SegmentEntry` (`upsert_point`'s
existing-pid branch, `update_vectors`, `delete_vector`,
`set_full_payload`, `set_payload`, `delete_payload`, `clear_payload`)
collapse to this dispatcher; `upsert_point`'s insert path stays on the
non-mutating fast path.

`delete_point` gets a tombstone-only variant
(`delete_point_tombstone_only`) that only flips the id-tracker deleted
bit, leaving the payload row and field-index postings at `internal_id`
in place. The append-only path skips the in-place `clear_payload` call
entirely, matching the "id tracker is the only thing we write" intent.

`NamedVectors::into_owned()` is added so the snapshot closure can take
ownership of a borrowed input wholesale.

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

* feat(debug): QDRANT_APPEND_ONLY_MUTATIONS env override

Debug-only escape hatch so newly built segments default to append-only
mutation routing when QDRANT_APPEND_ONLY_MUTATIONS=1 (or true/yes) is
set in the environment. Lets us run the existing test suites against
the append-only path without wiring a collection-level config knob
first.

Release builds compile this out — the function is a const false.
Logs a single warn-level message the first time the override fires.

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

* fix(append-only): skip deleted vectors on snapshot, always write payload

Two correctness fixes to clone_and_mutate_point uncovered by running
the openapi suite with QDRANT_APPEND_ONLY_MUTATIONS=1:

1. The snapshot loop read every named vector via get_vector_opt, which
   returns slot bytes even when the per-vector deletion bit is set.
   For sparse-only points (or any point that had update_vector(_, None)
   applied) this materialised the default-zero vector as if it were
   real data, wrote it at new_id, and never re-tombstoned the slot —
   so dense search started scoring phantom vectors. Now we check
   is_deleted_vector(old_id) and skip the read, letting the writer
   loop emit update_vector(new_id, None) and re-mark the slot deleted.

2. The payload write was skipped when the snapshot ended up empty.
   That dropped two side effects the field indexes rely on:
   payload_storage.overwrite(new_id, empty), and the remove_point
   fan-out across configured field indexes that bumps each index's
   total_point_count to cover new_id. Without the bump the null index
   doesn't see new_id, so is_empty / is_null filters lose the point
   even though its mapping is live in the id tracker. The skip was an
   optimisation, not a contract; remove it so the field indexes get
   the same registration they'd get from the standard clear_payload
   path.

Also collapses the debug env override to an inline cfg!()-gated
check at the struct literal — the helper with one-time logging and
multi-value matching was disproportionate for a debug-only escape
hatch.

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

* make test_move_points_to_copy_on_write work with copy-on-write

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-03 14:57:09 +02:00
Arnaud Gourlay
481066a9ad Remove unused dependencies (#9262) 2026-06-03 14:57:09 +02:00
Daniel Boros
b16bd255f5 feat: readonly geo index open (#9211)
* feat: add geo index open method

* fix: test

* feat: add open_mmap and open_gridstore methods
2026-06-03 14:57:09 +02:00
qdrant-cloud-bot
90ac5e8589 test: reproduce MatchAny(any=[]) strict-mode rejection on integer index (#9260)
* test: reproduce MatchAny(any=[]) strict-mode rejection on integer index

Adds an openapi test that demonstrates the strict-mode bug where
`match: {"any": []}` on an integer-indexed payload field is rejected with:

    Bad request: Index required but not found for "<field>" of one of
    the following types: [keyword, uuid]

even though the field is indexed as integer. Root cause is that an empty
`any` list deserializes as `AnyVariants::Strings(empty)` (untagged enum;
Strings variant listed first), and strict mode infers a keyword/uuid
index requirement from the variant tag — ignoring that the list is
empty (i.e. a no-op condition).

The test covers:
- Baseline no-op semantics with and without indexes (passes today).
- Reproduction under strict mode for `must`, `must_not`, and a
  FormulaQuery FieldCondition (currently failing; will pass once
  empty `any`/`except` no longer requires an index).

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

* fix: treat empty MatchAny/MatchExcept as no-op in strict-mode index check

An empty `match: {"any": []}` (or `{"except": []}`) is a no-op: `any: []`
matches nothing and `except: []` excludes nothing, regardless of the
field's data type. Because an empty list cannot carry type information it
deserializes as the keyword `AnyVariants::Strings(empty)` variant, which
previously caused strict mode to demand a keyword/uuid index and reject
the request with:

    Index required but not found for "<field>" of one of the following
    types: [keyword, uuid]

even on a field indexed as integer.

Fix:
- `infer_index_from_any_variants` returns no required index for an empty
  variant set.
- `Extractor::update_from_condition` skips a condition whose required
  index set is empty, so a no-op condition is never reported as needing
  an index (this also covers the FormulaQuery condition path).

This makes the openapi reproduction in test_match_any_empty.py pass.

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

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 14:57:09 +02:00
Andrey Vasnetsov
bfa9c182d9 test(consensus): batch initial upsert in snapshot-transfer missing-point test (#9261)
The initial 20k-point insert was sent as a single HTTP request (no
batch_size), saturating all cores long enough to starve the consensus
thread (cascading leader elections) and to exceed the 2000ms per-shard
update healthcheck deadline, returning a flaky 408 before the test's
actual snapshot-transfer logic even started.

Batch the insert into 1k-point requests, matching the convention used
by every other large initial upsert in the suite.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 14:57:09 +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
tellet-q
df0466538d ci: bump test_many_collections timeout to avoid flaky CI timeouts (#9253) 2026-06-03 14:55:06 +02:00
Tim Visée
20ea023e7d Fix REST auth whitelist, resolve route before authorizing (#9254)
* Don't whitelist endpoints on user provided path, but on endpoint pattern

* Add test

* Update comment
2026-06-03 14:55:06 +02:00
Arnaud Gourlay
32e94799c3 Cleanup cancelled optimized segment (#9217)
* Cleanup cancelled optimized segment

* crash safety comment
2026-06-03 14:55:06 +02:00
Jojii
cde6e82b1e Attempt to fix flaky test_hw_metrics_cancellation test (#9233)
* Fix flaky test_hw_metrics_cancellation test

* Speedup test duration
2026-06-03 14:55:06 +02:00
qdrant-cloud-bot
0476a28c02 Bump edge packages (Python + Rust) to 0.7.2 (#9252)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 14:55:05 +02:00
qdrant-cloud-bot
d75d805533 Strict mode: add max_disk_usage_percent (#9212)
* Strict mode: add `max_disk_usage_percent`

Mirrors `max_resident_memory_percent`: rejects disk-consuming update ops
(upsert, set/overwrite payload, update vectors) when the filesystem hosting
Qdrant storage is filled above the configured percentage. Delete-style ops
remain allowed so callers can free disk.

Disk usage is sampled via `statvfs` and TTL-cached for 5s (same cadence as
the resident-memory reader) so high-RPS request paths don't hammer the
syscall. Reader is keyed by path in `common::disk_usage` and returns `None`
on stat failure — callers (the strict-mode check) treat `None` as "skip",
matching the memory-check behaviour.

Plumbing follows the existing pattern: field on `StrictModeConfig` (+
output/diff/Hash), gRPC proto field `22`, validation 1..=100, REST/proto
conversions, and the hook into `check_strict_mode_toc_batch` alongside the
memory check (both guarded by `any_consumes_memory`).

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

* Fix CI: Windows disk_usage test + e2e WAL config

- `missing_path_returns_none` panicked on Windows because
  `GetDiskFreeSpaceEx` succeeds for non-existent paths (it resolves up to
  the containing drive). Relax the assertion to "must not panic; if a
  value is returned it must be well-formed". The contract we care about
  (None on failure) is platform-defined, not something we can portably
  force.

- e2e test failed at batch 0 with "WAL buffer size exceeds available disk
  space": Qdrant's existing per-shard `DiskUsageWatcher` enforces
  `free >= 2 * wal_capacity_mb` and the default WAL didn't fit in the
  50 MB tmpfs. Bump tmpfs to 200 MB and shrink `wal_capacity_mb` to 1 MB
  (same pattern as `test_low_disk.py`) so our strict-mode gate is the
  one that fires, not the WAL pre-check. Raise the gate threshold to
  50% to match the larger headroom.

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

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 14:54:03 +02:00
qdrant-cloud-bot
5ad6b139af Add Rust edge example: add-named-vector (#9230)
* Add Rust edge example: add-named-vector

Port `lib/edge/python/examples/add-named-vector.py` to Rust under
`lib/edge/publish/examples/src/bin/add-named-vector.rs`.

Re-export `VectorNameOperations`, `CreateVectorName`, `DeleteVectorName`,
`VectorNameConfig`, `DenseVectorConfig`, and `SparseVectorConfig` from
`qdrant_edge` so the public Rust API can create/delete named vectors
without reaching into the internal `shard` crate.

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

* fmt

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

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 14:54:03 +02:00
Rin
b5aa3a25d6 Update quinn-proto to 0.11.14 (RUSTSEC-2026-0037) (#9232) 2026-06-03 14:54:03 +02:00
Andrey Vasnetsov
790ce443a9 Batch initial load in resharding abort-crash test to fix flakiness (#9240)
The test fired a single 20k-point `wait=true` upsert into a fresh
3-shard x 2-replica collection. Under CI load that one op keeps a replica
busy past the hardcoded 2s inter-node health-check (transport_channel_pool
HEALTH_CHECK_TIMEOUT), so the coordinator fails the forward with a transient
"Healthcheck timeout 2000ms exceeded" 408 before resharding even starts.

It's the only resharding test doing a 20k single-shot upsert (others do
~1k), which is why it flakes and they don't. Batch the load at 1000 points
so each op stays well under the health-check window, matching the
proven-stable pattern. Data and transfer behavior are unchanged.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 14:53:55 +02:00
Andrey Vasnetsov
739a984da5 Fix stale snapshot transfer recovery comment on failure/cancellation (#9237)
* Fix stale snapshot transfer recovery comment on failure/cancellation

The transfer status "comment" prefers the destination-side recovery
progress over the sender task status. Recovery progress was tracked in
`active_recoveries` and only removed via `finish_shard_recovery`, which
was called on the success path only. If `recover_shard_snapshot` returned
early - clearing the local shard, downloading the snapshot, checksum
mismatch, or cancellation (the future is spawned with
`spawn_cancel_on_drop`) - the entry leaked.

A leaked entry keeps reporting its last stage with an ever-growing
elapsed time (computed live from a fixed `Instant`), so subsequent
transfers for the same shard show stale timings until the next recovery
overwrites the entry or the node restarts.

Replace the manual start/finish pair with an RAII `ShardRecoveryGuard`
that removes the progress entry on drop, covering every exit path
including early returns and cancellation. The guard only removes its own
entry (checked via `Arc::ptr_eq`) so it never clobbers a newer recovery.

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

* Extract recovery tracking into dedicated recovery_guard module

Move ShardRecoveryGuard out of the large shard_holder/mod.rs into a
dedicated recovery_guard.rs, and replace the verbose
`Arc<Mutex<HashMap<ShardId, Arc<Mutex<RecoveryProgress>>>>>` type with a
dedicated `ActiveRecoveries` struct that encapsulates start/comment/
set_stage/remove operations. No behavior change.

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

* Bind recovery stage updates to the guard instance, not shard id

`ActiveRecoveries::remove` is pointer-guarded so an unwinding recovery
cannot delete a newer recovery's entry, but stage updates still resolved
the progress entry by shard id. If recovery A is still unwinding after
recovery B started for the same shard, A's late `set_stage` would mutate
B's progress and resurrect incorrect transfer comments.

Thread the guard's own progress handle (`RecoveryProgressHandle`) through
`recover_shard_snapshot_impl` -> `Collection::restore_shard_snapshot` ->
`ShardHolder::restore_shard_snapshot`, so the Unpacking/Restoring stages
(and Downloading, via `ShardRecoveryGuard::set_stage`) update that exact
recovery's progress. Direct API recoveries, which are not tracked as
transfer-side recoveries, pass `None`.

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

* Allow too_many_arguments on recover_shard_snapshot_impl

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: root <111755117+qdrant-cloud-bot@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 14:53:55 +02:00
Andrey Vasnetsov
102c0dd3ed fix(tests): wait for abort target to apply transfer-start before aborting (#9238)
test_resharding_down_abort_converges_when_killed_mid_abort fired the
fire-and-forget abort_transfer request at alive_uri after waiting only for
the *victim* to apply the transfer-start. When alive_uri lagged in replicating
that consensus entry, the abort handler's local check_transfer_exists returned
404 (swallowed by the fire-and-forget thread); the transfer then completed
naturally, resharding was never aborted, and _victim_in_window never opened,
timing out after 30s.

Wait until both alive_uri (the abort target that gates the 404) and the victim
have applied the transfer-start before firing the abort.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 14:53:55 +02:00
Daniel Boros
62b7fad0f9 feat: readonly bool index open (#9200)
* feat: add readonly null index open

* feat: add open method

* refactor: split variants into modules

* feat: add get_mutability_type to ReadOnlyBoolIndex

* chore: remove duplicated impl

* fix: remove populate

* chore: add immutable bool index docs
2026-06-03 14:53:12 +02:00
Daniel Boros
ada6e82157 feat: add readonly null index open (#9197)
* feat: add readonly null index open

* feat: add get_mutability_type to ReadOnlyNullIndex

* fix: pr reviews

* simpler phantomdata

---------

Co-authored-by: Luis Cossío <luis.cossio@outlook.com>
2026-06-03 14:53:11 +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
xzfc
779a66f783 Migrate InvertedIndexCompressedMmap to UIO (#9144)
* Migrate InvertedIndexCompressedMmap to UniversalRead

* Rehaul search_scratch.rs (was scores_memory_pool.rs)

- Use `typed_arena::Arena` instead of `bumpalo::Bump`.
  Reason: bump don't drop.
  Drawback: `Arena::new()` is not free, and `Arena::clear()` doesn't
  exist; but this commit has workarounds.
- Use names that make more sense.

* Misc fixes

* InvertedIndexCompressedMmap: explicit S type parameter
2026-06-03 14:53:11 +02:00