mirror of
https://github.com/qdrant/qdrant.git
synced 2026-08-03 00:20:57 -05:00
* feat: `UpdateOnlySegment` / `UpdateOnlyEdgeShard` batch writer skeleton Mirror image of the read-only pair, for the serverless updater: a shard/segment whose public surface is writes only, built for batches of many tiny operations against remote, append-only storage. Implemented: * `UpdateOnlySegment<S>` with a deliberately narrow open — id tracker, payload storage and one storage per named vector, all cold. No vector index, no quantized vectors, no payload index on the segments the writer only reads from. * `SegmentUpdateView`, the shared home of resolution logic, generic over the component traits (`VectorDataStorageRead` is a `VectorDataRead` without the index, so a segment that opens no index can produce the view). Batched `locate_points` / `point_versions` / `read_stored_points`. * `UpdateOnlyEdgeShard<S>::apply_batch`: fold the batch to one entry per point, locate the points, read only the ones that cannot be resolved from the batch alone, materialize `FullyQualifiedPoint`s, append them and tombstone the slots they replace. `todo!()`, pending the append-only components on the roadmap (appendable `DynamicStoredFlags` and `ChunkedVectors`, an appendable payload blobstore and field indexes): `store_points`, `tombstone_points`, `flush`, and creating the first appendable segment. Filter-selected operations, point sync, conditional upserts and the schema-level operations are rejected up front rather than silently skipped. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: codespell implementor → implementer in SegmentUpdateView docs Co-authored-by: Cursor <cursoragent@cursor.com> * docs: trim update-only writer docstrings to guarantees Less verbose throughout: state each function's contract — ordering, absent-value behavior, preconditions, durability — and drop narration about where types are used or why alternatives were rejected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: fold SegmentUpdateView into UpdateOnlySegment as inherent methods The view was premature: it had exactly one producer, and its trait bounds bought an unexercised option. Resolution (locate / versions / read raw) now lives as inherent methods on UpdateOnlySegment, still generic over the backend. A shared view can be extracted when a second producer appears, e.g. batched CoW moves out of regular segments. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: split edge update_only batch.rs into a module Pure move: mutation.rs (PointMutation fold + materialize), plan.rs (UpdateBatchPlan operation intake), tests.rs. PointUpdates::new/push narrowed to pub(super). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: parallel per-segment batch reads + tombstone every copy of a point locate_points and read_stored_points visit segments in parallel on a dedicated edge-update rayon pool (build_search_pool generalized to build_segment_pool with a thread-name prefix). locate_points now keeps every slot a point occupies, not just the newest copy: a rewrite or delete retires all of them. Tombstoning only the newest slot would let an older duplicate left by an interrupted move outlive the point — and resurrect it after a delete. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: point_versions returns a map keyed by internal id The id tracker's batch read is keyed by internal id already; returning AHashMap drops the positions_of reverse-lookup adapter. Absent key = unwritten slot, defaulted to version 0 at the caller. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: accept a deferred threshold when opening UpdateOnlySegment Groundwork for an external rebuilder working the same directory: the cutoff loads slots at or above it into the appendable id tracker's deferred track (same appendable-only filter as ReadOnlySegment). It hides nothing from the writer — resolution runs WithDeferred, so every point still locates at its latest slot. The edge shard passes None until the rebuilder coordination exists. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: preview_batch — resolve a batch without writing anything apply_batch and the new preview_batch share one resolution stage (resolve_batch: locate, read, materialize into per-point PointActions), so a dry-run reports exactly what an apply would do. Plus segment_configs(): per-segment configs with the write target marked. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: prefetched + parallel segment opens for the update-only writer UpdateOnlySegment::open now mirrors ReadOnlySegment::open: a per-segment CachedFs primed by preopen, config parsed once and handed to open_via. The edge shard opens segments in parallel on its pool, keeping fail-hard semantics. With Populate::No throughout, prefetches transfer no data-file content — only configs, the id tracker and the deleted flags, whose opens consume them whole anyway. Also: ReadOnlyAppendableIdTracker::preopen now tolerates the not-yet-created mappings/versions files of an empty appendable segment, matching its open's contract — previously unreachable because followers skip appendable segments on error, while the writer must open them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: edge-shard-update — dry-run batch upserts against a shard Counterpart of edge-shard-query for the write path: opens an UpdateOnlyEdgeShard over a local directory or S3/GCS object storage, generates random points shaped by the shard's own schema (segment config + payload-index schema), and logs what applying them would do — locations, versions, actions, tombstones — via preview_batch. Nothing is written: the write half is still todo!(). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: box PointAction::Store to appease clippy::large_enum_variant A resolved point is ~384 bytes while every other variant is empty. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: adapt to dev's dead-code sweep (#10030) Restore NamedVectors::remove_ref — removed as dead on dev, but the batch fold's DeleteVectors arm is now its first caller. Drop the allow(dead_code) on segment::update_only (no longer needed) and switch the writer's unread fs field to expect(dead_code), per the new ast-grep rule. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: root <111755117+qdrant-cloud-bot@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com>
This dir contains a Justfile with recipes to build/check/run examples for edge packages. Install https://github.com/casey/just to use it.
Rust Qdrant Edge package
Rust Qdrant Edge workspace lives in the publish directory. It's a separate workspace, not tied to the main workspace.
The qdrant-edge package is autogenerated by the amalgamate.py script and placed in the ./publish/qdrant-edge directory (gitignored).
If you need to make changes in the qdrant-edge package, edit the original packages in this repo (/lib in the repo root).
just rs-examples
# Or, manually:
./amalgamate.py
cargo check -p examples
cargo run -p examples --bin demo
cargo run -p examples --bin …
(see full binary list in publish/examples/src/bin)
How to publish Rust Package to crates.io
- Update the
VERSIONin publish/amalgamate.py. - Run Qdrant Edge Rust Release workflow.
Python Qdrant Edge package
It's in the python directory. It's part of the main workspace, unlike the Rust package.
just py-build
just py-examples
Or, manually:
# Setup environment
cd lib/edge/python
python -m venv .venv
source .venv/bin/activate
pip install --user maturin
# Build and install the package:
cd lib/edge/python
maturin develop --no-default-features
# Run example:
python examples/demo.py