Repository navigation
[diskann-garnet] Implement XVCREATE, XVIMPORT - #1455
Jack Moffitt (metajack) wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Arbitrary-ID imports, malformed neighbor counts, reserved-ID mappings, and quantizer-state replacement can currently corrupt or fail imported indexes.
Review effort: Balanced
Findings: 4
Open (6)
Expand all intermediate blocks for arbitrary imported IDs · New Reject quantizer changes after index initialization · New Reject neighbor counts exceeding max_degree · New Reject external-ID mappings to reserved ID zero · New Document all operations that disable importing · New Remove stray Add prefix from field label · New
What changed in this PR
Adds bulk import support for externally generated DiskANN graphs through new Garnet FFI operations.
Changes:
- Adds quantizer-state injection and term import/finalization APIs.
- Persists import eligibility and validates imported graph terms.
- Adds import tests, documentation, and a patch version bump.
| File | Description |
|---|---|
Cargo.lock |
Updates the package version. |
diskann-garnet/Cargo.toml |
Bumps the crate version. |
diskann-garnet/diskann-garnet.nuspec |
Bumps the NuGet version. |
diskann-garnet/README.md |
Documents bulk import usage. |
diskann-garnet/docs/ffi-design.rs |
Documents import FFI APIs. |
diskann-garnet/docs/data-design.md |
Documents imported-term storage. |
diskann-garnet/src/dyn_index.rs |
Exposes import operations through DynIndex. |
diskann-garnet/src/ffi_tests.rs |
Adds import and FFI validation tests. |
diskann-garnet/src/fsm.rs |
Adds explicit internal-ID claiming. |
diskann-garnet/src/garnet.rs |
Adds term conversion and existence checks. |
diskann-garnet/src/lib.rs |
Exposes import-related C APIs. |
diskann-garnet/src/provider.rs |
Implements import gating, validation, and finalization. |
diskann-garnet/src/quantization.rs |
Supports externally supplied quantizer state. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| let mut id_minter = self.id_minter.write().unwrap(); | ||
| let max_id = id_minter.next_id.saturating_sub(1); | ||
|
|
||
| if id > max_id { | ||
| // NOTE: expand_to will modify max_block if needed. | ||
| self.expand_to(&mut id_minter, ctx, id)?; | ||
| id_minter.next_id = id + 1; | ||
| } |
| pub(crate) fn set_quant_state(&self, context: &Context, state: &[u8]) -> bool { | ||
| // NOTE: We change the quantizer before persisting to Garnet because if anything | ||
| // fails, the vector set will be thrown away. | ||
| if let Some(quantizer) = &self.quantizer | ||
| && quantizer.deserialize(state).is_ok() |
| Term::Neighbors => { | ||
| value.len() == (self.max_degree + 1) * mem::size_of::<u32>() | ||
| } |
| Term::IntMap => { | ||
| if value.len() != mem::size_of::<u32>() { | ||
| return false; | ||
| } | ||
|
|
||
| let eid = GarnetId::from(id); | ||
| self.callbacks.write_eid(&context.term(term), &eid, value) | ||
| } |
|
|
||
| At the FFI layer, `can_import` checks eligibility. | ||
|
|
||
| Indices created with `XVCREATE` are eligible for import until import finalization is complete. |
| state: *const u8, | ||
| state_len: usize, | ||
| ) -> bool { | ||
| let index = unsafe { &*index_ptr.cast::<Index>() }; |
| /// `index_ptr` must be a live index returned by `create_index`, and `ctx` must identify its storage. | ||
| #[unsafe(no_mangle)] | ||
| pub unsafe extern "C" fn can_import(ctx: u64, index_ptr: *const c_void) -> bool { | ||
| let index = unsafe { &*index_ptr.cast::<Index>() }; |
| value: *const u8, | ||
| value_len: usize, | ||
| ) -> bool { | ||
| let index = unsafe { &*index_ptr.cast::<Index>() }; |
| task_idx: usize, | ||
| task_count: usize, | ||
| ) -> u8 { | ||
| let index = unsafe { &*index_ptr.cast::<Index>() }; |
50b606d to
84d419c
Compare
Codecov Report❌ Patch coverage is
❌ Your patch status has failed because the patch coverage (84.74%) is below the target coverage (90.00%). You can increase the patch coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## main #1455 +/- ##
==========================================
- Coverage 90.93% 90.93% -0.01%
==========================================
Files 583 583
Lines 116854 117286 +432
==========================================
+ Hits 106266 106652 +386
- Misses 10588 10634 +46
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
84d419c to
d99ff9a
Compare


This PR adds direct import of externally stored diskann graphs to Garnet. This can be useful for interoperating with indices created with other DiskANN systems.
It adds two new commands,
XVCREATEto create a vector set without doing aVADD, andXVIMPORTto directly import index terms to a vector set. To make quantizer state compatible,XVCREATEallows directly specifying the quantizer state.Finalization of import checks that all required terms are present and have sensical sizes, but does not verify quantize vectors match unquantized vectors or that the neighbor lists make sense. It is expected that the graph is correctly constructed in the external system.
This currently requires that the internal ID 0 not be used in the graph since it will get used by the start point once the graph is imported.