1use ::core::borrow::Borrow;
2use ::core::fmt::{Debug, Display, Formatter};
3use ::core::iter::FusedIterator;
4use ::core::ops::RangeBounds;
5use alloc::vec::Vec;
6
7use super::set::BTreeSet;
8use crate::core::node::NodeLike;
9use crate::{cdc::change::ChangeEvent, core::pair::Pair};
10
11#[cfg(feature = "cdc")]
17#[derive(Clone, Debug, Eq, PartialEq)]
18pub struct Topology<T> {
19 pub node_capacity: usize,
20 pub nodes: Vec<Vec<T>>,
21}
22
23#[cfg(feature = "cdc")]
26#[derive(Clone, Debug, Eq, PartialEq)]
27pub enum TopologyError {
28 ZeroNodeCapacity,
29 EmptyNode { index: usize },
30 OversizedNode { index: usize, len: usize, capacity: usize },
31 UnsortedNode { index: usize },
32 OverlappingNodes { left: usize, right: usize },
33}
34
35#[cfg(feature = "cdc")]
36impl Display for TopologyError {
37 fn fmt(&self, formatter: &mut Formatter<'_>) -> ::core::fmt::Result {
38 match self {
39 Self::ZeroNodeCapacity => formatter.write_str("topology node capacity must be non-zero"),
40 Self::EmptyNode { index } => write!(formatter, "topology node {index} is empty"),
41 Self::OversizedNode { index, len, capacity } => write!(
42 formatter,
43 "topology node {index} has {len} entries but capacity is {capacity}"
44 ),
45 Self::UnsortedNode { index } => write!(formatter, "topology node {index} is not strictly ordered"),
46 Self::OverlappingNodes { left, right } => {
47 write!(
48 formatter,
49 "topology nodes {left} and {right} overlap or are out of order"
50 )
51 }
52 }
53 }
54}
55
56#[cfg(feature = "cdc")]
57impl ::core::error::Error for TopologyError {}
58
59#[derive(Debug)]
60pub struct BTreeMap<K, V, Node = Vec<Pair<K, V>>>
61where
62 K: Send + Ord + Clone + 'static,
63 V: Send + Clone + 'static,
64 Node: NodeLike<Pair<K, V>>,
65{
66 pub(crate) set: BTreeSet<Pair<K, V>, Node>,
67}
68
69impl<K, V, Node> Default for BTreeMap<K, V, Node>
70where
71 K: Send + Ord + Clone,
72 V: Send + Clone + 'static,
73 Node: NodeLike<Pair<K, V>> + Send + 'static,
74{
75 fn default() -> Self {
76 Self {
77 set: Default::default(),
78 }
79 }
80}
81
82pub struct Iter<'a, K, V, Node>
83where
84 K: Debug + Send + Ord + Clone + 'static,
85 V: Debug + Send + Clone + 'static,
86 Node: NodeLike<Pair<K, V>> + Send + 'static,
87{
88 inner: super::set::Iter<'a, Pair<K, V>, Node>,
89}
90
91impl<'a, K, V, Node> Iterator for Iter<'a, K, V, Node>
92where
93 K: Debug + Send + Ord + Clone + 'static,
94 V: Debug + Send + Clone + 'static,
95 Node: NodeLike<Pair<K, V>> + Send + 'static,
96{
97 type Item = (K, V);
98
99 fn next(&mut self) -> Option<Self::Item> {
100 if let Some(entry) = self.inner.next() {
101 return Some((entry.key, entry.value));
102 }
103
104 None
105 }
106}
107
108impl<'a, K, V, Node> DoubleEndedIterator for Iter<'a, K, V, Node>
109where
110 K: Debug + Send + Ord + Clone + 'static,
111 V: Debug + Send + Clone + 'static,
112 Node: NodeLike<Pair<K, V>> + Send + 'static,
113{
114 fn next_back(&mut self) -> Option<Self::Item> {
115 if let Some(entry) = self.inner.next_back() {
116 return Some((entry.key, entry.value));
117 }
118
119 None
120 }
121}
122
123impl<'a, K, V, Node> FusedIterator for Iter<'a, K, V, Node>
124where
125 K: Debug + Send + Ord + Clone + 'static,
126 V: Debug + Send + Clone + 'static,
127 Node: NodeLike<Pair<K, V>> + Send + 'static,
128{
129}
130
131pub struct Range<'a, K, V, Node>
132where
133 K: Debug + Send + Ord + Clone + 'static,
134 V: Debug + Send + Clone + 'static,
135 Node: NodeLike<Pair<K, V>> + Send + 'static,
136{
137 inner: super::set::Range<'a, Pair<K, V>, Node>,
138}
139
140impl<'a, K, V, Node> Iterator for Range<'a, K, V, Node>
141where
142 K: Debug + Send + Ord + Clone + 'static,
143 V: Debug + Send + Clone + 'static,
144 Node: NodeLike<Pair<K, V>> + Send + 'static,
145{
146 type Item = (K, V);
147
148 fn next(&mut self) -> Option<Self::Item> {
149 if let Some(entry) = self.inner.next() {
150 return Some((entry.key, entry.value));
151 }
152
153 None
154 }
155}
156
157impl<'a, K, V, Node> DoubleEndedIterator for Range<'a, K, V, Node>
158where
159 K: Debug + Send + Ord + Clone + 'static,
160 V: Debug + Send + Clone + 'static,
161 Node: NodeLike<Pair<K, V>> + Send + 'static,
162{
163 fn next_back(&mut self) -> Option<Self::Item> {
164 if let Some(entry) = self.inner.next_back() {
165 return Some((entry.key, entry.value));
166 }
167
168 None
169 }
170}
171
172impl<'a, K, V, Node> FusedIterator for Range<'a, K, V, Node>
173where
174 K: Debug + Send + Ord + Clone + 'static,
175 V: Debug + Send + Clone + 'static,
176 Node: NodeLike<Pair<K, V>> + Send + 'static,
177{
178}
179
180impl<K, V, Node> BTreeMap<K, V, Node>
181where
182 K: Debug + Send + Ord + Clone + 'static,
183 V: Debug + Send + Clone + 'static,
184 Node: NodeLike<Pair<K, V>> + Send + 'static,
185{
186 pub fn new() -> Self {
201 Self {
202 set: Default::default(),
203 }
204 }
205 pub fn with_maximum_node_size(node_capacity: usize) -> Self {
215 Self {
216 set: BTreeSet::with_maximum_node_size(node_capacity),
217 }
218 }
219 #[cfg(feature = "cdc")]
222 pub fn attach_node(&self, node: Node) {
223 self.set.attach_node(node)
224 }
225 #[cfg(feature = "cdc")]
227 pub fn attach_nodes(&self, nodes: impl IntoIterator<Item = Node>) {
228 self.set.attach_nodes(nodes)
229 }
230
231 #[cfg(feature = "cdc")]
236 pub fn snapshot_nodes(&self) -> Vec<Node>
237 where
238 Node: Clone,
239 {
240 self.set
241 .index
242 .read()
243 .values()
244 .map(|node| (*node.read()).clone())
245 .collect()
246 }
247
248 #[cfg(feature = "cdc")]
254 pub fn export_topology(&self) -> Topology<Pair<K, V>> {
255 let (node_capacity, nodes) = self.set.export_topology();
256 Topology { node_capacity, nodes }
257 }
258
259 #[cfg(feature = "cdc")]
261 pub fn from_topology(topology: Topology<Pair<K, V>>) -> Result<Self, TopologyError> {
262 if topology.node_capacity == 0 {
263 return Err(TopologyError::ZeroNodeCapacity);
264 }
265
266 for (index, node) in topology.nodes.iter().enumerate() {
267 if node.is_empty() {
268 return Err(TopologyError::EmptyNode { index });
269 }
270 if node.len() > topology.node_capacity {
271 return Err(TopologyError::OversizedNode {
272 index,
273 len: node.len(),
274 capacity: topology.node_capacity,
275 });
276 }
277 if node.windows(2).any(|pair| pair[0] >= pair[1]) {
278 return Err(TopologyError::UnsortedNode { index });
279 }
280 if index > 0
281 && topology.nodes[index - 1]
282 .last()
283 .expect("non-empty node validated above")
284 >= node.first().expect("non-empty node validated above")
285 {
286 return Err(TopologyError::OverlappingNodes {
287 left: index - 1,
288 right: index,
289 });
290 }
291 }
292
293 let map = Self::with_maximum_node_size(topology.node_capacity);
294 map.attach_nodes(topology.nodes.into_iter().map(|values| {
295 let mut node = Node::with_capacity(topology.node_capacity);
296 for value in values {
297 let (inserted, _) = NodeLike::insert(&mut node, value);
298 debug_assert!(inserted, "validated topology contains unique values");
299 }
300 node
301 }));
302 Ok(map)
303 }
304 pub fn contains_key<Q>(&self, key: &Q) -> bool
322 where
323 Pair<K, V>: Borrow<Q> + Ord,
324 Q: Ord + ?Sized,
325 {
326 self.set.contains(key)
327 }
328 pub fn get<Q>(&self, key: &Q) -> Option<super::r#ref::Ref<Pair<K, V>, Node>>
346 where
347 Pair<K, V>: Borrow<Q> + Ord,
348 Q: Ord + ?Sized,
349 {
350 self.set.get(key)
351 }
352
353 #[inline(always)]
359 pub fn lookup_for_select<Q>(&self, key: &Q) -> Option<V>
360 where
361 Pair<K, V>: Borrow<Q> + Ord,
362 Q: Ord + ?Sized,
363 V: Clone,
364 {
365 self.set.get_with(key, |pair| pair.value.clone())
366 }
367
368 #[inline(always)]
374 pub fn lookup_for_select_optimistic<Q>(&self, key: &Q) -> Option<V>
375 where
376 Pair<K, V>: Borrow<Q> + Ord,
377 Q: Ord + ?Sized,
378 V: Clone,
379 {
380 self.set.get_with_optimistic(key, |pair| pair.value.clone())
381 }
382
383 pub fn insert(&self, key: K, value: V) -> Option<V> {
407 let new_entry = Pair { key, value };
408
409 self.set.put(new_entry).map(|pair| pair.value)
410 }
411 pub fn checked_insert(&self, key: K, value: V) -> Option<()> {
412 let new_entry = Pair { key, value };
413 self.set.put_checked(new_entry).ok().map(|_| ())
414 }
415 pub fn insert_cdc(&self, key: K, value: V) -> (Option<V>, Vec<ChangeEvent<Pair<K, V>>>) {
419 let new_entry = Pair { key, value };
420
421 let (old_value, cdc) = self.set.put_cdc(new_entry);
422
423 (old_value.map(|pair| pair.value), cdc)
424 }
425 pub fn checked_insert_cdc(&self, key: K, value: V) -> Option<Vec<ChangeEvent<Pair<K, V>>>> {
426 let new_entry = Pair { key, value };
427 self.set.put_cdc_checked(new_entry).ok().map(|(_, evs)| evs)
428 }
429 pub fn remove<Q>(&self, key: &Q) -> Option<(K, V)>
448 where
449 Pair<K, V>: Borrow<Q> + Ord,
450 Q: Ord + ?Sized,
451 {
452 self.set.remove(key).map(|pair| (pair.key, pair.value))
453 }
454 #[allow(clippy::type_complexity)]
458 pub fn remove_cdc<Q>(&self, key: &Q) -> (Option<(K, V)>, Vec<ChangeEvent<Pair<K, V>>>)
459 where
460 Pair<K, V>: Borrow<Q> + Ord,
461 Q: Ord + ?Sized,
462 {
463 let (old_value, cdc) = self.set.remove_cdc(key);
464
465 (old_value.map(|pair| (pair.key, pair.value)), cdc)
466 }
467 pub fn len(&self) -> usize {
482 self.set.len()
483 }
484 pub fn is_empty(&self) -> bool {
499 self.set.is_empty()
500 }
501 pub fn capacity(&self) -> usize {
522 self.set.capacity()
523 }
524 pub fn node_count(&self) -> usize {
542 self.set.node_count()
543 }
544 pub fn iter(&self) -> Iter<'_, K, V, Node> {
566 Iter { inner: self.set.iter() }
567 }
568 pub fn range<Q, R>(&self, range: R) -> Range<'_, K, V, Node>
598 where
599 Pair<K, V>: Borrow<Q>,
600 Q: Ord + ?Sized,
601 R: RangeBounds<Q>,
602 {
603 Range {
604 inner: BTreeSet::range(&self.set, range),
605 }
606 }
607}
608
609#[cfg(test)]
610mod tests {
611 use super::BTreeMap;
612 use super::ChangeEvent;
613 use super::Pair;
614 #[cfg(feature = "cdc")]
615 use super::{Topology, TopologyError};
616 use crate::core::constants::DEFAULT_INNER_SIZE;
617 use crate::BTreeSet;
618 use rand::Rng;
619 use scc::HashMap;
620 use std::fmt::Debug;
621 use std::sync::{Arc, Mutex};
622 use std::thread;
623
624 #[cfg(feature = "cdc")]
625 #[test]
626 fn pointer_free_topology_round_trip_preserves_nodes() {
627 let map = BTreeMap::<u64, u64>::with_maximum_node_size(4);
628 for key in 0..37 {
629 map.insert(key, key * 10);
630 }
631
632 let topology = map.export_topology();
633 assert!(topology.nodes.len() > 1);
634 let restored = BTreeMap::<u64, u64>::from_topology(topology.clone()).unwrap();
635
636 assert_eq!(restored.export_topology(), topology);
637 assert_eq!(
638 restored.iter().collect::<Vec<_>>(),
639 (0..37).map(|k| (k, k * 10)).collect::<Vec<_>>()
640 );
641 }
642
643 #[cfg(feature = "cdc")]
644 #[test]
645 fn pointer_free_topology_rejects_invalid_boundaries() {
646 let error = BTreeMap::<u64, u64>::from_topology(Topology {
647 node_capacity: 4,
648 nodes: vec![
649 vec![Pair { key: 1, value: 10 }, Pair { key: 3, value: 30 }],
650 vec![Pair { key: 2, value: 20 }],
651 ],
652 })
653 .unwrap_err();
654 assert_eq!(error, TopologyError::OverlappingNodes { left: 0, right: 1 });
655 }
656
657 #[test]
658 fn test_range_edge_cast() {
659 let maximum_node_size = 3;
660 let map = BTreeMap::<usize, &str>::with_maximum_node_size(maximum_node_size);
661
662 map.insert(1usize, "a");
663
664 map.insert(2usize, "b");
665 map.insert(3usize, "c");
666
667 map.insert(4usize, "d");
668 map.insert(5usize, "e");
669
670 map.insert(6usize, "f");
671 map.insert(7usize, "g");
672
673 let mid_range = map.range::<usize, _>(3..5).collect::<BTreeSet<_>>();
674 assert_eq!(
675 mid_range,
676 vec![(3usize, "c"), (4usize, "d"),].into_iter().collect::<BTreeSet<_>>()
677 );
678 }
679
680 #[test]
681 fn test_split_insert_replaces_left_split_max() {
682 let maximum_node_size = 4;
683 let map = BTreeMap::<usize, &str>::with_maximum_node_size(maximum_node_size);
684
685 for key in 0..maximum_node_size {
686 assert_eq!(map.insert(key, "old"), None);
687 }
688
689 let split_left_max = maximum_node_size / 2 - 1;
690
691 assert_eq!(map.insert(split_left_max, "new"), Some("old"));
692 assert_eq!(map.len(), maximum_node_size);
693 assert_eq!(map.get(&split_left_max).map(|entry| entry.get().value), Some("new"));
694 assert_eq!(map.iter().filter(|(key, _)| *key == split_left_max).count(), 1);
695 }
696
697 #[derive(Debug, Default)]
698 struct PersistedBTreeMap<K, V>
699 where
700 K: Debug + Ord + Clone,
701 V: Debug + Clone + PartialEq,
702 {
703 nodes: std::collections::BTreeMap<K, Vec<Pair<K, V>>>,
704 }
705
706 impl<K: Debug + Ord + Clone, V: Debug + Clone + PartialEq> PersistedBTreeMap<K, V> {
707 fn persist(&mut self, event: &ChangeEvent<Pair<K, V>>) {
708 match event {
709 ChangeEvent::CreateNode { max_value, event_id: _ } => {
710 let node = vec![max_value.clone()];
711 self.nodes.insert(max_value.key.clone(), node);
712 }
713 ChangeEvent::RemoveNode { max_value, event_id: _ } => {
714 self.nodes.remove(&max_value.key);
715 }
716 ChangeEvent::InsertAt {
717 max_value,
718 index,
719 value,
720 event_id: _,
721 } => {
722 if let Some(node) = self.nodes.get_mut(&max_value.key) {
723 node.insert(*index, value.clone());
724 }
725 if max_value.key < value.key {
726 let node = self.nodes.remove(&max_value.key).unwrap();
727 self.nodes.insert(value.key.clone(), node);
728 }
729 }
730 ChangeEvent::RemoveAt {
731 max_value,
732 index,
733 value,
734 event_id: _,
735 } => {
736 let mut max_removed = false;
737 if let Some(node) = self.nodes.get_mut(&max_value.key) {
738 node.remove(*index);
739 max_removed = max_value.key == value.key;
740 }
741 if max_removed {
742 let node = self.nodes.remove(&max_value.key).unwrap();
743 if let Some(new_max) = node.last() {
744 self.nodes.insert(new_max.key.clone(), node);
745 }
746 }
747 }
748 ChangeEvent::SplitNode {
749 max_value,
750 split_index,
751 event_id: _,
752 } => {
753 if let Some(mut old_node) = self.nodes.remove(&max_value.key) {
754 let new_node = old_node.split_off(*split_index);
755 let new_max_value = new_node.last().unwrap();
756 self.nodes.insert(new_max_value.key.clone(), new_node);
757 let old_max_value = old_node.last().unwrap();
758 self.nodes.insert(old_max_value.key.clone(), old_node);
759 }
760 }
761 }
762 }
763
764 fn contains_pair(&self, key: &K, value: &V) -> bool {
765 for node in self.nodes.values() {
766 if let Ok(pos) = node.binary_search(&Pair {
767 key: key.clone(),
768 value: value.clone(),
769 }) {
770 if node[pos].value == *value {
771 return true;
772 }
773 }
774 }
775 false
776 }
777 }
778
779 #[cfg(feature = "cdc")]
780 #[test]
781 fn test_cdc_single_insert() {
782 let map = BTreeMap::<usize, &str>::new();
783 let mut mock_state = PersistedBTreeMap::default();
784
785 let (_, events) = map.insert_cdc(1, "a");
786
787 for event in events {
788 mock_state.persist(&event);
789 }
790
791 assert!(mock_state.contains_pair(&1, &"a"));
792 assert!(map.contains_key(&1));
793 assert_eq!(map.get(&1).unwrap().get().value, "a");
794
795 let expected_state = map
796 .set
797 .index
798 .read()
799 .iter()
800 .map(|(key, node)| (key.clone().key, node.read_arc().clone()))
801 .collect::<_>();
802 assert_eq!(mock_state.nodes, expected_state);
803 }
804
805 #[cfg(feature = "cdc")]
806 #[test]
807 fn test_cdc_multiple_inserts() {
808 let map = BTreeMap::<usize, String>::new();
809 let mut mock_state = PersistedBTreeMap::default();
810
811 for i in 0..1024 {
812 let (_, events) = map.insert_cdc(i, format!("val{}", i));
813
814 for event in events {
815 mock_state.persist(&event);
816 }
817 }
818
819 for i in 0..1024 {
820 assert!(mock_state.contains_pair(&i, &format!("val{}", i)));
821 assert!(map.contains_key(&i));
822 assert_eq!(map.get(&i).unwrap().get().value, format!("val{}", i));
823 }
824
825 let expected_state = map
826 .set
827 .index
828 .read()
829 .iter()
830 .map(|(key, node)| (key.clone().key, node.read_arc().clone()))
831 .collect::<_>();
832 assert_eq!(mock_state.nodes, expected_state);
833 }
834
835 #[cfg(feature = "cdc")]
836 #[test]
837 fn test_cdc_updates() {
838 let map = BTreeMap::<usize, &str>::new();
839 let mut mock_state = PersistedBTreeMap::default();
840
841 let (_, events) = map.insert_cdc(1, "a");
842 for event in events {
843 mock_state.persist(&event);
844 }
845
846 let (_, events) = map.insert_cdc(1, "b");
847 for event in events {
848 mock_state.persist(&event);
849 }
850
851 assert!(mock_state.contains_pair(&1, &"b"));
852 assert!(!mock_state.contains_pair(&1, &"a"));
853 assert!(map.contains_key(&1));
854 assert_eq!(map.get(&1).unwrap().get().value, "b");
855
856 let expected_state = map
857 .set
858 .index
859 .read()
860 .iter()
861 .map(|(key, node)| (key.clone().key, node.read_arc().clone()))
862 .collect::<_>();
863 assert_eq!(mock_state.nodes, expected_state);
864 }
865
866 #[cfg(feature = "cdc")]
867 #[test]
868 fn test_cdc_node_splits() {
869 let map = BTreeMap::<usize, String>::new();
870 let mut mock_state = PersistedBTreeMap::default();
871
872 let n = crate::core::constants::DEFAULT_INNER_SIZE + 10;
873
874 for i in 0..n {
875 let (_, events) = map.insert_cdc(i, format!("val{}", i));
876 for event in events {
877 mock_state.persist(&event);
878 }
879 }
880
881 for i in 0..n {
882 assert!(mock_state.contains_pair(&i, &format!("val{}", i)));
883 assert!(map.contains_key(&i));
884 assert_eq!(map.get(&i).unwrap().get().value, format!("val{}", i));
885 }
886
887 assert!(mock_state.nodes.len() > 1);
888
889 let expected_state = map
890 .set
891 .index
892 .read()
893 .iter()
894 .map(|(key, node)| (key.clone().key, node.read_arc().clone()))
895 .collect::<_>();
896 assert_eq!(mock_state.nodes, expected_state);
897 }
898
899 #[cfg(feature = "cdc")]
900 #[test]
901 fn test_concurrent_insert_cdc() {
902 let map = Arc::new(BTreeMap::<usize, String>::new());
903 let num_threads = 8;
904 let operations_per_thread = 1000;
905 let mut handles = vec![];
906
907 let test_data: Vec<Vec<(i32, (usize, String))>> = (0..num_threads)
908 .map(|_| {
909 let mut rng = rand::rng();
910 (0..operations_per_thread)
911 .map(|_| {
912 let value = rng.random_range(0..100000);
913 let operation = rng.random_range(0..2);
914 (operation, (value, format!("val{value}")))
915 })
916 .collect()
917 })
918 .collect();
919
920 let expected_values = Arc::new(Mutex::new(HashMap::new()));
921
922 for thread_idx in 0..num_threads {
923 let map_clone = Arc::clone(&map);
924 let expected_values = Arc::clone(&expected_values);
925 let thread_data = test_data[thread_idx].clone();
926
927 let handle = thread::spawn(move || {
928 let mut events = Vec::new();
929 for (operation, (k, v)) in thread_data {
930 if operation == 0 {
931 let (_, evs) = map_clone.insert_cdc(k, v.clone());
932 events.extend(evs);
933 let _ = expected_values.lock().unwrap().insert(k, v);
934 }
935 }
936 events
937 });
938 handles.push(handle);
939 }
940
941 let mut final_events = Vec::new();
942 for handle in handles {
943 let thread_events = handle.join().unwrap();
944 final_events.extend(thread_events)
945 }
946 final_events.sort_by(|ev1, ev2| ev1.id().cmp(&ev2.id()));
947
948 let mut mock_state = PersistedBTreeMap::default();
949 for ev in final_events {
950 mock_state.persist(&ev);
951 }
952
953 let expected_state = map
957 .set
958 .index
959 .read()
960 .iter()
961 .map(|(key, node)| (key.clone().key, node.read_arc().clone()))
962 .collect::<_>();
963 assert_eq!(mock_state.nodes, expected_state);
964 }
965
966 #[cfg(feature = "cdc")]
967 #[test]
968 fn test_cdc_event_ids_sequential_no_gaps() {
969 let map = BTreeMap::<usize, String>::new();
970 let mut all_events = Vec::new();
971
972 for i in 0..100 {
973 let (_, events) = map.insert_cdc(i, format!("val{}", i));
974 all_events.extend(events);
975 }
976 all_events.sort_by_key(|e| e.id());
977
978 assert!(!all_events.is_empty(), "Should have at least one event");
980 for i in 1..all_events.len() {
981 let prev_id = all_events[i - 1].id().inner();
982 let curr_id = all_events[i].id().inner();
983 assert_eq!(
984 curr_id,
985 prev_id + 1,
986 "Event IDs should be consecutive: {} followed by {}, but got gap",
987 prev_id,
988 curr_id
989 );
990 }
991 }
992
993 #[cfg(feature = "cdc")]
994 #[test]
995 fn normal_writes_do_not_consume_cdc_event_ids() {
996 let map = BTreeMap::<usize, String>::new();
997
998 map.insert(1, "first".to_owned());
999 map.insert(1, "second".to_owned());
1000 map.remove(&1);
1001
1002 let (_, events) = map.insert_cdc(2, "recorded".to_owned());
1003 assert!(!events.is_empty());
1004 assert_eq!(events[0].id().inner(), 0);
1005 }
1006
1007 #[cfg(feature = "cdc")]
1008 #[test]
1009 fn test_cdc_remove_monotonicity() {
1010 let map = BTreeMap::<usize, String>::new();
1011 let mut all_events = Vec::new();
1012
1013 for i in 0..50 {
1014 let (_, events) = map.insert_cdc(i, format!("val{}", i));
1015 all_events.extend(events);
1016 }
1017
1018 for i in 0..25 {
1019 let (_, events) = map.remove_cdc(&i);
1020 all_events.extend(events);
1021 }
1022
1023 all_events.sort_by_key(|e| e.id());
1024
1025 assert!(!all_events.is_empty(), "Should have at least one event");
1027 for i in 1..all_events.len() {
1028 let prev_id = all_events[i - 1].id().inner();
1029 let curr_id = all_events[i].id().inner();
1030 assert_eq!(
1031 curr_id,
1032 prev_id + 1,
1033 "Event IDs should be consecutive across inserts and removes"
1034 );
1035 }
1036 }
1037
1038 #[cfg(feature = "cdc")]
1039 #[test]
1040 fn test_cdc_split_no_gaps() {
1041 let map = BTreeMap::<usize, String>::new();
1042 let mut all_events = Vec::new();
1043
1044 let n = DEFAULT_INNER_SIZE + 200;
1045 for i in 0..n {
1046 let (_, events) = map.insert_cdc(i, format!("val{}", i));
1047 all_events.extend(events);
1048 }
1049
1050 all_events.sort_by_key(|e| e.id());
1051
1052 assert!(!all_events.is_empty(), "Should have at least one event");
1053 for i in 1..all_events.len() {
1054 let prev_id = all_events[i - 1].id().inner();
1055 let curr_id = all_events[i].id().inner();
1056 assert_eq!(
1057 curr_id,
1058 prev_id + 1,
1059 "Event IDs should be consecutive even during splits"
1060 );
1061 }
1062
1063 let split_events: Vec<_> = all_events
1065 .iter()
1066 .filter(|e| matches!(e, ChangeEvent::SplitNode { .. }))
1067 .collect();
1068 assert!(!split_events.is_empty(), "Should have at least one split event");
1069 }
1070
1071 #[cfg(feature = "cdc")]
1072 #[test]
1073 fn test_concurrent_cdc_no_gaps() {
1074 let map = Arc::new(BTreeMap::<usize, String>::new());
1075 let num_threads = 16;
1076 let operations_per_thread = 500;
1077 let mut handles = vec![];
1078
1079 for thread_idx in 0..num_threads {
1080 let map_clone = Arc::clone(&map);
1081
1082 let handle = thread::spawn(move || {
1083 let mut events = Vec::new();
1084 let base = thread_idx * 10000;
1085 for i in 0..operations_per_thread {
1086 let value = base + i;
1087 let (_, evs) = map_clone.insert_cdc(value, format!("val{}", value));
1088 events.extend(evs);
1089 }
1090 events
1091 });
1092 handles.push(handle);
1093 }
1094
1095 let mut final_events = Vec::new();
1096 for handle in handles {
1097 let thread_events = handle.join().unwrap();
1098 final_events.extend(thread_events);
1099 }
1100
1101 final_events.sort_by_key(|e| e.id());
1102
1103 assert!(!final_events.is_empty(), "Should have at least one event");
1105 for i in 1..final_events.len() {
1106 let prev_id = final_events[i - 1].id().inner();
1107 let curr_id = final_events[i].id().inner();
1108 assert_eq!(
1109 curr_id,
1110 prev_id + 1,
1111 "Concurrent event IDs should be consecutive with no gaps: {} -> {}",
1112 prev_id,
1113 curr_id
1114 );
1115 }
1116 }
1117
1118 #[cfg(feature = "cdc")]
1119 #[test]
1120 fn test_cdc_mixed_operations() {
1121 let map = BTreeMap::<usize, String>::new();
1122 let mut all_events = Vec::new();
1123
1124 for i in 0..100 {
1125 let (_, events) = map.insert_cdc(i, format!("val{}", i));
1126 all_events.extend(events);
1127 }
1128
1129 for i in 0..50 {
1130 let (_, events) = map.remove_cdc(&i);
1131 all_events.extend(events);
1132 }
1133
1134 for i in 100..125 {
1135 let (_, events) = map.insert_cdc(i, format!("val{}", i));
1136 all_events.extend(events);
1137 }
1138
1139 all_events.sort_by_key(|e| e.id());
1140
1141 assert!(!all_events.is_empty(), "Should have at least one event");
1143 for i in 1..all_events.len() {
1144 let prev_id = all_events[i - 1].id().inner();
1145 let curr_id = all_events[i].id().inner();
1146 assert_eq!(curr_id, prev_id + 1, "Mixed operation event IDs should be consecutive");
1147 }
1148 }
1149}