658 Commits

Author SHA1 Message Date
Andrey Vasnetsov
eafabd267f docs: describe StartResharding fields in OpenAPI (#9946)
Add doc comments to `StartResharding` fields so the generated OpenAPI
spec explains what a user has to pass, and regenerate the spec.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 12:15:39 +02:00
Andrey Vasnetsov
59742bbb26 docs: fix collection metadata removal description (#9907)
Setting metadata to an empty object does not clear it: the update merges
key by key, so an empty object is a no-op (and over gRPC an empty map is
indistinguishable from an absent one). Per-key removal via null values is
the mechanism that was actually implemented and tested in #7123.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 18:41:31 +02:00
Andrey Vasnetsov
446d140c2d Slice filtering condition: sliced scroll / deterministic sampling (#9899)
* feat: slice filtering condition for sliced scroll and deterministic sampling

Add a `slice` filter condition selecting points where
`stable_hash(point_id) % total == index`. The hash is SipHash-2-4 with a
zero key over canonical id bytes (8 LE bytes for numeric ids, 16 RFC 4122
bytes for UUIDs) — a frozen public contract, independent of the internal
resharding ring hash, reproducible by clients to predict membership.

For a fixed `total`, slices are disjoint and cover all points, enabling
parallel scroll streams (ES sliced-scroll style) and reproducible sampling
that composes with any other filter condition.

- REST: `{"slice": {"total": N, "index": R}}`; gRPC: `SliceCondition` in
  the condition oneof (tag 8)
- Evaluated per point via id_tracker external-id lookup; no payload index
  needed; cardinality estimated as `points / total` with no primary clause
- `total >= 1` enforced by NonZeroU32 at parse time, `index < total` by
  validation in both REST and gRPC paths
- Hash contract locked by test vectors independently reproduced with a
  reference SipHash-2-4 implementation

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

* tests: minimal OpenAPI test for slice filter condition

Scrolls all slices of a fixed total over numeric + UUID ids asserting
disjointness and full coverage, checks must_not inversion, and pins the
two rejection paths (422 for index >= total, 400 for total = 0). Requests
and responses are validated against the regenerated OpenAPI spec by the
test harness.

Note: the spec cannot itself reject total = 0 client-side — the Condition
anyOf falls through to the permissive Filter schema, as with any invalid
condition — so rejection is asserted via the server response.

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 17:45:17 +02:00
Andrey Vasnetsov
a6ea303e4b Compact stored bitmask for on-disk field index deleted masks (#9871)
* Compact stored bitmask for on-disk field index deleted masks

Add StoredBitmask: a compact persisted bitmask written and read as a
whole. The payload is a roaring bitmap of whichever bit value is the
minority (mostly-0 and mostly-1 masks both stay tiny), falling back to
raw dense bits when roaring would not be smaller, so the file is never
larger than the dense representation. Files are replaced atomically via
UniversalWriteFileOps::atomic_save; there is no in-place mutation.

Use it for the write-once "no values" masks of the on-disk numeric,
geo, map and full-text indexes, replacing the raw dense bitslice files
sized at point_count/8 bytes regardless of content.

Writing the new format is gated by the compact_bitmask feature flag
(default off; enabled by `all` and `serverless_compatible`). Reading is
format-agnostic regardless of the flag: the compact deleted_mask.bin is
tried first, then the index-specific legacy file, so old segments stay
readable and flag-off builds produce byte-identical legacy files.

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

* Split save_bitmask into named helpers

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

* Regenerate OpenAPI spec for compact_bitmask feature flag

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

* Address review findings on stored bitmask

- Avoid u64 overflow in the payload bound check when opening a mask
  with a corrupted payload_len.
- Reject roaring payload positions beyond logical_len at read time,
  enforcing the BitmaskContent range contract for corrupted files.
- Remove the opposite-format mask file after a successful save, so a
  rebuild with a flipped compact_bitmask flag can't leave a stale
  compact file shadowing the fresh legacy one (or an orphaned legacy
  file next to a compact one).
- Make the compact-open numeric test tolerate builds that already
  wrote the compact format.

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 17:47:08 +02:00
Andrey Vasnetsov
1d4d6f02da Per-query IDF corpus for sparse vector search (#9661)
* Add per-query IDF corpus for sparse vector search

Let the caller choose, per query, which population sparse IDF statistics
are computed over. `params.idf` is either `"global"` (default, unchanged
behavior) or `{"corpus": <filter>}`, where the corpus filter is
independent of - and usually broader than - the retrieval filter.
Decoupling the two keeps the score scale stable when the retrieval
filter tightens: term importance is measured against a population the
user names, not against whatever subset the filter happens to select.

Design decisions:
- Corpus grammar is restricted to a conjunction (`must`) of `match`
  conditions on payload fields; loosening later is backward compatible.
- Strict mode validates the corpus filter like a read filter
  (unindexed fields rejected).
- `idf` on a vector without the IDF modifier is a validation error,
  never silently ignored.
- An empty corpus yields degenerate but corpus-scoped scores (smoothed
  IDF over N=0), never a fallback to global statistics - in multi-tenant
  collections a fallback would leak term statistics across tenants.

Implementation:
- QueryContext IDF stats are keyed by corpus, so one batch can mix
  requests with different corpora.
- Statistics come from the sparse index: df(term) is counted over the
  query terms' posting lists only, never by scanning stored vectors.
  Small corpora (under ~1/32 of the segment, by cardinality estimate)
  are kept as a sorted id list galloping through posting lists via
  skip_to; large ones as a dense membership mask filled streaming from
  the filtered-points iterator. A misestimated small corpus degrades
  into the mask.
- Exposed uniformly: REST (`params.idf`), gRPC (`IdfParams` message),
  edge python bindings; OpenAPI schema regenerated.

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

* Apply rustfmt

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

* Fix clippy manual_is_multiple_of in sparse IDF corpus test.

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

* Allow any filter as IDF corpus

Drop the must+match grammar restriction on the corpus filter. A
restriction enforced only as a validation step over the full Filter
type buys nothing; if a narrower corpus syntax is ever wanted, it
should be a dedicated API-level type instead.

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

* Fix build: add memory field to SparseIndexConfig in idf corpus test

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-07-14 11:16:47 +02:00
Andrey Vasnetsov
173939d636 fix memory enum openapi (#9735)
* make sure Memory object is generated as enum in OpenAPI

* fmt
2026-07-08 14:21:18 +02:00
Andrey Vasnetsov
ab0d3ecc62 Add unified memory: cold|cached|pinned placement parameter for collection components (#9684)
* Add unified `memory: cold|cached|pinned` placement parameter for collection components

Introduce a single `memory` parameter that controls how each collection
component's data is held in RAM, replacing the inconsistent zoo of
`on_disk` / `always_ram` / `on_disk_payload` flags:

- `cold`: not pre-loaded from disk, cached with usage
- `cached`: pre-populated into page cache on load, evictable under pressure
- `pinned`: materialized on heap, never evicted by cache pressure

The parameter is available on dense vectors, HNSW config, all quantization
configs, the sparse index, all payload field index types, and payload
storage (as a new `payload: { memory }` sub-object on collection params).
When set, it overrides the deprecated legacy flag; when unset, behavior is
unchanged. Legacy flags are marked deprecated (Rust + proto) but keep
working; conflicts are resolved in favor of `memory` with a warning.

New capabilities enabled by the tri-state model:
- HNSW graph links can be pinned (first production caller of the existing
  `GraphLinksResidency::Pinned`)
- sparse mmap index, quantized vectors and on-disk payload field indexes
  gain a `cached` tier (mmap + populate on open)

`pinned` is rejected by API validation for components without a heap
variant (dense vector storage, payload storage). Low-memory mode degrades
placements at load time via `Memory::clamp_to_low_memory`, matching the
existing `prefer_disk`/`skip_populate` behavior. Effective-placement
comparison in the config-mismatch optimizer avoids spurious rebuilds when
the same placement is expressed through the new parameter.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix gpu-gated tests for the new `memory` field

CI clippy runs with --all-features, which compiles the gpu-gated tests
that were missed locally: add the `memory` field to config literals and
allow deprecated placement params, same as in the rest of the tests.

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

* Add OpenAPI tests for memory placement, keep sparse config downgrade-clean

- OpenAPI tests: create/update collections with `memory` on every component,
  assert the parameters are echoed in collection info, assert legacy-only
  collections expose no new fields, and assert `pinned` is rejected (422)
  for dense vector storage and payload storage on both create and update.
- Persist only the explicitly requested `memory` parameter in
  `sparse_index_config.json` instead of the legacy-resolved placement, so
  configurations using only the deprecated `on_disk` flag keep byte-identical
  files that older Qdrant versions load without unknown fields.

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

* Validate collection meta ops at construction, not only in the API layer

The `memory: pinned` rejection for dense vectors and payload storage
lived in `Validate` impls on the internal request types, which only ran
through the REST actix extractor. gRPC validates just the proto message,
so a gRPC client could persist `pinned` where it is not supported and
have it silently treated as `cached`.

Run the derived validation in `CreateCollectionOperation::new` and
`UpdateCollectionOperation::new` instead: the constructors are the
common chokepoint for all API paths, before the operation is proposed
to consensus. This covers every validator on these types, not just the
`memory` checks, and keeps consensus-apply unaffected so mixed-version
clusters never reject already-committed operations.

`UpdateCollectionOperation::new` becomes fallible; `remove_replica` now
uses `new_empty` since it carries no user config. Regression tests drive
the gRPC conversion path and assert `InvalidArgument` for `pinned` on
create and update, with `cold`/`cached` accepted as a control.

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-07 14:32:51 +02:00
Andrey Vasnetsov
efab63d024 Add prefix matching option to keyword index (#9683)
* Add prefix matching option to keyword index

Introduce an opt-in `prefix` option for the keyword payload index and a
new `match: { "prefix": ... }` filter condition, enabling efficient
byte-wise prefix filtering over keyword values (e.g. URL prefixes,
web-ui value autocompletion via facet + prefix filter).

Index side: a new `prefix_index.bin` file stores a sorted, front-coded
key dictionary with a resident block index (cumulative counts per
block); it is an ordered view over the keys of `values_to_points.bin`
and stores no postings. Presence of the file signals prefix support at
load time, so legacy segments load unchanged and enabling the option
goes through the standard incompatible-schema rebuild. The mutable
variant keeps an in-RAM ordered key set (not persisted), the immutable
variant builds a sorted key vector at load, and the on-disk variant
reads the dictionary lazily (block index resident, 1-2 block reads per
prefix lookup; reader is generic over UniversalRead).

Query side: prefix conditions are served from the dictionary when
available (filter + cardinality estimation from per-block aggregates),
from the forward index as per-point checks, and degrade to the payload
full-scan fallback otherwise - same execution model as other match
conditions. Strict mode (`unindexed_filtering_*`) rejects prefix
queries on fields without a prefix-enabled keyword index via a new
KeywordPrefix capability.

HNSW payload blocks: prefix-enabled indexes additionally emit prefix
blocks for heavy branching trie nodes (single-child chains collapsed to
their longest common prefix, one block per distinct point set, emitted
largest-first) so filtered search with prefix conditions gets navigable
subgraphs without rebuilding the same subset repeatedly.

API: `prefix` flag on KeywordIndexParams (REST bool, gRPC empty
message for extensibility), `prefix` variant in the Match oneof, edge
python bindings, regenerated OpenAPI spec.

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

* Split prefix index into a dedicated module, fix clippy in tests

Reorganize the flat prefix_index.rs / prefix_read.rs into a
map_index/prefix_index/ module: format.rs (on-disk layout primitives),
writer.rs, reader.rs (PrefixIndex), map_read.rs (StrMapIndexPrefixRead
with per-variant impls) and tests.rs, with a file-format diagram and a
read-path walkthrough in the module docs. No logic changes.

Also fix clippy --all-targets complaints in test code: replace a
wildcard Match arm with an exhaustive list and a field-reassign-with-
default with a struct literal.

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

* Add OpenAPI test for prefix match and snapshot file-tracking test

- tests/openapi/test_prefix_match.py: index-less fallback, prefix index
  creation with schema echo, scroll/count parity against ground truth,
  facet + prefix filter (the autocompletion flow), strict-mode rejection
  without the prefix capability.
- test_prefix_index_file_tracking: `prefix_index.bin` is listed in
  `files()` / `immutable_files()` exactly when built with the option.

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

* Replace hand-rolled varint parsing with bytemuck Pod records

Per review: the prefix index format now uses fixed-size little-endian
Pod records (BlockEntry 24 B, KeyEntry 12 B, Header 40 B) written with
bytemuck::bytes_of and read back by copy via pod_read_unaligned — no
manual varint encode/decode, no alignment requirement, one shared
read_record helper. Costs ~9 bytes per key on disk versus LEB128; the
raw key bytes dominate dictionary size, so the simplification wins.

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

* Fetch the whole candidate block range with a single storage read

Candidate key blocks of a prefix lookup are contiguous in the file, so
enumerate them from one ranged read instead of one read per block; the
over-read versus the exact key range is bounded by the two boundary
blocks. Block decoding is split into a storage-free helper reused by
the per-block path of stats estimation.

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

* Align prefix payload blocks with the geo index granularity principle

Geo's large_hashes emits only the smallest geohash regions above the
threshold — a disjoint antichain, never a parent nested with its
children. Prefix payload blocks now follow the same rule: a heavy
collapsed trie node is emitted only if nothing heavy is nested inside
it, counting both deeper qualifying prefixes and single heavy values
(which already get their own exact-match blocks). Emitted blocks are
therefore mutually disjoint and disjoint from exact-value blocks; no
near-collection-sized ancestor subgraphs, no reliance on the HNSW
connectivity check to skip nested duplicates.

Implemented as a `covered` flag propagated through the existing
LCP-interval scan, still one O(total key bytes) pass.

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

* Document block wire format and unaligned-read rationale in decode_block

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 14:40:39 +02:00
qdrant-cloud-bot
7d9aef4c7c feat: add serverless_compatible feature flag (#9655)
* feat: add serverless feature flag

Introduce a composite `serverless` feature flag that automatically enables
`write_segment_manifest` and `append_only_mutations` during initialization.

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

* refactor: rename serverless flag to serverless_compatible

Rename the composite feature flag for clarity and consistency with
serverless deployment terminology.

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

* fix: satisfy clippy field_reassign_with_default in flag tests

Use struct update syntax instead of mutating Default::default() fields.

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

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-02 08:34:36 +02:00
qdrant-cloud-bot
12be3c3488 feat: move segment manifest next to segments/ directory (#9564)
Follow-up to #9530 and #9558.

The shard-level segment manifest was written inside the `segments/`
directory (`segments/manifest.json`). Older versions of Qdrant choke on
an unknown file inside `segments/`, so move it next to the directory as
`segments_manifest.json` instead.

- `SEGMENT_MANIFEST_FILE` is now `segments_manifest.json` and
  `segment_manifest_path()` points at the shard root.
- The manifest is added to `ShardDataFiles` so clear/move handle it.
- Snapshots write the manifest to the snapshot root (next to
  `segments/`), and restore/partial-snapshot loaders no longer need to
  skip it inside `segments/`.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-24 15:44:36 +02:00
Andrey Vasnetsov
852ca200b5 feat: feature-flagged segment manifest for LocalShard (#9530)
* feat: feature-flagged segment manifest for LocalShard

Add an on-disk segment manifest (`segments/manifest.json`) that lists a
shard's segments and their state, so out-of-process readers (e.g. a
read-only follower, possibly over object storage) can discover segments
without scanning the filesystem. Gated by the new `write_segment_manifest`
feature flag (off by default).

shard: define the structure + helpers (`SegmentsManifest`,
`SegmentManifestState`, `from_segment_holder`) in a new `segment_manifest`
module, plus the `SEGMENT_MANIFEST_FILE` constant and path helper. The
manifest is a flat `{ "<uuid>": "<state>" }` map; only `active` is written
today, with `under_construction`/`retiring` defined so the format can be
extended without breaking compatibility.

collection: LocalShard owns the writing logic. The manifest is persisted
via `SaveOnDisk<SegmentsManifest>`, initialized from the live segment set
on load/build and refreshed by the optimization worker whenever the
segment set changes (the helper re-derives from the holder and no-ops when
unchanged). No changes to `lib/shard/src/optimize.rs` internals.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Emj8TFxdtrf3K32eWhGgor

* feat: make append_only_mutations a proper feature flag

Replace the debug-only `QDRANT_APPEND_ONLY_MUTATIONS=1` env-var escape
hatch in segment construction with a `FeatureFlags::append_only_mutations`
flag, so it works in release builds and is configurable like the other
flags (config / `QDRANT__FEATURE_FLAGS__APPEND_ONLY_MUTATIONS`).

Deliberately left out of `FeatureFlags::all()`: it changes mutation
semantics and `all` is enabled in dev and e2e configs, so it stays
explicit opt-in.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Emj8TFxdtrf3K32eWhGgor

* upd openapi schema

* feat: register segments in manifest via must-use token, holder builder

Wire segment-manifest maintenance through the segment lifecycle so a
newly created segment is registered as soon as it exists on disk, and
construction can't silently skip it:

- build_segment now returns a #[must_use] NewSegmentToken carrying the
  new segment UUID; the lint forces callers to register or drop it.
- SegmentHolder owns the manifest and reconciles it on sync; new
  segments are registered ASAP (even before being added to the holder)
  via the token, before they can receive writes.
- SegmentHolderBuilder is the only way to obtain a shard's holder; its
  build() wires up the manifest, so it can't be forgotten. init/set
  manifest helpers are now private / test-only.
- Optimization registers the optimized segment before dropping the
  superseded segments' data; deletion is intentionally lenient.
- Document the consistency assumptions on SegmentsManifest: it is a
  superset-biased view that may list not-yet-finalized or already-deleted
  segments, which readers must tolerate.

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-22 12:51:57 +02:00
Tim Visée
b9154713d7 Fix empty min_should with non-zero min_count matching everything (#9401)
* Empty match any with non-zero min count matches nothing

* Update description

* Validate that min_count is greater than 0
2026-06-19 13:28:51 +02:00
qdrant-cloud-bot
8c487a17e8 feat(bm25): explicit Disabled stemmer; deprecate language: "none" hack (#9376)
* feat(bm25): add explicit Disabled stemmer; deprecate language hack

Adds a `Disabled` variant to `StemmingAlgorithm` (`stemmer: {"type": "none"}`)
so stemming can be turned off explicitly in both the main engine and Edge,
instead of relying on the undocumented `language: "none"` footgun that
silently disabled both stemming and stopwords.

For language-neutral text processing the supported setup is now:
1. set the stemmer to disabled, and
2. configure an empty stopword set.

The main engine still tolerates unsupported languages (so existing
`language: "none"` configs keep working on upgrade) but now logs a
deprecation warning pointing users to the explicit setup. Edge continues
to reject unsupported languages, and now has a real way to disable stemming.

Refs: https://github.com/qdrant/qdrant/issues/9289
Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(edge-py): handle Disabled stemmer in python bindings; fix openapi schema

- Handle the new StemmingAlgorithm::Disabled variant in the qdrant-edge-py
  bindings (FromPyObject/IntoPyObject/Repr) and add a DisabledStemmer pyclass
  plus its .pyi stub entry.
- Match generator output for the StemmingAlgorithm OpenAPI schema (plain $ref
  in anyOf) so docs/redoc/master/openapi.json stays consistent.

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

* fix(openapi): regenerate StemmingAlgorithm schema with generator output

Ran tools/generate_openapi_models.sh so docs/redoc/master/openapi.json
exactly matches generator output: DisabledStemmerParams/NoStemmer are placed
after SnowballLanguage, and the StemmingAlgorithm anyOf entry is a plain $ref
(the schema2openapi step flattens the allOf+description wrapper).

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

* fix(test): avoid wildcard enum match arm in bm25 sparse_len helper

clippy --all-targets flags `other => panic!()` as wildcard_enum_match_arm;
match the Dense/MultiDense variants explicitly instead.

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

* fix: issues

* fix: log::warn as call once

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Daniel Boros <dancixx@gmail.com>
2026-06-19 13:11:13 +02:00
Tim Visée
122ef1595c Enable the single_file_mmap_vector_storage flag by default (#9332)
* Enable the `single_file_mmap_vector_storage` flag by default

* Update comment on when flag is enabled by default

* Update OpenAPI spec

* Fix tests
2026-06-18 16:24:03 +02:00
qdrant-cloud-bot
e8897c9ca1 docs: clarify purpose of /healthz, /livez and /readyz endpoints (#9509)
Previously all three Kubernetes health endpoints shared the same generic
description ("An endpoint for health checking used in Kubernetes."), which did
not convey what each one actually guarantees.

- /healthz and /livez: clarify they are pure liveness checks (200 once the HTTP
  API is up), do not inspect data/shards/consensus, and are identical to each
  other.
- /readyz: clarify it is a readiness probe that waits out pending data
  operations (consensus catch-up + shard health in distributed mode) before
  reporting ready, and document the real 200 ("all shards are ready") and 503
  ("some shards are not ready") responses.

Documentation-only change (OpenAPI descriptions/examples + added 503 response
doc). No runtime behavior changes.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-18 14:13:59 +02:00
Roman Titov
2fd1b5371a Add async_payload_storage feature flag (#9409) 2026-06-10 16:49:15 +02:00
Djole
25a4d4906d fix: validate hnsw_ef search parameter (#9320)
* fix: validate hnsw_ef search parameter

* chore: regenerate openapi spec
2026-06-08 18:09:52 +02:00
qdrant-cloud-bot
c150bd5caa 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-01 11:32:25 +02:00
Jojii
7a8703f166 [TQDT] API (#9172)
* [ai] TQDT in the API

* [ai] unify new sparse error for TQDT

* Rename to `turbo4`

* Add `Turbo4` to comments and doc strings.

* Also validate named sparse vector creation
2026-05-29 15:26:39 +02:00
qdrant-cloud-bot
26aeb9c0b2 docs: refresh prevent_unoptimized description (#9133)
The previous description was outdated: it claimed that enabling this
option "blocks updates at the request level" until segments are
re-optimized. In practice the implementation uses "deferred points":
new points written to large unoptimized segments are persisted but
excluded from read/search results until the segments are optimized.

Updates are not blocked; only `wait=true` clients are made to wait for
the deferred points to become visible. Update this in the REST schema
(via `OptimizersConfig` / `OptimizersConfigDiff`), in the gRPC proto,
in the edge config docstrings, and regenerate the OpenAPI bundle via
`tools/generate_openapi_models.sh`.

Co-authored-by: Cursor Agent <agent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 17:36:34 +02:00
Arnaud Gourlay
ee2f72e3a0 fix correct ReadConsistency factor minimum to 1 (#9021)
* fix(openapi): correct ReadConsistency factor minimum to 1

* validate ReadConsistencyGrpc
2026-05-12 17:27:36 +02:00
Abdon Pijpelink
b5124388ab Update readme (#8923)
* Initial commit

* Edits

* Change non-descriptive “here” link texts

* Review feedback

* Add Edge code snippet, Web UI section with screenshot, mention autit logging, mention NVIDIA and AMD GPU support

* Resize Web UI image; Move to end of Features section

* Restore Web UI section in README

Reintroduce Web UI section to README with visual aid.

---------

Co-authored-by: Andrey Vasnetsov <andrey@vasnetsov.com>
2026-05-07 21:11:26 +02:00
Jojii
d3ad1ac988 API Adjustments for TQ (#8914)
* API Adjustments for TQ

* Clippy
2026-05-05 22:57:11 +02:00
Andrey Vasnetsov
d02ef48f24 Dynamic cpu pool (#8790)
* [AI] inptoduce CPU process measurement

* use parking_lot + 4 seconds refresh rate

* [AI] AdaptiveSearchHandle

* fmt

* openapi schema

* keep Runtime field

* fix test

* [AI] instead of async semaphore, use 2 runtimes

* Adjust usage window to 2 seconds

* Address CodeRabbit review comments for dynamic CPU pool

- OpenAPI / telemetry: user-facing cpu_cores_used description (2s window, when null).
- process_cpu_usage: backoff after procfs errors; serialize Linux unit tests on CACHE.
- Docs: decouple runtime thread comments from hardcoded 4× multiplier; name search_runtime in test.
- consensus test: replace stale runtime comment.

Made-with: Cursor

* chore(openapi): regenerate master spec via generate_openapi_models.sh

Replace hand-edited cpu_cores_used description with output from
schema_generator + merge pipeline so openapi_consistency_check passes.

Made-with: Cursor

---------

Co-authored-by: Cursor Agent <agent@cursor.com>
2026-04-27 12:56:29 +02:00
Arnaud Gourlay
4d435e5734 Fix Openapi spec for new vector crud ops (#8779)
* Fix Openapi spec for new vector crud ops

* validate spec conformance in tests
2026-04-23 18:34:50 +02:00
Arnaud Gourlay
4cb494d3b6 Fix missing validation on adding named vector (#8776) 2026-04-23 12:23:38 +02:00
qdrant-cloud-bot
61f3bd19c3 Clean up unused #[validate] field attributes (#8732)
`StrictModeConfigOutput` is an output-only type that is never validated,
so remove its dead `#[validate(...)]` field attributes.

`ShardSnapshotRecover` does need validation: add the missing `Validate`
derive and switch the actix handler from `web::Json` to `valid::Json`
so the SHA256 checksum constraint is actually enforced.

Made-with: Cursor

Co-authored-by: Cursor Agent <agent@cursor.com>
2026-04-22 09:48:00 +02:00
Andrey Vasnetsov
9686c8f952 low ram strict mode (#8715)
* [AI] strict mode parameter for limiting update requests if ram usage is over threshold

* opanAPI update

* [AI] end-to-end test

* fmt

* Fix e2e test: memory rejection check broken by string truncation

UnexpectedResponse.__str__() truncates the raw response body, cutting
off the `max_resident_memory_percent` hint at the end of the error
message. Use `resident memory usage` instead, which appears early
enough to survive the truncation.

Made-with: Cursor

* add grpc validation

* test check_resident_memory

---------

Co-authored-by: Cursor Agent <agent@cursor.com>
Co-authored-by: Arnaud Gourlay <arnaud.gourlay@gmail.com>
2026-04-20 15:48:39 +02:00
Andrey Vasnetsov
f321c9fe37 Low Memory mode (#8714)
* [AI] implement parameter + cover populate + cover quantized vectors

* telemetry OpenAPI schema

* [AI] hook immutable payload indexes

* fmt

* do not populate payload index if we fallback to mmap

* Reformat

* Also suppress universal IO disk cache population

---------

Co-authored-by: timvisee <tim@visee.me>
2026-04-20 11:33:35 +02:00
Jojii
8c72d3b12f API Changes for TurboQuant (#8686)
* [ai + manual] API changes for TQ

* CI

* Add TurboQuant types to Python type stubs

Made-with: Cursor

* Revert "Add TurboQuant types to Python type stubs"

This reverts commit 6543af3244.

---------

Co-authored-by: Cursor Agent <agent@cursor.com>
Co-authored-by: generall <andrey@vasnetsov.com>
2026-04-16 12:12:33 +02:00
Andrey Vasnetsov
acfb6503b1 crud named vectors (#8605)
* Add empty placeholder vector storage types for named vector CRUD

Introduce EmptyDenseVectorStorage and EmptySparseVectorStorage as
placeholder storages for newly created named vectors on immutable
segments. These report all vectors as deleted, consume no disk space,
and are reconstructed from segment config on load via the new
VectorStorageType::Empty and SparseVectorStorageType::Empty variants.

Key design decisions:
- is_on_disk is derived from original user config, not hardcoded
- MultiVectorConfig is preserved for multi-vector support
- Config mismatch optimizer skips Empty storage to avoid false rebuilds
- Quantization delegates normally (handles 0 vectors gracefully)
- get_vector includes debug_assert to catch unexpected access

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

* [AI] segment-level operations for creating and deleting anmed vectors

* [AI] implement named vector creation and deleting in proxy segment

* [AI] Step 3: Proxy Segment Handling for Named Vector Operations

* [AI] implement for Edge

* [AI] implement consensus operations for named vector operations

* [AI] refactor VectorNameConfig, remove VectorNameConfigInternal

* [AI] handle vector schema inconsistency in raft snapshot recovery

* [AI] rest + grpc API

* [AI] clippy

* [AI] generate openAPI schema

* fmt

* ci fixes

* [AI] fix jwt access test

* [AI] nop operation for awaiting of consensus-commited update ops

* [AI] move vector name operations into points service

* [AI] implement internal api for vector name operations

* [AI] change collection-level config along with segment level operation

* [AI] vector schema reconceliation instead of error

* fmt

* missing compile-time option

* [AI] integration test

* [AI] fix missing JWT tests

* [AI] remove NOP

* [AI] openapi test

* [AI] fix initialization of mutable segment

* [AI] more simple integration tests

* fmt

* [AI] make cluster test a bit harder

* [AI] make test less flacky

* [AI] rabbit comments

* [AI] check params compatibility before writing vector config

* [AI] make sure to register vector storages in structure payload index

* [AI] vector name validation

* lower vector length validation to 200 chars to account for prefix in filename

* [AI] proxy segment: prevent stale data leak through optimization

* fmt

* [AI] filter out removed vectors from proxy response

* [AI] handle vector name in proxy

* fmt

* adjust proxy info based on dropped vectors

* [AI] proxy segment: update filters to correct has_vector condition

* fmt

* clippy

* Fix consensus snapshot applicaiton for vector schema

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 14:45:18 +02:00
Andrey Vasnetsov
0699956019 audit logging telemetry (#8636)
* [AI] report audit logging size and config in telemetry, size in metrics

* fmt

* openapi schema
2026-04-09 15:23:48 +02:00
qdrant-cloud-bot
ec7d7d2c7e docs: make dev branch requirement more prominent for contributors (#8581)
Contributors frequently open PRs against master instead of dev.
Add prominent callouts and a root-level CONTRIBUTING.md so the
branch requirement is visible at every step of the contribution flow.

Made-with: Cursor

Co-authored-by: Cursor Agent <agent@cursor.com>
2026-04-01 13:56:29 +02:00
xzfc
0b0df145b3 Drop RocksDB (#8529)
* Skip broken tests

* Add rocksdb dropper

This is a temporary tool. It will be removed later in this PR.

* [automated] Drop rocksdb

This commit is made by running tools/rocksdb/drop.sh

* Touch-up after ast-grep

The previous automated commit removed items, but not their comments.
Also, some blocks are left with only one item.
Also, ast-grep can't handle macros like `vec![]`.

This commit completes the job.

* Remove RocksDB dropper

* Remove mentions of rocksdb feature in CI

* Fix clippy warnings

These `FIXME` comments added previously in this PR by ast-grep by
"peeling" cfg_attr like this:

    -#[cfg_attr(not(feature = "rocksdb"), expect(...))]
    +#[expect(...)] // FIXME(rocksdb): ...

This commit removes these allow/expect attributes and fixes clippy
lints.

* Remove leftover rocksdb-related code

Removed:
- Cargo.toml: rocksdb cargo feature flag and dependencies.
- code: rocksdb-related items that was not under feature flag.
- flags.rs: rocksdb-related qdrant feature flags.

Disabled:
- Some benchmarks because these do not compile now.

* Print warning if rocksdb leftovers found in snapshots

* Remove Clone from tokenizer

* Regenerate openapi.json
2026-03-31 18:08:39 +00:00
Arnaud Gourlay
70c2b58ae3 Fix openapi min timeout (#8515) 2026-03-27 12:10:27 +01:00
Bhagirath Kapdi
fb704e5d13 Feature/strict mode search max batchsize (#8469)
* feat: add search_max_batchsize to strict mode config

* added test case for search_max_batchsize

* Changes for fixing CI issue dure openapi

* Modify check_strict_mode_batch

---------

Co-authored-by: Arnaud Gourlay <arnaud.gourlay@gmail.com>
2026-03-26 16:26:09 +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
Jojii
c015b86515 Add deferred point count to UpdateQueueInfo (#8414)
* Add deferred point count to UpdateQueueInfo

* Hide if prevent_unoptimized is false

* Clippy

* Openapi

* Iterate over appendable segments only

* Only calculate deferred point count if prevent_unoptimized is true

* Rebase fixes

* Codespell
2026-03-19 15:27:23 +01:00
Jojii
356040312b Correct calculation of deferred point counts (#8366)
* Don't account for deferred points in some places

# Conflicts:
#	lib/collection/src/shards/local_shard/scroll.rs

* Add in QueryContext

* Cover more places

* Coderabbit review remarks

* Properly count amount of deleted deferred points (#8386)

* Properly count amount of deleted deferred points

* Prevent double-counting of the same point

* Remove hints to estimations

* Properly handle counts in ProxySegment

* Add tests and fix deleted point count issue

* Adjusts tests + fix issues

* Separte fields for deferred points in telemetry (SegmentInfo)

* Remove deferred_points_count()

* openapi

* Fix test by manually calculating visible points

* Adjust ProxySegment test to revertion of SegmentInfo

* Throw error if collection was not found in telemetry (e2e Test)

* Update lib/segment/src/segment/segment_ops.rs

Co-authored-by: Andrey Vasnetsov <andrey@vasnetsov.com>

* Make new fields in API optional

* Don't take range if no deferred point exist

* Review remarks

---------

Co-authored-by: Andrey Vasnetsov <andrey@vasnetsov.com>
2026-03-19 11:33:09 +01:00
qdrant-cloud-bot
33ed0dac9f Propagate deferred_internal_id into SegmentInfo and telemetry (#8382)
Add `deferred_internal_id` field to `SegmentInfo` so it is exposed
through segment telemetry. The field is optional and skipped from
serialization when `None`. It is excluded from anonymization via
`#[anonymize(false)]`.

Made-with: Cursor

Co-authored-by: Cursor Agent <agent@cursor.com>
2026-03-13 12:31:19 +01:00
tellet-q
c0bfc21754 Enable heap profiling in pyroscope (#8371)
* Start heap profiling if MALLOC_CONF set

* Update doc
2026-03-13 11:45:30 +01:00
Daniel Boros
d4cb6a58d9 feat/edge segment opt (#8224)
* feat: add edge shard optimize

* feat: refactor edge optimize logic

* chore: remove unused &self

* feat: add more tests

* fix: linter

* fix: missing threshold prop

* fix: local nightly version

* fix: linter

* fix: linter issues

* fix: use of explicit from

* feat: add some notes

* fix: feature_flags call once

* [manual] review refactor

* fix:
- default_segment_number -> move shard
- rename: default_hnsw_config -> hnsw_config
- infer existing hnsw_config

* feat: add optimize to python

* feat: add python optimize example

* feat: add hnsw config load tests

* fix: linter

* feat: make unified build config

* fix: linter

* fix: openapi definition'

* fix: review comments

* fix: remove mut self & reset_temp_segments_dir

* fix: linter

* review: rename for simpler public name + use explicit strucuture deconstruction

* clipy

---------

Co-authored-by: generall <andrey@vasnetsov.com>
2026-03-06 18:47:15 +01:00
qdrant-cloud-bot
d7ad2d45f7 fix: codespell 2.4.2 - pre-selected -> preselected, pre-select -> preselect (#8303)
* fix: codespell 2.4.2 - pre-selected -> preselected, pre-select -> preselect

Fixes CI failure with codespell 2.4.2 which flags hyphenated forms.
Updated in: types.rs, query/mod.rs, qdrant.rs, points.proto

Made-with: Cursor

* chore: regenerate OpenAPI spec (tools/generate_openapi_models.sh)

Updates oversampling description to use preselected spelling.

Made-with: Cursor

---------

Co-authored-by: Cursor Agent <agent@cursor.com>
2026-03-06 11:47:16 +01:00
tellet-q
ea0455600c Bump pyroscope version to lib-1.0.2 (#8282)
* Bump pyroscope version to lib-1.0.2

* Provide doc with pyroscope configuration
2026-03-03 16:03:26 +01:00
Andrey Vasnetsov
ab6cb8e467 missing timeout for payload index ops (#8176) 2026-02-19 10:39:50 +01:00
Andrey Vasnetsov
6e79f8bf02 Untagged Enum for FeedbackStrategy (#8171)
* [manual] make untagged enum for consistent API for FeedbackStrategy

* add #[validate(nested)]

---------

Co-authored-by: Luis Cossío <luis.cossio@outlook.com>
2026-02-18 16:55:47 +01:00
Andrey Vasnetsov
345621f447 Expose shard snapshot API (#8132)
* [AI] generate openapi schema for stream download of shard snapshot

* bump number of APIs

* cover API with access test

* fix test

* fix test
2026-02-16 13:34:44 +01:00
generall
72cc67421b add missing timeout parameter for delete points API 2026-02-15 16:11:07 +01:00
Arnaud Gourlay
5ffc6a45a8 Remove update queue opnum from CollectionInfo (#8127) 2026-02-13 17:36:54 +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