Skip to main content

commonware_storage/index/
ordered.rs

1//! Implementation of [Ordered] that uses an ordered map internally to map translated keys to
2//! arbitrary values. Beyond the standard [Unordered] implementation, this variant adds the
3//! capability to retrieve values associated with both next and previous translated keys of a given
4//! key. There is no ordering guarantee provided over the values associated with each key. Ordering
5//! applies only to the _translated_ key space.
6
7use crate::{
8    index::{
9        Cursor as CursorTrait, Ordered, Unordered,
10        storage::{Cursor as CursorImpl, IndexEntry, Overflow, Values, push_displaced},
11    },
12    translator::{OrderedTranslator, Translator},
13};
14use commonware_runtime::{
15    Metrics,
16    telemetry::metrics::{Counter, Gauge, MetricsExt as _},
17};
18use std::{
19    collections::{
20        BTreeMap, HashMap,
21        btree_map::{
22            Entry as BTreeEntry, OccupiedEntry as BTreeOccupiedEntry,
23            VacantEntry as BTreeVacantEntry,
24        },
25    },
26    ops::Bound::{Excluded, Unbounded},
27};
28
29/// Implementation of [IndexEntry] for [BTreeOccupiedEntry].
30impl<K: Ord + Send + Sync, V: Send + Sync> IndexEntry<V> for BTreeOccupiedEntry<'_, K, V> {
31    type Key = K;
32
33    fn key(&self) -> &K {
34        BTreeOccupiedEntry::key(self)
35    }
36
37    fn get_mut(&mut self) -> &mut V {
38        self.get_mut()
39    }
40
41    fn remove(self) {
42        self.remove_entry();
43    }
44}
45
46/// A [crate::index::Cursor] over the values associated with a translated key.
47pub type Cursor<'a, K, V, S> = CursorImpl<'a, K, V, BTreeOccupiedEntry<'a, K, V>, S>;
48
49/// A memory-efficient index that uses an ordered map internally to map translated keys to arbitrary
50/// values.
51///
52/// Each translated key maps directly to its most recently inserted value. Conflicting values (from
53/// key collisions or repeated insertions) live in a separate overflow map, keeping the common
54/// (collision-free) case compact.
55pub struct Index<T: Translator, V: Send + Sync> {
56    translator: T,
57    map: BTreeMap<T::Key, V>,
58    overflow: Overflow<T::Key, V, T>,
59
60    keys: Gauge,
61    items: Gauge,
62    pruned: Counter,
63}
64
65impl<T: Translator, V: Send + Sync> Index<T, V> {
66    /// Create a new entry in the index.
67    fn create(keys: &Gauge, items: &Gauge, vacant: BTreeVacantEntry<'_, T::Key, V>, v: V) {
68        keys.inc();
69        items.inc();
70        vacant.insert(v);
71    }
72
73    /// Create a new [Index] with the given translator and metrics registry.
74    pub fn new(ctx: impl Metrics, translator: T) -> Self {
75        Self {
76            overflow: HashMap::with_hasher(translator.clone()),
77            translator,
78            map: BTreeMap::new(),
79            keys: ctx.gauge("keys", "Number of translated keys in the index"),
80            items: ctx.gauge("items", "Number of items in the index"),
81            pruned: ctx.counter("pruned", "Number of items pruned"),
82        }
83    }
84
85    /// Returns an iterator over the values associated with the translated key `k`, given that
86    /// key's inline (head) value.
87    fn values<'a>(&'a self, k: &T::Key, head: &'a V) -> Values<'a, T::Key, V, T> {
88        Values::new(Some(head), &self.overflow, *k)
89    }
90
91    /// Returns an iterator over all values associated with an already-translated key.
92    pub(super) fn get_translated(&self, key: T::Key) -> Values<'_, T::Key, V, T> {
93        Values::new(self.map.get(&key), &self.overflow, key)
94    }
95
96    /// Returns an iterator over the values of the translated key that lexicographically follows
97    /// `key`, or None if no such key exists (no cycling).
98    pub(super) fn next_translated_values_no_cycle(
99        &self,
100        key: &[u8],
101    ) -> Option<Values<'_, T::Key, V, T>> {
102        let k = self.translator.transform(key);
103        self.map
104            .range((Excluded(k), Unbounded))
105            .next()
106            .map(|(k, head)| self.values(k, head))
107    }
108
109    /// Returns an iterator over the values of the translated key that lexicographically precedes
110    /// `key`, or None if no such key exists (no cycling).
111    pub(super) fn prev_translated_values_no_cycle(
112        &self,
113        key: &[u8],
114    ) -> Option<Values<'_, T::Key, V, T>> {
115        let k = self.translator.transform(key);
116        self.map
117            .range(..k)
118            .next_back()
119            .map(|(k, head)| self.values(k, head))
120    }
121
122    /// Returns an iterator over the values of the lexicographically first translated key, or
123    /// None if the index is empty.
124    pub(super) fn first_translated_values(&self) -> Option<Values<'_, T::Key, V, T>> {
125        self.map
126            .first_key_value()
127            .map(|(k, head)| self.values(k, head))
128    }
129
130    /// Returns an iterator over the values of the lexicographically last translated key, or
131    /// None if the index is empty.
132    pub(super) fn last_translated_values(&self) -> Option<Values<'_, T::Key, V, T>> {
133        self.map
134            .last_key_value()
135            .map(|(k, head)| self.values(k, head))
136    }
137}
138
139impl<T: OrderedTranslator, V: Send + Sync> Ordered for Index<T, V> {
140    fn prev_translated_key<'a>(
141        &'a self,
142        key: &[u8],
143    ) -> Option<(impl Iterator<Item = &'a V> + Send + 'a, bool)> {
144        if let Some(values) = self.prev_translated_values_no_cycle(key) {
145            return Some((values, false));
146        }
147        self.last_translated_values().map(|values| (values, true))
148    }
149
150    fn next_translated_key<'a>(
151        &'a self,
152        key: &[u8],
153    ) -> Option<(impl Iterator<Item = &'a V> + Send + 'a, bool)> {
154        if let Some(values) = self.next_translated_values_no_cycle(key) {
155            return Some((values, false));
156        }
157        self.first_translated_values().map(|values| (values, true))
158    }
159
160    fn first_translated_key<'a>(&'a self) -> Option<impl Iterator<Item = &'a V> + Send + 'a> {
161        self.first_translated_values()
162    }
163
164    fn last_translated_key<'a>(&'a self) -> Option<impl Iterator<Item = &'a V> + Send + 'a> {
165        self.last_translated_values()
166    }
167}
168
169impl<T: Translator, V: Send + Sync> super::Factory for Index<T, V> {
170    type Translator = T;
171
172    fn new(ctx: impl commonware_runtime::Metrics, translator: T) -> Self {
173        Self::new(ctx, translator)
174    }
175}
176
177impl<T: Translator, V: Send + Sync> Unordered for Index<T, V> {
178    type Value = V;
179
180    // A lookup warms the tree but repeats the full descent that the mutation of its key makes.
181    // Dependent node loads overlap few misses, so the extra descent costs more than warming saves.
182    const LOOKUP_WARMS_UPDATE: bool = false;
183
184    fn get_many<'a, K: AsRef<[u8]>>(
185        &'a self,
186        keys: impl IntoIterator<Item = K>,
187        mut visit: impl FnMut(usize, &'a V),
188    ) {
189        // Probe in translated-key order: consecutive tree descents share upper node paths,
190        // which stay cache-resident across the batch.
191        let mut order: Vec<(T::Key, usize)> = keys
192            .into_iter()
193            .enumerate()
194            .map(|(key_idx, key)| (self.translator.transform(key.as_ref()), key_idx))
195            .collect();
196        order.sort_unstable();
197        for (translated, key_idx) in order {
198            for value in self.get_translated(translated) {
199                visit(key_idx, value);
200            }
201        }
202    }
203    type Cursor<'a>
204        = Cursor<'a, T::Key, V, T>
205    where
206        Self: 'a;
207
208    fn get<'a>(&'a self, key: &[u8]) -> impl Iterator<Item = &'a V> + 'a {
209        self.get_translated(self.translator.transform(key))
210    }
211
212    fn get_mut<'a>(&'a mut self, key: &[u8]) -> Option<Self::Cursor<'a>> {
213        let k = self.translator.transform(key);
214        match self.map.entry(k) {
215            BTreeEntry::Occupied(entry) => Some(Cursor::<'_, T::Key, V, T>::new(
216                entry,
217                &mut self.overflow,
218                &self.keys,
219                &self.items,
220                &self.pruned,
221            )),
222            BTreeEntry::Vacant(_) => None,
223        }
224    }
225
226    fn get_mut_or_insert<'a>(&'a mut self, key: &[u8], value: V) -> Option<Self::Cursor<'a>> {
227        let k = self.translator.transform(key);
228        match self.map.entry(k) {
229            BTreeEntry::Occupied(entry) => Some(Cursor::<'_, T::Key, V, T>::new(
230                entry,
231                &mut self.overflow,
232                &self.keys,
233                &self.items,
234                &self.pruned,
235            )),
236            BTreeEntry::Vacant(entry) => {
237                Self::create(&self.keys, &self.items, entry, value);
238                None
239            }
240        }
241    }
242
243    fn insert(&mut self, key: &[u8], value: V) {
244        let k = self.translator.transform(key);
245        match self.map.entry(k) {
246            BTreeEntry::Occupied(mut entry) => {
247                // The newest value is stored inline; the displaced value joins the end of the
248                // overflow chain.
249                let old = std::mem::replace(entry.get_mut(), value);
250                push_displaced(&mut self.overflow, k, old);
251                self.items.inc();
252            }
253            BTreeEntry::Vacant(entry) => {
254                Self::create(&self.keys, &self.items, entry, value);
255            }
256        }
257    }
258
259    fn insert_and_retain(
260        &mut self,
261        key: &[u8],
262        value: V,
263        mut should_retain: impl FnMut(&V) -> bool,
264    ) {
265        let k = self.translator.transform(key);
266        match self.map.entry(k) {
267            BTreeEntry::Occupied(mut entry) => {
268                // Optimized fast path for the common case of no overflow chain.
269                #[allow(clippy::map_entry)]
270                if !self.overflow.contains_key(&k) {
271                    match (should_retain(entry.get()), should_retain(&value)) {
272                        // Keep both, with the new value placed at the end of the overflow chain.
273                        (true, true) => {
274                            self.overflow.insert(k, vec![value]);
275                            self.items.inc();
276                        }
277                        // Drop the existing value, keep the new one: replace in place.
278                        (false, true) => {
279                            *entry.get_mut() = value;
280                            self.pruned.inc();
281                        }
282                        // Drop both: remove the key entirely.
283                        (false, false) => {
284                            entry.remove();
285                            self.keys.dec();
286                            self.items.dec();
287                            self.pruned.inc();
288                        }
289                        // Keep the existing value, drop the new one: nothing to do.
290                        (true, false) => {}
291                    }
292                    return;
293                }
294
295                // Slow path: the key has conflicting values; walk them with a cursor.
296                let mut cursor = Cursor::<'_, T::Key, V, T>::new(
297                    entry,
298                    &mut self.overflow,
299                    &self.keys,
300                    &self.items,
301                    &self.pruned,
302                );
303
304                // Drop anything that should not be retained.
305                cursor.retain(&mut should_retain);
306
307                // Add the new value only if it should be retained.
308                if should_retain(&value) {
309                    cursor.insert(value);
310                }
311            }
312            BTreeEntry::Vacant(entry) => {
313                // Create the entry only if the value should be retained.
314                if should_retain(&value) {
315                    Self::create(&self.keys, &self.items, entry, value);
316                }
317            }
318        }
319    }
320
321    fn remove(&mut self, key: &[u8]) {
322        let k = self.translator.transform(key);
323        if self.map.remove(&k).is_some() {
324            // To ensure metrics are accurate, account for all conflicting values in the chain.
325            self.keys.dec();
326            self.items.dec();
327            self.pruned.inc();
328            if !self.overflow.is_empty()
329                && let Some(chain) = self.overflow.remove(&k)
330            {
331                self.items.dec_by(chain.len() as i64);
332                self.pruned.inc_by(chain.len() as u64);
333            }
334        }
335    }
336
337    #[cfg(test)]
338    fn keys(&self) -> usize {
339        self.keys.get() as usize
340    }
341
342    #[cfg(test)]
343    fn items(&self) -> usize {
344        self.items.get() as usize
345    }
346
347    #[cfg(test)]
348    fn pruned(&self) -> usize {
349        self.pruned.get() as usize
350    }
351}
352
353#[cfg(test)]
354mod tests {
355    use super::*;
356    use crate::translator::OneCap;
357    use commonware_formatting::hex;
358    use commonware_macros::test_traced;
359    use commonware_runtime::{Runner, deterministic};
360
361    #[test_traced]
362    fn test_ordered_empty_index() {
363        let runner = deterministic::Runner::default();
364        runner.start(|context| async move {
365            let index = Index::<_, u64>::new(context, OneCap);
366
367            assert!(index.first_translated_key().is_none());
368            assert!(index.last_translated_key().is_none());
369            assert!(index.prev_translated_key(b"key").is_none());
370            assert!(index.next_translated_key(b"key").is_none());
371        });
372    }
373
374    #[test_traced]
375    fn test_ordered_index_ordering() {
376        let runner = deterministic::Runner::default();
377        runner.start(|context| async move {
378            let mut index = Index::<_, u64>::new(context, OneCap);
379            assert_eq!(index.keys(), 0);
380
381            let k1 = &hex!("0x0b02AA"); // translated key 0b
382            let k2 = &hex!("0x1c04CC"); // translated key 1c
383            let k2_collides = &hex!("0x1c0311");
384            let k3 = &hex!("0x2d06EE"); // translated key 2d
385            index.insert(k1, 1);
386            index.insert(k2, 21);
387            index.insert(k2_collides, 22);
388            index.insert(k3, 3);
389            assert_eq!(index.keys(), 3);
390
391            // First translated key is 0b.
392            let mut next = index.first_translated_key().unwrap();
393            assert_eq!(next.next().unwrap(), &1);
394            assert_eq!(next.next(), None);
395
396            // Next translated key to 0x00 is 0b.
397            let (mut next, wrapped) = index.next_translated_key(&[0x00]).unwrap();
398            assert!(!wrapped);
399            assert_eq!(next.next().unwrap(), &1);
400            assert_eq!(next.next(), None);
401
402            // Next translated key to 0x0b is 1c.
403            let (mut next, wrapped) = index.next_translated_key(&hex!("0x0b0102")).unwrap();
404            assert!(!wrapped);
405            assert_eq!(next.next().unwrap(), &22);
406            assert_eq!(next.next().unwrap(), &21);
407            assert_eq!(next.next(), None);
408
409            // Next translated key to 0x1b is 1c.
410            let (mut next, wrapped) = index.next_translated_key(&hex!("0x1b010203")).unwrap();
411            assert!(!wrapped);
412            assert_eq!(next.next().unwrap(), &22);
413            assert_eq!(next.next().unwrap(), &21);
414            assert_eq!(next.next(), None);
415
416            // Next translated key to 0x2a is 2d.
417            let (mut next, wrapped) = index.next_translated_key(&hex!("0x2a01020304")).unwrap();
418            assert!(!wrapped);
419            assert_eq!(next.next().unwrap(), &3);
420            assert_eq!(next.next(), None);
421
422            // Next translated key to 0x2d cycles around to 0x0b.
423            let (mut next, wrapped) = index.next_translated_key(k3).unwrap();
424            assert!(wrapped);
425            assert_eq!(next.next().unwrap(), &1);
426            assert_eq!(next.next(), None);
427
428            // Another cycle-around case.
429            let (mut next, wrapped) = index.next_translated_key(&hex!("0x2eFF")).unwrap();
430            assert!(wrapped);
431            assert_eq!(next.next().unwrap(), &1);
432            assert_eq!(next.next(), None);
433
434            // Previous translated key of first key is the last key.
435            let (mut prev, wrapped) = index.prev_translated_key(k1).unwrap();
436            assert!(wrapped);
437            assert_eq!(prev.next().unwrap(), &3);
438            assert_eq!(prev.next(), None);
439
440            // Previous translated key is 0b.
441            let (mut prev, wrapped) = index.prev_translated_key(&hex!("0x0c0102")).unwrap();
442            assert!(!wrapped);
443            assert_eq!(prev.next().unwrap(), &1);
444            assert_eq!(prev.next(), None);
445
446            // Previous translated key is 1c.
447            let (mut prev, wrapped) = index.prev_translated_key(&hex!("0x1d0102")).unwrap();
448            assert!(!wrapped);
449            assert_eq!(prev.next().unwrap(), &22);
450            assert_eq!(prev.next().unwrap(), &21);
451            assert_eq!(prev.next(), None);
452
453            // Previous translated key is 2d.
454            let (mut prev, wrapped) = index.prev_translated_key(&hex!("0xCC0102")).unwrap();
455            assert!(!wrapped);
456            assert_eq!(prev.next().unwrap(), &3);
457            assert_eq!(prev.next(), None);
458
459            // Last translated key is 2d.
460            let mut last = index.last_translated_key().unwrap();
461            assert_eq!(last.next().unwrap(), &3);
462            assert_eq!(last.next(), None);
463        });
464    }
465}