Commit Graph
7000 Commits
Author SHA1 Message Date
xzfc b03f86f4a9 Docstrings: fix concatenated class + constructor docs
Previous "Docstrings:" commits concatenated original class docstrings
with original __init__ docstrings.  The reason was technical: CPython
won't let us easily add a docstring for the __new__ method.  But this
resulted in weird-looking docstrings.  This commit un-weirds them.

See also: https://www.github.com/PyO3/pyo3/issues/4326
2026-09-24 01:47:45 +00:00
xzfc 77f8724f86 Docstrings: manual fixes 2026-09-24 01:47:45 +00:00
xzfc 6dc326e552 Docstrings: pre-PR qdrant_edge.pyi -> Rust (only replace)
Copy the original pre-PR qdrant_edge.pyi docstrings to Rust verbatim.
Replace existing Rust docstrings.
2026-09-24 01:47:45 +00:00
xzfc 0fb259e6ec Docstrings: pre-PR qdrant_edge.pyi -> Rust (only add)
Copy the original pre-PR qdrant_edge.pyi docstrings to Rust verbatim.
Only add: existing Rust docstrings stay intact.
2026-09-24 01:47:45 +00:00
xzfc 3730368422 Proper python signatures for EdgeShard.retrieve and EdgeConfig(vectors=…) 2026-09-24 01:47:45 +00:00
xzfc 5b3231465d Export ShardInfo and PayloadIndexInfo 2026-09-24 01:47:45 +00:00
xzfc d9e6721e0c Add just py-docs recipe 2026-09-24 01:47:45 +00:00
xzfc 443157e436 Spell out type hints PyO3 cannot infer 2026-09-24 01:47:45 +00:00
xzfc cc028721d3 Drop redundant __repr__
The result of `repr(…)` is identical.
2026-09-24 01:47:45 +00:00
xzfc 7c36f391fd Run just py-stubs
This commit replaces hand-written stubs with auto-generated stubs.

Temporary quality drop (no docstrings, `Incomplete` annotations, etc);
subsequent commits will restore them.

This commit is made early in the stack so changes in stub generation
would be visible in subsequent commits.
2026-09-24 01:47:45 +00:00
xzfc 20a60b9214 Rename identifiers that collide with keywords 2026-09-24 01:47:45 +00:00
xzfc f10ef3538f Add just py-stubs recipe 2026-09-24 01:47:43 +00:00
xzfc 0bc7c1a783 Use maturin from uvx, not nix 2026-09-24 00:01:48 +00:00
xzfc 19d4da9988 UIO tracing and visualizer (#10742)
* Add uio_trace module and visualizer

* Handle object store, not just gRPC

* Improve visualizer

* Improve visualizer [2]
2026-09-23 21:56:24 +00:00
Andrey VasnetsovandClaude Fable 5.1 878843e6e0 Unify the blobstore tracker read interface across storage modes (#10735)
One TrackerRead trait, in lib/blobstore/src/tracker/read.rs, now covers
the Gridstore Tracker and ReadOnlyTracker and the Logstore
AppendOnlyTracker. LogstoreView, LogstoreReader, and validate_consistency
are generic over it, as GridstoreView already was over the Gridstore-only
trait.

The trait gains get_range and an access pattern on get, and its iter
returns impl Iterator instead of the concrete Gridstore Iter, which drops
the storage type parameter from the trait. The Gridstore trackers
implement get_range through a new read_slots helper; nothing in Gridstore
calls it yet. The lifecycle methods of LogstoreReader (open, preopen,
files, live preload, live reload, clear cache) stay in an impl block bound
to AppendOnlyTracker, so a future immutable in-RAM tracker plugs into the
read path without inheriting reload semantics.

No runtime change: Gridstore slot reads keep the Random access pattern,
and PointerItem stays the iterator item type.

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-23 15:40:51 +02:00
Arnaud GourlayandClaude Opus 5.5 06433b7028 [BM25] Add a BM25-over-sparse baseline benchmark (#10677)
* Add a BM25-over-sparse baseline benchmark

The text payload index is meant to score about as fast as BM25 over sparse
vectors, so the number it has to match needs to exist before the scorer
does. Measures a local shard end to end, no HTTP.

- embeds the corpus through `lib/bm25` with its defaults, so the baseline
  is the route a user migrates from rather than a reimplementation
- Zipf-like vocabulary. On a uniform one every term is equally selective,
  IDF is flat and pruning has nothing to prune, which would flatter any
  scorer measured against it
- two shards rather than one shard before and after optimization: a shard
  that will optimize starts as soon as the upsert lands, so the first cut
  timed a half-converted index and called it fresh. Both states assert
  what they hold before anything is timed

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

* Measure the sparse BM25 baseline where the routes separate

- 200k documents by default (BM25_SPARSE_DOCS overrides): at 20k every shape
  of both routes measures the same and half of a shard-level query is the
  shard; reachable through the shard since #10682
- a third shard with the sparse index on disk, which the optimized state never
  exercised
- recall at 10 against BM25 by definition, printed per state: the default
  avg_len of 256 on a corpus averaging 110 tokens misses a quarter of the true
  top 10, so the optimized state is also timed with the corpus average
- corpus, queries, reference and recall move to segment::fixtures::bm25_corpus,
  to be shared with the text-index bench and the comparison harness
- module doc: the sparse shapes measure within a few percent of each other; the
  states exist for the text comparison

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

* Refuse an empty corpus in the sparse BM25 baseline

BM25_SPARSE_DOCS=0 built empty shards, made the average length NaN and
scored every empty truth as recall 1.0.

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

* Measure what the sparse BM25 baseline states claim

The fresh shard kept the default 10 MB indexing threshold and optimized
itself in the background, so it was timed as a second optimized state.
Disable indexing and re-check it after timing. Keep the shard storage
under CARGO_TARGET_TMPDIR so the on-disk index is not read from a tmpfs.
Fix doc comments that named missing files, a no-op IDF clamp and the
wrong reason for the empty-corpus guard.

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 13:59:20 +02:00
Luis Cossío 87dc39bfdc [Diskcache] piggyback remote reads across pipelines (#10690)
* AI: Share reads across different diskcache pipelines

* AI: Unify scheduled reads into single ScheduledRead struct

* AI: Implement follower promotion on fetch abandonment

* AI: self-promote if waiting for an abandoned placeholder

* AI: simplify

* AI: use Weak<Placeholder> instead of manually counting strong refs

* AI: track abandoned waiters

* AI + manual: park with timeout

* AI: fix barrier in test

* fmt with latest nightly

* expect remote pipeline
2026-09-22 19:30:48 -03:00
Roman Titov 6a03f4288b Implement resharding operations for ConsensusStateMachine (#10697) 2026-09-22 14:49:03 +02:00
qdrant-cloud-bot b50c0fe012 test: wait for replicas Active before snapshot layout assert (#10739)
Avoid racing peer 0's cluster view after kill+recover, where remotes
can still be Partial while the recovered peer's local shards are Active.
2026-09-22 13:16:00 +02:00
Arnaud GourlayandClaude Opus 5 c296898355 [BM25] Add document length into the immutable and on-disk text indexes (#10639)
* Carry document length into the immutable and on-disk text indexes

The mutable index records `doc_len`. The immutable and on-disk backends dropped
it on conversion. They now carry it and persist it as a sidecar, so a length
survives an optimizer run and a restart.

- `ImmutableInvertedIndex` gains `point_to_doc_len`, parallel to
  `point_to_tokens_count` and zeroed wherever that vector is, so summing it
  never counts a deleted document.
- `OnDiskInvertedIndex` writes `point_to_doc_len.dat`, only when the index
  records lengths. `files()` lists it only when it exists, so a snapshot carries
  it exactly when there is one.
- Deletions are masked on load, not at build time. The file is written once and
  a point deleted later through the id-tracker is zeroed in
  `TryFrom<&OnDiskInvertedIndex>`. No segment total is stored, since it could
  only be summed after that masking.
- A sidecar shorter than the counts is treated as absent. It can only come from
  a partially copied file set, and padding it would give every point past the
  truncation a zero that reads like a real length.
- A missing sidecar on an index that should have one makes `new_mmap` report the
  index absent, which routes into the existing rebuild from payload. The check
  lives there rather than in `OnDiskInvertedIndex::open` because the read-only
  stack never builds and would drop the field instead.
- `FullTextMmapIndexBuilder::add_many` reached below `index_str_tokens` and so
  declared no length at all. It now tokenizes through the shared helper and
  measures like the gridstore path.

Recording is still gated behind `TextIndexParams::scoring()`, so none of this is
written today.

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

* Wipe the text index directory instead of its listed files

`files()` lists the `doc_len` sidecar only when the index loaded it, so a
truncated sidecar is omitted. `wipe` then deleted the listed files, failed to
remove the now non-empty directory, discarded that error and returned success,
leaving the directory and a stale sidecar behind.

Remove the directory itself. Each field owns its own `{field}-text` directory,
so this no longer depends on `files()` being an accurate inventory, and real
removal errors propagate rather than being swallowed.

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

* Drop the fully qualified visibility paths in the text index

`pub(in crate::index::field_index::full_text_index)` is tedious to read and to
keep correct, and it grants nothing here: `inverted_index` and
`mutable_text_index` are private modules of `full_text_index`, so their types
are not nameable from outside it whatever the field visibility says. Plain
`pub` is no wider in practice.

Widening `Storage` surfaced two `private_interfaces` warnings, since
`ZerocopyPostingValue` and `PostingListHeader` in `types.rs` were still behind
the long path; those move too.

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

* Harden the document length sidecar after review

- check for the sidecar before opening the index rather than after. The
  open populates the whole file set, so the first start after scoring is
  enabled would fault in every segment's postings only to discard them
- treat a sidecar that covers more points than the index as untrustworthy,
  not just one that covers fewer, and warn with both counts the way
  `SortedBlockIndex::open` does. A longer one used to be accepted and then
  silently truncated when materialized
- unlink a stale sidecar when a build records no lengths. It was the only
  file here that could outlive the build that wrote it, and `open` would
  have read it as this build's
- say why an index is being rebuilt from payload instead of leaving a
  silent full re-index announced at debug level
- read `phrase_matching` from the config in the mmap builder, like the
  other two callers of the same helper pair, so the two halves of the
  sentinel rule cannot drift apart
- assert the length invariant the writers actually maintain, and correct
  what `files()` and the field comment claim

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

* Address review on the document length sidecar

- shrink the document length vector when it is handed over to the immutable index, it arrives with the mutable index's doubling capacity
- narrow the immutable index fields to pub(super), with a test-only accessor for the one reader outside the module
- let wipe propagate a missing index directory instead of treating it as success, nothing reaches it with the directory already gone

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 13:13:38 +02:00
c218fa67c0 Configurable key prefix for object storage snapshots (#10715)
* Configurable key prefix for object storage snapshots

Snapshot object keys were always derived from the local snapshots path,
so every deployment sharing a bucket wrote under the same `snapshots/`
root. Each cloud config block gains an optional `prefix`, and objects
become `<prefix>/snapshots/...`.

The prefix is applied by wrapping the client in `PrefixStore`, so the
snapshot operations and the names returned by the API are unchanged.
Leading, trailing and repeated slashes are dropped, and an empty prefix
is a no-op.

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

* Test that snapshot operations cannot escape the storage prefix

Hostile targets are handed to the cloud manager directly, past
`validate_snapshot_name`: parent references, absolute paths, encoded
slashes, backslashes and empty paths. Writes must stay under the prefix,
and objects planted outside the prefix must be invisible to list,
download, stream and delete.

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

* Normalize empty prefix components

Refactor prefix handling to remove empty components.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Tim Visée <tim+github@visee.me>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-09-22 12:48:26 +02:00
6cd816acac Add match: { substring } filter condition (#10711)
* Add `match: { substring }` filter condition

Unindexed `text` and `text_any` matching became token-aware in #10341 and
#10593. Users who relied on the old raw substring behaviour get it back as
an explicit condition: `match: { "substring": "..." }` selects points with a
string value containing the given string, byte-wise and case-sensitive,
consistent with exact keyword and prefix matching.

Execution: a keyword index (with or without the `prefix` option) serves the
condition by scanning its value dictionary and uniting the postings of the
matching keys; cardinality reuses the prefix estimator, generalised into
`keys_union_cardinality`. The per-point checker goes through the forward
index. Without a keyword index the condition falls back to reading the
payload. Text, bool, integer and uuid indexes decline it.

Strict mode: the condition requires the `KeywordMatch` capability, so with
`unindexed_filtering_retrieve: false` it is rejected on unindexed and on
text-indexed fields and allowed on any keyword index.

API: `MatchSubstring` in the REST `Match` union with regenerated OpenAPI,
gRPC `Match.substring = 12`, edge python `MatchSubstring`, edge ffi
`Match::Substring`.

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

* Test substring fallback on a text-indexed field, document estimator params

A text index cannot serve `substring`, so on a field that has only a text
index the condition runs through the payload fallback; only strict mode may
reject it. Pin that in the OpenAPI suite and reword the strict-mode unit
test comment, which read as if the text index itself blocked the query.

Also spell out what `keys` and `postings` mean in `keys_union_cardinality`.

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

* Serve `match: { substring }` from the keyword key dictionary

The condition used to enumerate keys through `MapIndexRead::for_each_value`,
which on the on-disk variant drags the whole `value_to_points` file through
`for_each_entry`, plus one random read per matching key for its postings
count. Query planning paid that scan in full, before deciding whether to use
the clause at all.

Route it through the `prefix_index.bin` key dictionary instead: front-coded
keys with their postings counts inline, no postings. Estimation now reads
keys only and never touches `value_to_points`; filtering takes the matched
key list and resolves postings in one batched read, as prefix matching
already does.

This makes the `prefix` option a requirement: a keyword index without it has
no key dictionary, so it declines the condition and falls back to the payload
scan, the same as a text index. Strict mode follows — substring now infers
`KeywordPrefix`, so `unindexed_filtering_retrieve: false` names
`keyword (with prefix: true)` as the index to create.

`PrefixIndex::for_each_key` reads blocks in ~1 MiB chunks rather than the
whole key section at once: a substring cannot be pruned by the block index,
so the one-shot read would grow with the dictionary.

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

* Sync MatchSubstring OpenAPI description with Rust docs

After routing substring matching through the prefix key dictionary,
the schema docstring required regenerating so OpenAPI stays consistent.

* Scan the map index keys for substring match without a dictionary

Without the keyword dictionary a substring condition was declined by the
field index and left to the per-point condition checker, which reads the
forward index for every candidate point. Enumerate the distinct keys of
`values_to_points` instead: the same one-pass-over-distinct-values shape as
the dictionary scan, only over a structure that interleaves keys with their
postings. Filtering and cardinality estimation are then always served, so
the condition can act as a primary clause on a plain keyword index.

Prefix matching keeps its per-point fallback: an ordered dictionary is what
makes a prefix a bounded range, and enumerating every key to answer one is
not a trade worth making implicitly.

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

* Reject substring matching in strict mode

A substring condition is answered by looking at every distinct value of the
field: no index gives it a bounded access path, so there is no index a user
could create to make it affordable. Reject it under strict mode instead,
wherever a filter reaches verification — read and write filters, nested
sub-filters, and prefetch filters.

Filter limits are now checked before the unindexed-field check, so the
rejection is not reported as "create an index for this key", advice that
would lead to the same rejection afterwards.

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

* Update the strict mode substring test to the new rejection

The test asserted that substring filtering under strict mode asks for a
keyword index with the `prefix` option. It is now rejected whatever index
the field carries, so every case in the test gets the same answer.

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

* Estimate a substring condition without scanning

Counting the keys a substring matches costs the same scan as answering the
condition, and `filter` then repeats it to collect those keys. Report the
uninformed estimate instead — the one an unindexed condition has always
reported — and keep the primary clause, so the scan happens once, in
`filter`, and only when the planner picks the condition to drive iteration.

With no counts to collect, `substring_scan` collapses into `substring_keys`:
the in-RAM variants no longer look up a posting count per matched key.

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

* Don't to parse everything as UTF-8

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: qdrant-cloud-bot <111755117+qdrant-cloud-bot@users.noreply.github.com>
Co-authored-by: timvisee <tim@visee.me>
2026-09-22 12:47:34 +02:00
Andrey VasnetsovandClaude Fable 5.1 baec6a3119 Support GCS and Azure Blob Storage for collection snapshots (#10714)
Snapshot storage was limited to S3 although the object_store dependency
already ships the GCS and Azure backends. `snapshots_storage` now accepts
`gcs` (alias `gcp`) and `azure`, configured through `gcs_config` and
`azure_config` blocks next to the existing `s3_config`. The legacy S3
shape is unchanged.

Client construction is split into one builder per backend, all sharing
the Qdrant user agent and the plain-HTTP rule for `http://` endpoints.
Startup warns when a config block for an unselected backend is present.

The e2e snapshot recovery test is parameterized over the cloud backends.
The GCS case is skipped because fake-gcs-server does not implement the
XML multipart upload API that object_store uses for GCS uploads.

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-22 11:55:19 +02:00
xzfc 8f44ddd8ba Combined storage tests (#10676) 2026-09-22 09:52:58 +00:00
Arnaud GourlayandClaude Opus 5 0a0fd87909 Count documents, not points, in the mutable text index (#10675)
A value that tokenizes to nothing is indexed and matches nothing. The mmap
build already treats it as no document, folding it into the "no tokens"
mask, while the mutable index counted it, so `points_count` for the same
data depended on whether the segment had been optimized yet.

- `index_tokens` counts a transition rather than incrementing, so a point
  becomes a document when it gains its first token and stops being one when
  rewritten to nothing. Re-indexing the same point no longer counts it twice
- `remove` decrements only when the removed token set had tokens
- the builder counts the same way

`points_count` feeds `count_indexed_points` for cardinality estimation, and
is `N` for the IDF of a text score.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 11:20:44 +02:00
dependabot[bot] 72f80b832e build(deps): bump docker/build-push-action from 7.3.0 to 7.4.0 (#10724)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 7.3.0 to 7.4.0.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/53b7df96c91f9c12dcc8a07bcb9ccacbed38856a...c3c9e263c25d99ce0380d002d59b67737d91b0dc)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-version: 7.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-09-22 10:52:56 +02:00
dependabot[bot] 2b713b4fc5 build(deps): bump jlumbroso/free-disk-space from 1.3.1 to 2.0.0 (#10720)
Bumps [jlumbroso/free-disk-space](https://github.com/jlumbroso/free-disk-space) from 1.3.1 to 2.0.0.
- [Release notes](https://github.com/jlumbroso/free-disk-space/releases)
- [Commits](https://github.com/jlumbroso/free-disk-space/compare/54081f138730dfa15788a46383842cd2f914a1be...ceedf095f4ec1a097402bc6bd80831f2e1a6fde6)

---
updated-dependencies:
- dependency-name: jlumbroso/free-disk-space
  dependency-version: 2.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-22 10:21:59 +02:00
dependabot[bot] 61f19e4073 build(deps): bump tinyvec from 1.13.2 to 1.13.3 (#10728)
Bumps [tinyvec](https://github.com/Lokathor/tinyvec) from 1.13.2 to 1.13.3.
- [Changelog](https://github.com/Lokathor/tinyvec/blob/main/changelog.md)
- [Commits](https://github.com/Lokathor/tinyvec/compare/v1.13.2...v1.13.3)

---
updated-dependencies:
- dependency-name: tinyvec
  dependency-version: 1.13.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-09-22 10:18:40 +02:00
dependabot[bot] e308e64586 build(deps): bump dial9 from 0.5.0 to 0.5.1 (#10731)
Bumps [dial9](https://github.com/dial9-rs/dial9) from 0.5.0 to 0.5.1.
- [Release notes](https://github.com/dial9-rs/dial9/releases)
- [Changelog](https://github.com/dial9-rs/dial9/blob/main/CHANGELOG.md)
- [Commits](https://github.com/dial9-rs/dial9/compare/dial9-v0.5.0...dial9-v0.5.1)

---
updated-dependencies:
- dependency-name: dial9
  dependency-version: 0.5.1
  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-09-22 10:17:49 +02:00
tellet-q 4ea347a5a6 fix(deps): repair Cargo.lock after concurrent syn and serde_with bumps (#10734) 2026-09-22 09:35:44 +02:00
tellet-qandClaude Opus 5 5abf55f2c7 test/fix-flaky-building-cancellation (#10696)
* test(segment): replace timing-based building cancellation test

`test_building_cancellation` compared wall-clock times of independent
builds cancelled after fractions of a measured baseline. Its tolerances
had to be widened repeatedly (#2039, #8243, #10346) and it still failed on
Windows CI, e.g. when an early stop landed in a slow non-cancellable setup
step (time_early: 969, time_later: 631, baseline 2488).

Replace it with tests that target build phases explicitly and measure
work instead of time:

- test_building_cancelled_before_start: a build cancelled up front returns
  `Cancelled` without starting any vector index work.
- test_building_cancelled_during_main_graph: a watcher cancels the build
  once the main HNSW graph (observed via the progress tracker) reaches
  1000 of 10000 points. The build must return `Cancelled`, leave the graph
  unfinished, and insert at most 2 points per build thread after the flag
  is set (only in-flight insertions may complete).

Both also check that a cancelled build leaves nothing behind in the
segments or temp directory. Use random vectors with a fixed seed instead
of identical zero vectors.

Verified the main-graph test fails when the per-point stop check is
removed, or only done every 64 points (102 points after cancel, limit 16).

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

* test(segment): cover cancellation during HNSW graph healing

it: an uncancellable heal phase is what blocked consensus when removing a
replica during an optimization.

Track progress of the `migrate` phase (healed `(point, level)` pairs out of
the total), like `main_graph` already does. Besides showing healing
progress in optimization telemetry, this lets a test tell "stopped right
away" apart from "healed everything, then noticed the flag"; both return
`Cancelled`, because the flag is checked again right after healing.

Generalize the main-graph cancellation test into a helper that cancels
any phase at 10% of its work and checks that at most 2 items per build
thread complete afterwards, and add test_building_cancelled_during_heal:
it builds an HNSW segment, deletes a quarter of its points (below the
default healing_threshold of 0.3), and cancels the rebuild while healing.

Verified the test fails with the heal stop check removed ("migrate was
completed despite cancellation"). Measured: 8 items after cancel with 8
build threads, out of 3000-5700 to heal.

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 08:32:39 +02:00
dependabot[bot] b036949171 build(deps): bump docker/setup-buildx-action from 4.3.0 to 4.4.1 (#10722)
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 4.3.0 to 4.4.1.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/37fe631027851001ddb9b187196cc803df7f5f0e...f87e5991a6d7451dcb8d9637bfbc97413f497069)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-version: 4.4.1
  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-09-22 08:10:13 +02:00
dependabot[bot] 439d4025aa build(deps): bump serde_with from 3.22.0 to 3.23.0 (#10732)
Bumps [serde_with](https://github.com/jonasbb/serde_with) from 3.22.0 to 3.23.0.
- [Release notes](https://github.com/jonasbb/serde_with/releases)
- [Commits](https://github.com/jonasbb/serde_with/compare/v3.22.0...v3.23.0)

---
updated-dependencies:
- dependency-name: serde_with
  dependency-version: 3.23.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-09-22 07:39:25 +02:00
dependabot[bot] 526882cd0a build(deps): bump codecov/codecov-action from 7.0.0 to 7.1.1 (#10721)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 7.0.0 to 7.1.1.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/fb8b3582c8e4def4969c97caa2f19720cb33a72f...303a32d7a59b442fa8d48b6a1cc6825c09c847a5)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-version: 7.1.1
  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-09-22 07:29:30 +02:00
dependabot[bot] a5f8628d7b build(deps): bump object_store from 0.14.1 to 0.14.2 (#10727)
Bumps [object_store](https://github.com/apache/arrow-rs-object-store) from 0.14.1 to 0.14.2.
- [Release notes](https://github.com/apache/arrow-rs-object-store/releases)
- [Changelog](https://github.com/apache/arrow-rs-object-store/blob/main/CHANGELOG-old.md)
- [Commits](https://github.com/apache/arrow-rs-object-store/compare/v0.14.1...v0.14.2)

---
updated-dependencies:
- dependency-name: object_store
  dependency-version: 0.14.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-09-22 07:24:24 +02:00
dependabot[bot] d68ab4aa01 build(deps): bump actix-multipart from 0.8.1 to 0.8.2 (#10729)
Bumps [actix-multipart](https://github.com/actix/actix-web) from 0.8.1 to 0.8.2.
- [Release notes](https://github.com/actix/actix-web/releases)
- [Changelog](https://github.com/actix/actix-web/blob/main/CHANGES.md)
- [Commits](https://github.com/actix/actix-web/compare/multipart-v0.8.1...multipart-v0.8.2)

---
updated-dependencies:
- dependency-name: actix-multipart
  dependency-version: 0.8.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-09-22 07:20:58 +02:00
dependabot[bot] 789c168e2d build(deps): bump syn from 3.0.5 to 3.0.6 (#10730)
Bumps [syn](https://github.com/dtolnay/syn) from 3.0.5 to 3.0.6.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/3.0.5...3.0.6)

---
updated-dependencies:
- dependency-name: syn
  dependency-version: 3.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-09-22 07:18:30 +02:00
dependabot[bot] 4783cef693 build(deps): bump rustix from 1.1.4 to 1.1.5 (#10726)
Bumps [rustix](https://github.com/bytecodealliance/rustix) from 1.1.4 to 1.1.5.
- [Release notes](https://github.com/bytecodealliance/rustix/releases)
- [Changelog](https://github.com/bytecodealliance/rustix/blob/main/CHANGES.md)
- [Commits](https://github.com/bytecodealliance/rustix/compare/v1.1.4...v1.1.5)

---
updated-dependencies:
- dependency-name: rustix
  dependency-version: 1.1.5
  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-09-22 07:10:18 +02:00
dependabot[bot] f318eb7e64 build(deps): bump rustls from 0.23.44 to 0.23.45 (#10723)
Bumps [rustls](https://github.com/rustls/rustls) from 0.23.44 to 0.23.45.
- [Release notes](https://github.com/rustls/rustls/releases)
- [Changelog](https://github.com/rustls/rustls/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rustls/rustls/compare/v/0.23.44...v/0.23.45)

---
updated-dependencies:
- dependency-name: rustls
  dependency-version: 0.23.45
  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-09-22 07:08:06 +02:00
dependabot[bot] 0fdec39044 build(deps): bump rui314/setup-mold (#10719)
Bumps [rui314/setup-mold](https://github.com/rui314/setup-mold) from 7e4f20ad28a2e8ca6fd0892ccf72e2abb706b9c3 to 10ca16bf91dc22e05ebdc935cad9c75ea248f621.
- [Commits](https://github.com/rui314/setup-mold/compare/7e4f20ad28a2e8ca6fd0892ccf72e2abb706b9c3...10ca16bf91dc22e05ebdc935cad9c75ea248f621)

---
updated-dependencies:
- dependency-name: rui314/setup-mold
  dependency-version: 10ca16bf91dc22e05ebdc935cad9c75ea248f621
  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-09-22 07:06:35 +02:00
dependabot[bot] f3220a1683 build(deps): bump cc from 1.4.5 to 1.4.7 (#10725)
Bumps [cc](https://github.com/rust-lang/cc-rs) from 1.4.5 to 1.4.7.
- [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.4.5...cc-v1.4.7)

---
updated-dependencies:
- dependency-name: cc
  dependency-version: 1.4.7
  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-09-22 07:05:52 +02:00
qdrant-cloud-bot 242a8d3b1e Warn when distributed mode has API key but enforce_internal_auth is off (#10718)
* Warn when distributed mode has API key but enforce_internal_auth is off

Make the insecure internal (p2p) gRPC configuration visible at startup so
operators enable enforcement after rolling upgrades complete.

* Shorten enforce_internal_auth warning message

Drop the rolling-upgrade guidance from the log line.
2026-09-21 21:04:19 +02:00
qdrant-cloud-bot 07caef93fd build(deps): bump anyio from 4.13.0 to 4.14.2 in /tests (#10713)
Bumps anyio from 4.13.0 to 4.14.2 (same change as #10703, retargeted to dev).
2026-09-21 15:13:29 +02:00
Anton Antonov 7c8dc0e08d test: skip three known consensus failures (#10712) 2026-09-21 14:01:20 +03:00
Anton Antonov c8c8895beb test(consensus): prove leader removal can stall (#10684)
* test(consensus): prove leader removal can stall

A departing leader can clear peer addresses before its queued commit
notification reaches the surviving voter. Control the removal append
and its acknowledgement to reproduce that ordering without fixed sleeps
or stopping the leader process.

Check the actual notification failure and the survivor's commit index.
Keep follower-removal and three-voter controls, and cover both the default
removal wait and timeout=60.

* test: timeout=60s with follower or leader

Signed-off-by: Anton Antonov <anton.synd.antonov@gmail.com>

* test: fail when leader removal loses its commit

Require the surviving voter to receive the committed removal and remain
operational. Fail explicitly when the commit notification never reaches
its gate instead of treating the stuck cluster as success.

Both two-node leader-removal cases now fail against the existing bug.
The follower-removal and three-node controls pass.

---------

Signed-off-by: Anton Antonov <anton.synd.antonov@gmail.com>
2026-09-21 12:38:20 +03:00
Anton Antonov 4c4c3d967e test: improve consensus test suite (#10671)
* test: add a gated proxy for peer RPCs

Pause one selected internal request while other peer traffic continues.
Preserve payloads, metadata, deadlines, and cancellation so consensus
tests can control transfer timing without blocking unrelated requests.

Cover forwarding, independent gates, and cleanup with socket tests.

* test: connect peer proxies to consensus clusters

Let consensus tests route internal RPCs through request gates. Keep each
proxy alive across peer restarts so advertised addresses remain stable,
and close all proxies during test cleanup.

Wait for the upstream gRPC connection before returning from proxied
startup. Verify consensus progress during a held WAL-delta request,
recovery data, and restart behavior with both URI configuration modes.

* test: fix potentially misleading peer proxy method names

Explicitly state the guarantees, or lack of.

Signed-off-by: Anton Antonov <anton.synd.antonov@gmail.com>

* test: add support for hold_snapshot_download

Removes flakiness from snapshot-related consensus tests too

Signed-off-by: Anton Antonov <anton.synd.antonov@gmail.com>

* test: improve asserts when force deleting peer

Actually verify survivors recover and retain the expected data.
Making sure no data loss happens.

Signed-off-by: Anton Antonov <anton.synd.antonov@gmail.com>

* test: add OsError socket handling + explicit wal_delta tests

Signed-off-by: Anton Antonov <anton.synd.antonov@gmail.com>

* test: reject zero as a defined consensus leader

* test: recheck leader agreement on each poll

After a restart, the leader can change during election. Let the cluster
wait resample the leader on each poll and require agreement on a nonzero
leader before the snapshot test starts its transfer.

Keep explicit leader checks for existing callers, membership-size checks,
and the existing timeout. Cover election changes and offline peers.

* test: verify independent snapshot download gates

* test: use a positive peer connection deadline

* test: cover recovery after the removed source exits

* chore: add clarifying comment on timeout=0 usage

It's not obvious at first why it's like so.

Signed-off-by: Anton Antonov <anton.synd.antonov@gmail.com>

* test: share consensus response gates

Move response gates, their tests, and Raft decoding from the leader
removal proof into the base test infrastructure. Both removal scenarios
can then use the same successful-response check.

* test: support selective RPC blocking

Keep a removed source unaware of membership changes while its transfer
continues. Block its Raft traffic in both directions so election
attempts cannot disrupt survivor recovery.

* test: make source removal scenarios deterministic

Separate recovery after source exit from late data sent by a removed
source. Require a successful receiver response in the late scenario,
and retain complete data and replica-state checks in both cases.

* test: refactor timeouts and deadlines

* Cancellation happens after observing the intended phase, without an RPC deadline.
* Separate deadline tests cover held requests, upstream work, and held responses.

Signed-off-by: Anton Antonov <anton.synd.antonov@gmail.com>

* test: bound peer probes and removal requests

Give cluster probes and peer removal finite client timeouts so a stalled
HTTP request cannot leave the test waiting indefinitely.

* test: separate RPC release from termination

Keep the upstream handler blocked until the test releases it or the RPC
terminates. Use a separate termination event for cancellation assertions,
and release the handler during teardown instead of racing a fixture timer.

* test: use monotonic polling deadlines

Measure elapsed polling time with a monotonic clock so system clock
adjustments cannot shorten or extend the wait.

* test: allow more time to observe proxy events

Allow ten seconds for proxy observations and ordinary test requests.
Event and future waits still return as soon as they complete. Keep the
one-second expiry tests and document the HTTP deadline setup race.

* test: bound leader and replication requests

Limit how long leader lookup and transfer submission wait for an HTTP
response. A stalled submission must fail so the test can release its
transfer gates and clean up the peers.

* test: preserve readiness failures in diagnostics

Catch request failures while collecting cluster diagnostics, including
read timeouts. Report the original readiness failure instead of replacing
it with a diagnostic error.

* test: assert points calls for the correct collection

Signed-off-by: Anton Antonov <anton.synd.antonov@gmail.com>

* test: make sure check_cluster_size and check_leader cannot stall

Have an explicit timeout.

Signed-off-by: Anton Antonov <anton.synd.antonov@gmail.com>

* test: retry timeouts during initial leader lookup

Treat request timeouts as retryable while discovering the expected leader,
matching the subsequent leader and membership checks. Keep polling after
a transient timeout instead of aborting the cluster-status wait.

* test: ensure batch data is different

Signed-off-by: Anton Antonov <anton.synd.antonov@gmail.com>

---------

Signed-off-by: Anton Antonov <anton.synd.antonov@gmail.com>
2026-09-21 12:04:51 +03:00
Andrey VasnetsovandClaude Fable 5.1 173f9d3ed6 Split HNSWIndex::build into phase modules (#10709)
* Split additional links phase out of HNSWIndex::build

Move the per-payload-block subgraph phase, together with its existing
helpers condition_points and build_filtered_graph, from HNSWIndex::build
into hnsw/build/additional_links.rs. The phase body is moved verbatim;
the borrowed locals become explicit parameters, and the function returns
the number of vectors indexed through the subgraphs.

GPU insert context creation moves to gpu_build.rs next to the other GPU
setup helpers, so build.rs no longer needs feature-gated GPU imports.

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

* Split main graph CPU phase out of HNSWIndex::build

Move old-index healing, the single-threaded warm-up and the parallel
point insertion into hnsw/build/main_graph.rs as build_main_graph_on_cpu,
mirroring build_main_graph_on_gpu. Level assignment and the GPU attempt
stay in the orchestrator, since the GPU result decides whether the CPU
path runs at all.

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

* Extract config derivation, field subtasks and thread pool from HNSWIndex::build

Share the kilobytes-to-vectors full scan threshold conversion between
load_or_derive_config and build as derive_config, with the vector count
as an explicit argument so both call sites keep their existing divisor.

Move the per-field progress subtasks into additional_links_fields next
to the phase that consumes them, and the rayon pool with its low-priority
spawn handler into build_thread_pool.

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

* Extract GPU attempt, graph saving and the deleted-points check from build

upload_and_build_main_graph in gpu_build.rs owns the "any graph will be
built" gate, the upload, the main graph attempt and its timing log, so
build.rs keeps one cfg pair and adopts the GPU graph with an if-let.

save_graph builds the links format param and calls into_graph_layers in
one place, since the param borrows the inline vectors.

The debug-only walk over deleted points becomes a predicate over
links_empty under debug_assert!.

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

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-21 08:22:47 +02:00
Andrey VasnetsovandClaude Fable 5.1 6dd9b8002a Cancellable open and live_reload for read-only edge shards (#10699)
* Cancellable open and live_reload for read-only edge shards

Add cooperative cancellation to ReadOnlyEdgeShard::open and
ReadOnlyEdgeShard::live_reload, following the contract of
EdgeShardReadWithCancellation: a shared stop flag is checked between
stages and between segments, a set flag yields OperationError::Cancelled
and never a partial result, and the flag is never set or reset by the
callee.

New entry points: ReadOnlyEdgeShard::open_with_cancellation and
ReadOnlyEdgeShard::live_reload_with_cancellation. The existing open,
live_reload and live_reload_with keep their signatures and delegate with
a fresh flag.

The flag is propagated into ReadOnlySegment::schedule_open. The staged
handle carries it, so both the prefetch staging and finish check it
between components. The edge loader propagates a Cancelled error instead
of logging it as an unloadable segment.

The holder swap and the config re-derivation that follows it are one
indivisible step, so a cancellation never leaves a config lagging behind
the segment set. Segment reloads stay atomic under their write lock, so a
cancelled live_reload leaves the shard consistent and the next one
continues from there.

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

* Check cancellation between vector component preopens

A preopen polls its open future once, so real work starts right away.
Checking the flag between the storage, quantized vectors and index
preopens of a dense vector, and between the storage and index preopens
of a sparse vector, keeps the staging contract that the flag is observed
between components.

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

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-21 07:34:03 +02:00
Andrey VasnetsovandClaude Fable 5.1 c4b2d1cadd test: wait for applied CommitRead before cleanup in test_resharding_deferred[up] (#10702)
The test compared raft commit indices across peers before issuing shard
cleanup. A follower advances its commit index before it applies the entry,
so cleanup could still race the follower applying CommitRead, which
invalidates running clean tasks and makes the endpoint return 500.

Wait for every peer to report the applied `read_hash_ring_committed`
resharding stage via telemetry instead. Reading it takes the same shard
holder lock as the consensus handler, so an observed stage is fully applied.

Repurpose the unused stage check helper to read the stage from telemetry,
since the `comment` field it read no longer exists.

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-21 07:23:21 +02:00
Daniel Borosandtimvisee 76a8647728 Follow-up tests for #10349: pinned findings on the merged code (#10661)
* test: pin persisted proxy segments follow-up findings

* fix: tests

* Respect `up_to` in flushing pending proxy changes

* Fix unproxy divergence, centralize logic in single shared function

* Don't pass locked segments we don't use

* Close the post-swap window losing acknowledged proxy changes

`finish_optimization` left a window between `swap_new` and the end of the
function where the propagated proxy changes were durable in no reachable
place. The proxies had left the holder, so a flush pass no longer saw them
as unsaved work and acknowledged the WAL past what their pending changes
logs persisted, while their source files were still on disk contradicting
the newer state. Their `ack_pin` was only registered at the very end, and
the optimized segment, the only durable home of those changes, had no
version file yet, so `normalize_segment_dir` deleted it on the next load.
A failure or crash in that window lost every change past the proxies' logs.

Close it from both sides, reordering only:

- Save the optimized segment's version file right after the flush that made
  the propagated changes durable, before the swap. That flush is what
  `SegmentBuilder::build` postponed the version file for, so the segment is
  loadable from the swap onwards.
- Register the proxies' deferred destruction (and with it their `ack_pin`)
  under the same write lock that evicted them, so no flush pass can observe
  the holder without either the proxies or their pin.

Collecting the deferred point ids moves up with the registration, as it
borrows the swapped-out proxies. The deferred destruction still cannot run
before the manifest is synced: `locked_proxies` holds the segments alive
until the end of the function, so `try_drop_data` retries until then.

* Rename the optimization test hook after the window it guards

The hook no longer sits before the version save, it marks a failure
anywhere in the window after the optimized segment was swapped in. Rename
it and the test accordingly, and restate the test doc as the invariant that
window must uphold rather than the bug it used to describe.

* Pin WAL ack while creating snapshot

* Patch test that was stuck

* Initialize necessary feature flags in tests

* Correctly propagate changes in two stages, lock updates on second stage

* Use existing WAL ack pinning infrastructure

* test: pin unproxy phase 2 propagation failure losing acknowledged changes

* test: pin WAL ack pin at zero suppressing clock persistence

* Fix propagate and unproxy data consistency error on failure

* Store clocks before checking WAL ack pin

* fix: wait for the flush worker when stopping it in tests

* fix: linter

---------

Co-authored-by: timvisee <tim@visee.me>
2026-09-18 12:33:18 +02:00