reddb_server/storage/index/
mod.rs1pub mod bloom_segment;
24pub mod heavy_hitters;
25pub mod registry;
26pub mod stats;
27pub mod tid_bitmap;
28pub mod zone_map;
29pub mod zone_map_persist;
30
31pub use bloom_segment::{BloomSegment, BloomSegmentBuilder, HasBloom};
32pub use heavy_hitters::HeavyHitters;
33pub use registry::{IndexRegistry, IndexScope, SharedIndex};
34pub use stats::{IndexKind, IndexStats};
35pub use zone_map::{ZoneDecision, ZoneMap, ZonePredicate};
36
37use std::fmt;
38
39#[derive(Debug, Clone)]
41pub enum IndexError {
42 InvalidKey(String),
44 InvalidValue(String),
46 ReadOnly,
48 Full,
50 Storage(String),
52}
53
54impl fmt::Display for IndexError {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 match self {
57 IndexError::InvalidKey(m) => write!(f, "invalid key: {m}"),
58 IndexError::InvalidValue(m) => write!(f, "invalid value: {m}"),
59 IndexError::ReadOnly => write!(f, "index is read-only"),
60 IndexError::Full => write!(f, "index capacity exceeded"),
61 IndexError::Storage(m) => write!(f, "storage error: {m}"),
62 }
63 }
64}
65
66impl std::error::Error for IndexError {}
67
68pub trait IndexBase: Send + Sync {
72 fn name(&self) -> &str;
74
75 fn kind(&self) -> IndexKind;
77
78 fn stats(&self) -> IndexStats;
80
81 fn definitely_absent(&self, _key_bytes: &[u8]) -> bool {
88 false
89 }
90}
91
92pub trait PointIndex<K: ?Sized, V>: IndexBase {
95 fn insert(&mut self, key: &K, value: V) -> Result<(), IndexError>;
97
98 fn remove(&mut self, key: &K) -> Result<usize, IndexError>;
100
101 fn lookup(&self, key: &K) -> Vec<V>;
103
104 fn contains(&self, key: &K) -> bool {
106 !self.lookup(key).is_empty()
107 }
108}
109
110pub trait RangeIndex<K: ?Sized, V>: PointIndex<K, V> {
112 fn range(&self, start: Option<&K>, end: Option<&K>) -> Vec<(Vec<u8>, V)>;
115
116 fn distinct_keys(&self) -> usize;
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123 use crate::storage::index::BloomSegment;
124 use std::collections::BTreeMap;
125
126 struct TestBTree {
128 name: String,
129 map: BTreeMap<Vec<u8>, Vec<u64>>,
130 bloom: BloomSegment,
131 }
132
133 impl TestBTree {
134 fn new(name: &str) -> Self {
135 Self {
136 name: name.to_string(),
137 map: BTreeMap::new(),
138 bloom: BloomSegment::with_capacity(1024),
139 }
140 }
141 }
142
143 impl IndexBase for TestBTree {
144 fn name(&self) -> &str {
145 &self.name
146 }
147 fn kind(&self) -> IndexKind {
148 IndexKind::BTree
149 }
150 fn stats(&self) -> IndexStats {
151 IndexStats {
152 entries: self.map.values().map(|v| v.len()).sum(),
153 distinct_keys: self.map.len(),
154 approx_bytes: 0,
155 kind: IndexKind::BTree,
156 has_bloom: true,
157 index_correlation: 0.0,
158 }
159 }
160 fn definitely_absent(&self, key_bytes: &[u8]) -> bool {
161 self.bloom.definitely_absent(key_bytes)
162 }
163 }
164
165 impl PointIndex<[u8], u64> for TestBTree {
166 fn insert(&mut self, key: &[u8], value: u64) -> Result<(), IndexError> {
167 self.bloom.insert(key);
168 self.map.entry(key.to_vec()).or_default().push(value);
169 Ok(())
170 }
171 fn remove(&mut self, key: &[u8]) -> Result<usize, IndexError> {
172 Ok(self.map.remove(key).map(|v| v.len()).unwrap_or(0))
173 }
174 fn lookup(&self, key: &[u8]) -> Vec<u64> {
175 self.map.get(key).cloned().unwrap_or_default()
176 }
177 }
178
179 impl RangeIndex<[u8], u64> for TestBTree {
180 fn range(&self, start: Option<&[u8]>, end: Option<&[u8]>) -> Vec<(Vec<u8>, u64)> {
181 use std::ops::Bound;
182 let lo = start.map(Bound::Included).unwrap_or(Bound::Unbounded);
183 let hi = end.map(Bound::Excluded).unwrap_or(Bound::Unbounded);
184 self.map
185 .range::<[u8], _>((lo, hi))
186 .flat_map(|(k, vs)| vs.iter().map(move |v| (k.clone(), *v)))
187 .collect()
188 }
189 fn distinct_keys(&self) -> usize {
190 self.map.len()
191 }
192 }
193
194 #[test]
195 fn point_index_roundtrip() {
196 let mut idx = TestBTree::new("test");
197 idx.insert(b"alpha", 1).unwrap();
198 idx.insert(b"alpha", 2).unwrap();
199 idx.insert(b"beta", 3).unwrap();
200
201 assert_eq!(idx.lookup(b"alpha"), vec![1, 2]);
202 assert!(idx.contains(b"beta"));
203 assert!(!idx.contains(b"gamma"));
204 }
205
206 #[test]
207 fn bloom_prunes_absent_keys() {
208 let mut idx = TestBTree::new("test");
209 idx.insert(b"alpha", 1).unwrap();
210 assert!(!idx.definitely_absent(b"alpha"));
212 assert!(idx.lookup(b"not-there").is_empty());
215 }
216
217 #[test]
218 fn range_iteration() {
219 let mut idx = TestBTree::new("test");
220 for (i, k) in [b"a", b"b", b"c", b"d"].iter().enumerate() {
221 idx.insert(*k, i as u64).unwrap();
222 }
223 let out = idx.range(Some(b"b"), Some(b"d"));
224 let keys: Vec<&[u8]> = out.iter().map(|(k, _)| k.as_slice()).collect();
225 assert_eq!(keys, vec![b"b".as_slice(), b"c".as_slice()]);
226 }
227
228 #[test]
229 fn stats_surface_cardinality() {
230 let mut idx = TestBTree::new("test");
231 idx.insert(b"a", 1).unwrap();
232 idx.insert(b"a", 2).unwrap();
233 idx.insert(b"b", 3).unwrap();
234 let s = idx.stats();
235 assert_eq!(s.entries, 3);
236 assert_eq!(s.distinct_keys, 2);
237 assert!(s.has_bloom);
238 }
239}