Skip to main content

nodedb_vector/vamana/
shard.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! `Shard` trait — abstracts local-mmap vs future remote-RPC partition fetch.
4//!
5//! Vamana's beam-search rerank step needs full-precision FP32 vectors for
6//! exact distance computation.  On a single node these live in an mmap region;
7//! in a distributed deployment they would come from a remote RPC or RDMA read.
8//! This trait provides that seam without coupling search to any I/O mechanism.
9
10/// Abstraction over a single partition's vector store.
11///
12/// `LocalShard` is the single-node implementation backed by an in-memory
13/// `Vec`.  Future impls (RDMA, QUIC RPC) implement the same interface.
14pub trait Shard: Send + Sync {
15    /// Vector dimensionality.
16    fn dim(&self) -> usize;
17
18    /// Fetch the full-precision FP32 vector at the given node index.
19    ///
20    /// Returns `None` if `idx` is out of range.  Local = mmap read (or
21    /// `Vec` copy in tests); remote = future RPC/RDMA fetch.
22    fn fetch_fp32(&self, idx: usize) -> Option<Vec<f32>>;
23}
24
25/// Single-node shard backed by an in-memory vector slice.
26///
27/// In production this would wrap a mmap pointer to the SSD-resident
28/// full-precision block described in `storage::VamanaStorageLayout`.
29/// For the current tier we hold owned `Vec<Vec<f32>>` so the type compiles
30/// and tests pass without io_uring plumbing.
31pub struct LocalShard {
32    /// Vector dimensionality.
33    pub dim: usize,
34    /// Dense FP32 vector storage indexed by node order.
35    pub vectors: Vec<Vec<f32>>,
36}
37
38impl LocalShard {
39    /// Create a `LocalShard` from a slice of FP32 vectors.
40    ///
41    /// All vectors must have the same length (`dim`).  The constructor
42    /// is infallible; callers are expected to validate dimensions upstream.
43    pub fn new(dim: usize, vectors: Vec<Vec<f32>>) -> Self {
44        Self { dim, vectors }
45    }
46}
47
48impl Shard for LocalShard {
49    fn dim(&self) -> usize {
50        self.dim
51    }
52
53    fn fetch_fp32(&self, idx: usize) -> Option<Vec<f32>> {
54        self.vectors.get(idx).cloned()
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61
62    #[test]
63    fn local_shard_returns_correct_vector() {
64        let vecs = vec![
65            vec![1.0_f32, 2.0, 3.0],
66            vec![4.0_f32, 5.0, 6.0],
67            vec![7.0_f32, 8.0, 9.0],
68        ];
69        let shard = LocalShard::new(3, vecs.clone());
70
71        assert_eq!(shard.dim(), 3);
72        assert_eq!(shard.fetch_fp32(0), Some(vecs[0].clone()));
73        assert_eq!(shard.fetch_fp32(1), Some(vecs[1].clone()));
74        assert_eq!(shard.fetch_fp32(2), Some(vecs[2].clone()));
75    }
76
77    #[test]
78    fn local_shard_out_of_range_returns_none() {
79        let shard = LocalShard::new(2, vec![vec![1.0, 2.0]]);
80        assert!(shard.fetch_fp32(1).is_none());
81        assert!(shard.fetch_fp32(100).is_none());
82    }
83
84    #[test]
85    fn local_shard_empty_returns_none() {
86        let shard = LocalShard::new(4, vec![]);
87        assert!(shard.fetch_fp32(0).is_none());
88    }
89}