nodedb_vector/segment_backing/backing.rs
1// SPDX-License-Identifier: Apache-2.0
2
3/// Storage abstraction for HNSW vector data.
4///
5/// Two implementations coexist:
6/// - [`crate::segment_backing::PlainMmapBacking`]: zero-copy mmap of a plaintext NDVS file (Origin).
7/// - `PagedbBacking`: encrypted segment read via pagedb (Lite, task 2a.2).
8///
9/// Implementations are responsible for vector retrieval; HNSW graph traversal
10/// makes no storage-level decisions.
11///
12/// # `Send + Sync` bound
13///
14/// The bound allows consumers to park the backing in an
15/// `Arc<dyn VectorSegmentBacking>` and dispatch across tasks. Lite will
16/// require this in task 2a.2.
17///
18/// # Return type for `get_vector`
19///
20/// `-> Option<&[f32]>` borrows from `&self`. This is correct for
21/// `PlainMmapBacking` (slice into mmap region lives as long as the backing)
22/// and for the planned `PagedbBacking` (which will hold a long-lived
23/// decrypted vector slab in a `MmapView` field on `self`).
24///
25/// If a future backing genuinely cannot return a `&self`-lifetime slice
26/// without copying, the signature can be changed to `Cow<'_, [f32]>` in a
27/// follow-up refactor. For now both known impls support the zero-copy path.
28pub trait VectorSegmentBacking: Send + Sync {
29 /// Number of vectors stored in this segment.
30 fn len(&self) -> usize;
31
32 /// Returns `true` when the segment contains no vectors.
33 fn is_empty(&self) -> bool {
34 self.len() == 0
35 }
36
37 /// Dimensionality of each stored vector.
38 fn dim(&self) -> usize;
39
40 /// Fetch one vector by local position id (0..len).
41 ///
42 /// Backings should make this cheap — zero-copy where possible, decrypted
43 /// page lookup on cold paths. Returns `None` if `id` is out of bounds.
44 fn get_vector(&self, id: u32) -> Option<&[f32]>;
45
46 /// Surrogate id for a local position.
47 ///
48 /// Returns `None` if `id` is out of bounds.
49 fn get_surrogate(&self, id: u32) -> Option<u64>;
50
51 /// Optional prefetch hint.
52 ///
53 /// Implementations may call `madvise(MADV_WILLNEED)` (mmap) or warm a
54 /// pagedb page cache entry. Default is a no-op.
55 fn prefetch(&self, _id: u32) {}
56}