Skip to content

vector-store: diskann basic queries - #535

Draft
Akvear wants to merge 4 commits into
scylladb:masterfrom
Akvear:diskann-basic-queries
Draft

vector-store: diskann basic queries#535
Akvear wants to merge 4 commits into
scylladb:masterfrom
Akvear:diskann-basic-queries

Conversation

@Akvear

@Akvear Akvear commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

This PR focuses on a simple one-by-one actor loop implementation of:

  • AddVector
  • RemoveVector
  • Ann
  • Count

The aim is to establish a correctly behaving baseline to build upon.

Things that are currently beyond the scope of this PR:

  • Partitioning
  • Dispatching tasks to different workers

Ref: VECTOR-714

@Akvear Akvear changed the title Diskann basic queries vector-store: diskann basic queries Jul 27, 2026
@Akvear
Akvear requested a review from Copilot July 27, 2026 09:09

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds baseline DiskANN support for vector insertion, removal, ANN search, and counting.

Changes:

  • Implements a single-task DiskANN actor and ID mapping.
  • Adds search validation, metric handling, and count support.
  • Adds unit and integration tests.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.

File Description
crates/vector-store/src/vs_index/diskann.rs Implements core DiskANN operations.
crates/vector-store/tests/integration/diskann.rs Adds DiskANN integration coverage.
crates/vector-store/tests/integration/main.rs Registers the DiskANN tests.

Comment on lines +239 to +242
if let Err(err) = self
.index
.insert(&FullPrecision, &DefaultContext, &id, embedding.as_slice())
.await

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAX_POINTS in general is a problem, should it be configurable, or a constant or something else entirely? It controls the maximum number of points that a DiskANNIndex can keep in memory. Diskann explicitly returns an Err on overflow. Unlike usearch, the memory can't be resized, that's why it's good to decide on a correct approach earlier rather than later.

Comment thread crates/vector-store/src/vs_index/diskann.rs
Comment thread crates/vector-store/src/vs_index/diskann.rs
Comment thread crates/vector-store/tests/integration/diskann.rs Outdated
Comment on lines +140 to +142
VsIndex::RemoveVector { primary_id, .. } => {
state.remove_vector(primary_id).await;
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I write additional tests or keep them consistent with usearch?

@Akvear
Akvear force-pushed the diskann-basic-queries branch 2 times, most recently from 1c4caaf to 6ae9367 Compare July 27, 2026 11:20
@Akvear
Akvear marked this pull request as ready for review July 27, 2026 12:06
@Akvear
Akvear requested a review from QuerthDP July 27, 2026 12:06
@Akvear
Akvear force-pushed the diskann-basic-queries branch 6 times, most recently from 1c1fd23 to 617ff5f Compare July 28, 2026 09:19
@Akvear
Akvear requested a review from ewienik July 28, 2026 14:17

@ewienik ewienik left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the current solution uses a ExternalId <-> Epoch mapping
The epoch is the 16 oldest bits of the PrimaryId
because diskann requires u32 external ids we map the 32 LSBs
to the epoch number and resolve this number when to recreate the PrimaryId
This doesnt fix the fact that only 2^32-1 ids can be distributed
but that's the limitation of diskann
the solution currently operates on a sigle tokio worker

I understand that limitation for 32bits is from memory DiskAnn provider. Using PrimaryId::idx() as an ExternalId seems a bad idea - Epoch is a part of the PrimaryId. In the same operation wee can remove old PrimaryId, increase only Epoch and insert a epoch-changed-only PrimaryId - as we are proceeding concurrently we should take care of the Epoch in the ExternalId.
Consider using a mapping between PrimaryId and internal 32 bit counter as ExternalId. After removing some PrimaryId (and associated ExternalId) you can store unused ExternalId in a cache.
Consider using BTreeMap instead of HashMap -> with a large number of vectors realloc of HashMap takes significant time.

Comment thread crates/vector-store/src/vs_index/diskann.rs Outdated
@Akvear
Akvear force-pushed the diskann-basic-queries branch from 617ff5f to f1a0dda Compare July 29, 2026 09:53
@Akvear

Akvear commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

Changes:

  • changed ExternalId <-> Epoch mapping to ExternalId <-> PrimaryId; DiskANN uses soft deletes, so reusing ids here might not be the best idea.

@Akvear
Akvear requested a review from ewienik July 29, 2026 10:16
@Akvear
Akvear force-pushed the diskann-basic-queries branch from f1a0dda to 3112dce Compare July 29, 2026 14:32
@Akvear

Akvear commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

microsoft/DiskANN#1206 was just merged into DiskANN. It not easy to really understand what's going on but this might allow us rely on diskANN internal id mapping instead of our own. Apart from that it also provides improvements towards safety of concurrent operations. It has some open follow ups so it might be a bit early to adopt this right now, but it's worth keeping an eye on this.

Relevant docs:

//! An in-memory provider for the DiskANN graph index.
//!
//! This type supports the following:
//!
//! * Arbitrary external IDs for store data (provided they satisfy [`Id`].
//! * Support for concurrent insertions, deletions, and searches.
//! * Specialized implementations of [`glue::SearchAccessor::expand_beam`] enabling full
//!   inlining of distance kernels.
//!
//! Known areas for future work:
//!
//! * Insert and delete protection: The [`DiskANNIndex`](diskann::graph::DiskANNIndex) doesn't
//!   support ergonomic insert or delete guards to protect slots during insert or delete
//!   operations. This leaves open a situation where an item can be inserted and during
//!   the insertion algorithm, it is deleted, and then re-inserted.
//!
//!   This can cause some issue within the main indexing algorithms which assume the inserted
//!   ID is present but requires upstream changes to properly fix.
//!
//! * Failed insert rollback: again, this needs some upstream changes to full support.
//!
//! * Quantization + reranking: The current version of this index targets just a single
//!   data-store and is planned to be addressed in the near future.
//!
//! * Lack of save/load support: The index is currently ephemeral, but there are plans to
//!   address this gap.

@Akvear
Akvear force-pushed the diskann-basic-queries branch from 3112dce to 3eb537b Compare July 29, 2026 15:02
Akvear added 4 commits July 29, 2026 17:09
the current solution uses a PrimaryId <-> u32 ExternalId
DiskANN unfortunately supports u32 ids with DefaultProvider
the solution currently operates on a sigle tokio worker
index.search() outputs a vector of neighbours that need distances to be added to them, hence the State::search() helper
State::ann is similar to usearch thanks to this, with the difference being no partitions in diskann as of yet.

added space_type, l_default and beam_width to DiskannParams added to unit test
these tests are the working subset of usearch tests
basically everything but filtered ann(since it's beyond scope) and http server responsiveness since it uses usearch simulator
@Akvear
Akvear force-pushed the diskann-basic-queries branch from 3eb537b to 2886672 Compare July 29, 2026 15:09
@ewienik

ewienik commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

microsoft/DiskANN#1206 was just merged into DiskANN. It not easy to really understand what's going on but this might allow us rely on diskANN internal id mapping instead of our own.

//! * Arbitrary external IDs for store data (provided they satisfy [Id].

I think this is a good catch. I'd rather go with this new code as it seems it allows to use our PrimaryId instead of problematic mapping with u32. Do you think it is possible?

Apart from that it also provides improvements towards safety of concurrent operations. It has some open follow ups so it might be a bit early to adopt this right now, but it's worth keeping an eye on this.

//! * Support for concurrent insertions, deletions, and searches.

//! Known areas for future work:
//!
//! * Insert and delete protection: The DiskANNIndex doesn't
//! support ergonomic insert or delete guards to protect slots during insert or delete
//! operations. This leaves open a situation where an item can be inserted and during
//! the insertion algorithm, it is deleted, and then re-inserted.
//!
//! This can cause some issue within the main indexing algorithms which assume the inserted
//! ID is present but requires upstream changes to properly fix.

We've similar situation with usearch - there is implemented specific Permit structure to support concurrent operations. Additionally we guarantee that every delete/insert operation will use different PartitionId (Epoch variable) - so we should be safe.

I think removing our mapping PrimaryId <-> u32 has a lot of benefits and I don't see issues with a new inmem provider. Can you make a try to update dependencies?

@Akvear

Akvear commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

I think removing our mapping PrimaryId <-> u32 has a lot of benefits and I don't see issues with a new inmem provider. Can you make a try to update dependencies?

I can either do this right now, or we can wait until crate version 0.56.0 is released, but ETA is unknown.

@ewienik

ewienik commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

I think removing our mapping PrimaryId <-> u32 has a lot of benefits and I don't see issues with a new inmem provider. Can you make a try to update dependencies?

I can either do this right now, or we can wait until crate version 0.56.0 is released, but ETA is unknown.

So better seems to do it right now to avoid problems with id mapping.

@Akvear

Akvear commented Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

microsoft/DiskANN#1303 waiting on this, because clippy fails

@ewienik

ewienik commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

microsoft/DiskANN#1303 waiting on this, because clippy fails

You can temporarily use your github PR as a patched reference in Cargo.toml

@Akvear
Akvear marked this pull request as draft July 31, 2026 13:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants