1use crate::error::Result;
10use crate::storage::write_engine::mutation::{DecoratedKey, Mutation};
11use std::collections::BTreeMap;
12
13#[derive(Debug)]
18pub struct Memtable {
19 data: BTreeMap<DecoratedKey, Vec<Mutation>>,
21 size_bytes: usize,
23 row_count: usize,
25 created_at: i64,
27}
28
29impl Memtable {
30 pub fn new() -> Self {
32 Self {
33 data: BTreeMap::new(),
34 size_bytes: 0,
35 row_count: 0,
36 created_at: Self::current_timestamp_micros(),
37 }
38 }
39
40 pub fn insert(&mut self, _mutation: Mutation) -> Result<()> {
45 Err(crate::error::Error::InvalidInput(
61 "Use insert_with_key() - decorated key must be provided with mutation".to_string(),
62 ))
63 }
64
65 #[tracing::instrument(name = "memtable.insert", skip(self, key, mutation))]
70 pub fn insert_with_key(&mut self, key: DecoratedKey, mutation: Mutation) -> Result<()> {
71 let mutation_size = Self::mutation_size(&mutation);
75
76 let mutations = self.data.entry(key).or_default();
78
79 mutations.push(mutation);
81 self.row_count = self.row_count.saturating_add(1);
82 self.size_bytes = self.size_bytes.saturating_add(mutation_size);
89
90 Ok(())
91 }
92
93 pub fn get(&self, key: &DecoratedKey) -> Option<&[Mutation]> {
95 self.data.get(key).map(|v| v.as_slice())
96 }
97
98 pub fn is_empty(&self) -> bool {
100 self.data.is_empty()
101 }
102
103 pub fn size_bytes(&self) -> usize {
105 self.size_bytes
106 }
107
108 pub fn row_count(&self) -> usize {
110 self.row_count
111 }
112
113 pub fn should_flush(&self, threshold_bytes: usize) -> bool {
115 self.size_bytes >= threshold_bytes
116 }
117
118 pub fn created_at(&self) -> i64 {
120 self.created_at
121 }
122
123 pub fn iter(&self) -> impl Iterator<Item = (&DecoratedKey, &[Mutation])> {
127 self.data.iter().map(|(k, v)| (k, v.as_slice()))
128 }
129
130 pub fn clear(&mut self) {
134 self.data.clear();
135 self.size_bytes = 0;
136 self.row_count = 0;
137 }
139
140 const MAX_ESTIMATE_NODES: usize = 1_000_000;
153
154 pub(crate) fn estimate_mutation_size(&self, m: &Mutation) -> usize {
161 Self::mutation_size(m)
162 }
163
164 fn mutation_size(mutation: &Mutation) -> usize {
172 let mut size: usize = 48;
177
178 for (col_name, value) in &mutation.partition_key.columns {
180 size = size.saturating_add(col_name.len());
181 size = size.saturating_add(Self::estimate_value_size(value));
182 }
183
184 if let Some(ref clustering_key) = mutation.clustering_key {
186 for (col_name, value) in &clustering_key.columns {
187 size = size.saturating_add(col_name.len());
188 size = size.saturating_add(Self::estimate_value_size(value));
189 }
190 }
191
192 for op in &mutation.operations {
194 size = size.saturating_add(Self::estimate_operation_size(op));
195 }
196
197 size
198 }
199
200 fn estimate_value_size(value: &crate::types::Value) -> usize {
228 use crate::types::Value;
229 use smallvec::SmallVec;
230
231 let mut total: usize = 0;
232 let mut visited: usize = 0;
233 let mut worklist: SmallVec<[&Value; 32]> = SmallVec::new();
238 worklist.push(value);
239
240 let would_exceed_cap = |visited: usize, pending: usize, incoming: usize| -> bool {
248 visited.saturating_add(pending).saturating_add(incoming) > Self::MAX_ESTIMATE_NODES
249 };
250
251 while let Some(v) = worklist.pop() {
252 visited += 1;
253 if visited > Self::MAX_ESTIMATE_NODES {
254 return usize::MAX;
256 }
257
258 match v {
259 Value::Null => {}
260 Value::Boolean(_) | Value::TinyInt(_) => total = total.saturating_add(1),
261 Value::SmallInt(_) => total = total.saturating_add(2),
262 Value::Integer(_) | Value::Float32(_) | Value::Date(_) => {
263 total = total.saturating_add(4)
264 }
265 Value::BigInt(_)
266 | Value::Counter(_)
267 | Value::Timestamp(_)
268 | Value::Time(_)
269 | Value::Float(_) => total = total.saturating_add(8),
270 Value::Uuid(_) | Value::Duration { .. } => total = total.saturating_add(16),
271 Value::Text(s) => total = total.saturating_add(s.len()),
272 Value::Blob(bytes) | Value::Varint(bytes) | Value::Inet(bytes) => {
273 total = total.saturating_add(bytes.len())
274 }
275 Value::Decimal { scale: _, unscaled } => {
276 total = total.saturating_add(4).saturating_add(unscaled.len())
277 }
278 Value::Json(json) => total = total.saturating_add(json.to_string().len()),
279 Value::Tombstone(_) => total = total.saturating_add(24),
280 Value::List(items) | Value::Set(items) | Value::Tuple(items) => {
281 total = total.saturating_add(16);
282 if would_exceed_cap(visited, worklist.len(), items.len()) {
283 return usize::MAX;
284 }
285 worklist.extend(items.iter());
286 }
287 Value::Map(entries) => {
288 total = total.saturating_add(16);
289 let incoming = entries.len().saturating_mul(2);
291 if would_exceed_cap(visited, worklist.len(), incoming) {
292 return usize::MAX;
293 }
294 for (k, val) in entries {
295 worklist.push(k);
296 worklist.push(val);
297 }
298 }
299 Value::Udt(udt) => {
300 total = total.saturating_add(16);
301 if would_exceed_cap(visited, worklist.len(), udt.fields.len()) {
305 return usize::MAX;
306 }
307 for field in &udt.fields {
308 total = total.saturating_add(field.name.len());
309 if let Some(fv) = field.value.as_ref() {
310 worklist.push(fv);
311 }
312 }
313 }
314 Value::Frozen(inner) => {
315 total = total.saturating_add(8);
316 if would_exceed_cap(visited, worklist.len(), 1) {
317 return usize::MAX;
318 }
319 worklist.push(inner);
320 }
321 }
322 }
323
324 total
325 }
326
327 fn estimate_operation_size(
329 op: &crate::storage::write_engine::mutation::CellOperation,
330 ) -> usize {
331 use crate::storage::write_engine::mutation::CellOperation;
332
333 match op {
336 CellOperation::Write { column, value } => column
337 .len()
338 .saturating_add(Self::estimate_value_size(value))
339 .saturating_add(8), CellOperation::WriteWithTtl {
341 column,
342 value,
343 ttl_seconds: _,
344 } => {
345 column
347 .len()
348 .saturating_add(Self::estimate_value_size(value))
349 .saturating_add(16)
350 }
351 CellOperation::Delete { column, .. } => column.len().saturating_add(8),
352 CellOperation::DeleteRow => 8,
353 CellOperation::WriteComplexElement {
356 column,
357 cell_path,
358 value,
359 ..
360 } => column
361 .len()
362 .saturating_add(cell_path.len())
363 .saturating_add(value.as_ref().map(Self::estimate_value_size).unwrap_or(0))
364 .saturating_add(16), CellOperation::ComplexDeletion { column, .. } => column.len().saturating_add(16),
366 }
367 }
368
369 #[cfg(test)]
373 pub(crate) fn set_size_bytes_for_test(&mut self, size: usize) {
374 self.size_bytes = size;
375 }
376
377 fn current_timestamp_micros() -> i64 {
379 std::time::SystemTime::now()
380 .duration_since(std::time::UNIX_EPOCH)
381 .unwrap_or_default()
382 .as_micros() as i64
383 }
384}
385
386impl Default for Memtable {
387 fn default() -> Self {
388 Self::new()
389 }
390}
391
392#[cfg(test)]
393mod tests {
394 use super::*;
395 use crate::storage::write_engine::mutation::{
396 CellOperation, ClusteringKey, PartitionKey, TableId,
397 };
398 use crate::types::Value;
399
400 fn create_test_mutation(
401 id: i32,
402 name: &str,
403 clustering_val: Option<i64>,
404 ) -> (DecoratedKey, Mutation) {
405 let table_id = TableId::new("test_ks", "test_table");
406 let partition_key = PartitionKey::single("id", Value::Integer(id));
407
408 let key_bytes = id.to_be_bytes().to_vec();
410 let decorated_key = DecoratedKey::from_key_bytes(key_bytes).unwrap();
411
412 let clustering_key =
413 clustering_val.map(|val| ClusteringKey::single("ts", Value::BigInt(val)));
414
415 let operations = vec![CellOperation::Write {
416 column: "name".to_string(),
417 value: Value::Text(name.to_string()),
418 }];
419
420 let mutation = Mutation::new(
421 table_id,
422 partition_key,
423 clustering_key,
424 operations,
425 1234567890,
426 None,
427 );
428
429 (decorated_key, mutation)
430 }
431
432 #[test]
433 fn test_memtable_new() {
434 let memtable = Memtable::new();
435 assert!(memtable.is_empty());
436 assert_eq!(memtable.size_bytes(), 0);
437 assert_eq!(memtable.row_count(), 0);
438 assert!(memtable.created_at() > 0);
439 }
440
441 #[test]
442 fn test_memtable_insert_and_get() {
443 let mut memtable = Memtable::new();
444
445 let (key, mutation) = create_test_mutation(1, "Alice", None);
446 memtable.insert_with_key(key.clone(), mutation).unwrap();
447
448 assert!(!memtable.is_empty());
449 assert_eq!(memtable.row_count(), 1);
450 assert!(memtable.size_bytes() > 0);
451
452 let mutations = memtable.get(&key).unwrap();
454 assert_eq!(mutations.len(), 1);
455 assert_eq!(mutations[0].table.table, "test_table");
456 }
457
458 #[test]
459 fn test_memtable_multiple_mutations_same_partition() {
460 let mut memtable = Memtable::new();
461
462 let (key, mutation1) = create_test_mutation(1, "Alice", Some(1000));
464 let (_, mutation2) = create_test_mutation(1, "Alice Updated", Some(2000));
465
466 memtable.insert_with_key(key.clone(), mutation1).unwrap();
467 memtable.insert_with_key(key.clone(), mutation2).unwrap();
468
469 assert_eq!(memtable.row_count(), 2);
470
471 let mutations = memtable.get(&key).unwrap();
473 assert_eq!(mutations.len(), 2);
474 }
475
476 #[test]
477 fn test_memtable_multiple_partitions() {
478 let mut memtable = Memtable::new();
479
480 let (key1, mutation1) = create_test_mutation(1, "Alice", None);
481 let (key2, mutation2) = create_test_mutation(2, "Bob", None);
482 let (key3, mutation3) = create_test_mutation(3, "Charlie", None);
483
484 memtable.insert_with_key(key1, mutation1).unwrap();
485 memtable.insert_with_key(key2, mutation2).unwrap();
486 memtable.insert_with_key(key3, mutation3).unwrap();
487
488 assert_eq!(memtable.row_count(), 3);
489 assert!(!memtable.is_empty());
490 }
491
492 #[test]
493 fn test_memtable_token_ordering() {
494 let mut memtable = Memtable::new();
495
496 let (key3, mutation3) = create_test_mutation(300, "Charlie", None);
498 let (key1, mutation1) = create_test_mutation(100, "Alice", None);
499 let (key2, mutation2) = create_test_mutation(200, "Bob", None);
500
501 memtable.insert_with_key(key3.clone(), mutation3).unwrap();
502 memtable.insert_with_key(key1.clone(), mutation1).unwrap();
503 memtable.insert_with_key(key2.clone(), mutation2).unwrap();
504
505 let keys: Vec<_> = memtable.iter().map(|(k, _)| k.token).collect();
507 assert_eq!(keys.len(), 3);
508
509 assert!(keys.windows(2).all(|w| w[0] <= w[1]));
511 }
512
513 #[test]
514 fn test_memtable_size_tracking() {
515 let mut memtable = Memtable::new();
516
517 let initial_size = memtable.size_bytes();
518 assert_eq!(initial_size, 0);
519
520 let (key, mutation) = create_test_mutation(1, "Alice", None);
522 memtable.insert_with_key(key, mutation).unwrap();
523
524 assert!(memtable.size_bytes() > initial_size);
526 let size_after_insert = memtable.size_bytes();
527
528 let (key2, mutation2) = create_test_mutation(2, "Bob with a longer name", None);
530 memtable.insert_with_key(key2, mutation2).unwrap();
531
532 assert!(memtable.size_bytes() > size_after_insert);
533 }
534
535 #[test]
536 fn test_memtable_should_flush() {
537 let mut memtable = Memtable::new();
538
539 assert!(!memtable.should_flush(1024));
541
542 for i in 0..100 {
544 let (key, mutation) = create_test_mutation(i, "Test data", None);
545 memtable.insert_with_key(key, mutation).unwrap();
546 }
547
548 let current_size = memtable.size_bytes();
550 assert!(memtable.should_flush(current_size - 1));
551 assert!(!memtable.should_flush(current_size + 1000));
552 }
553
554 #[test]
555 fn test_memtable_clear() {
556 let mut memtable = Memtable::new();
557
558 let created_at = memtable.created_at();
559
560 let (key, mutation) = create_test_mutation(1, "Alice", None);
562 memtable.insert_with_key(key, mutation).unwrap();
563
564 assert!(!memtable.is_empty());
565 assert!(memtable.size_bytes() > 0);
566 assert!(memtable.row_count() > 0);
567
568 memtable.clear();
570
571 assert!(memtable.is_empty());
572 assert_eq!(memtable.size_bytes(), 0);
573 assert_eq!(memtable.row_count(), 0);
574 assert_eq!(memtable.created_at(), created_at); }
576
577 #[test]
578 fn test_memtable_iterator() {
579 let mut memtable = Memtable::new();
580
581 let (key1, mutation1) = create_test_mutation(1, "Alice", None);
583 let (key2, mutation2) = create_test_mutation(2, "Bob", None);
584
585 memtable.insert_with_key(key1.clone(), mutation1).unwrap();
586 memtable.insert_with_key(key2.clone(), mutation2).unwrap();
587
588 let mut count = 0;
590 for (key, mutations) in memtable.iter() {
591 assert!(!mutations.is_empty());
592 assert!([key1.token, key2.token].contains(&key.token));
593 count += 1;
594 }
595
596 assert_eq!(count, 2);
597 }
598
599 #[test]
600 fn test_memtable_empty_check() {
601 let mut memtable = Memtable::new();
602 assert!(memtable.is_empty());
603
604 let (key, mutation) = create_test_mutation(1, "Alice", None);
605 memtable.insert_with_key(key, mutation).unwrap();
606 assert!(!memtable.is_empty());
607
608 memtable.clear();
609 assert!(memtable.is_empty());
610 }
611
612 #[test]
613 fn test_memtable_size_estimates() {
614 let small_text = Value::Text("hi".to_string());
616 let large_text = Value::Text("a".repeat(1000));
617 let integer = Value::Integer(42);
618 let uuid = Value::Uuid([0u8; 16]);
619
620 assert_eq!(Memtable::estimate_value_size(&small_text), 2);
621 assert_eq!(Memtable::estimate_value_size(&large_text), 1000);
622 assert_eq!(Memtable::estimate_value_size(&integer), 4);
623 assert_eq!(Memtable::estimate_value_size(&uuid), 16);
624 }
625
626 #[test]
627 fn test_memtable_collection_size_estimates() {
628 let list = Value::List(vec![
630 Value::Integer(1),
631 Value::Integer(2),
632 Value::Integer(3),
633 ]);
634 let size = Memtable::estimate_value_size(&list);
635 assert!(size >= 12); let set = Value::Set(vec![
639 Value::Text("a".to_string()),
640 Value::Text("b".to_string()),
641 ]);
642 let size = Memtable::estimate_value_size(&set);
643 assert!(size >= 2); let map = Value::Map(vec![
647 (Value::Integer(1), Value::Text("one".to_string())),
648 (Value::Integer(2), Value::Text("two".to_string())),
649 ]);
650 let size = Memtable::estimate_value_size(&map);
651 assert!(size >= 11); }
653
654 #[test]
655 fn test_memtable_realistic_flush_threshold() {
656 let mut memtable = Memtable::new();
657
658 let flush_threshold = 64 * 1024 * 1024; for i in 0..10_000 {
664 let (key, mutation) = create_test_mutation(
665 i,
666 "Typical user data with moderate length name",
667 Some(i as i64),
668 );
669 memtable.insert_with_key(key, mutation).unwrap();
670 }
671
672 let final_size = memtable.size_bytes();
673 println!(
674 "10K mutations size: {} bytes ({} KB)",
675 final_size,
676 final_size / 1024
677 );
678
679 assert!(final_size < flush_threshold);
681
682 let avg_size = final_size / 10_000;
684 println!("Average mutation size: {} bytes", avg_size);
685 assert!(avg_size > 0);
686 assert!(avg_size < 10_000); }
688
689 #[test]
690 fn test_memtable_get_nonexistent_key() {
691 let memtable = Memtable::new();
692 let key = DecoratedKey::new(12345, vec![0, 0, 0, 99]);
693
694 assert!(memtable.get(&key).is_none());
695 }
696
697 #[test]
698 fn test_memtable_insert_deprecated_api() {
699 let mut memtable = Memtable::new();
700
701 let table_id = TableId::new("test_ks", "test_table");
702 let partition_key = PartitionKey::single("id", Value::Integer(1));
703 let operations = vec![CellOperation::Write {
704 column: "name".to_string(),
705 value: Value::Text("Alice".to_string()),
706 }];
707
708 let mutation = Mutation::new(table_id, partition_key, None, operations, 1234567890, None);
709
710 let result = memtable.insert(mutation);
712 assert!(result.is_err());
713 }
714
715 #[test]
716 fn test_memtable_nested_collection_depth_limit() {
717 let mut nested_value = Value::Integer(42);
721 for _ in 0..40 {
722 nested_value = Value::List(vec![nested_value]);
723 }
724
725 let size = Memtable::estimate_value_size(&nested_value);
726 assert_eq!(
727 size,
728 40 * 16 + 4,
729 "deep list of a tiny scalar must be counted accurately, not floored"
730 );
731 }
732
733 #[test]
734 fn test_memtable_nested_map_depth_limit() {
735 let mut nested_value = Value::Text("bottom".to_string());
738 for _ in 0..35 {
739 nested_value = Value::Map(vec![(Value::Integer(1), nested_value)]);
740 }
741
742 let size = Memtable::estimate_value_size(&nested_value);
743 assert_eq!(
744 size,
745 35 * (16 + 4) + 6,
746 "deep map of a tiny scalar must be counted accurately, not floored"
747 );
748 }
749
750 #[test]
751 fn test_memtable_nested_udt_depth_limit() {
752 use crate::types::{UdtField, UdtValue};
753
754 let mut nested_value = Value::Integer(1);
757 for i in 0..35 {
758 let udt = UdtValue {
759 type_name: format!("type_{}", i),
760 keyspace: "test_ks".to_string(),
761 fields: vec![UdtField {
762 name: "field".to_string(),
763 value: Some(nested_value),
764 }],
765 };
766 nested_value = Value::Udt(udt);
767 }
768
769 let size = Memtable::estimate_value_size(&nested_value);
770 assert_eq!(
771 size,
772 35 * (16 + 5) + 4,
773 "deep UDT of a tiny scalar must be counted accurately, not floored"
774 );
775 }
776
777 #[test]
778 fn test_memtable_frozen_nested_depth_limit() {
779 let mut nested_value = Value::Integer(99);
782 for _ in 0..40 {
783 nested_value = Value::Frozen(Box::new(nested_value));
784 }
785
786 let size = Memtable::estimate_value_size(&nested_value);
787 assert_eq!(
788 size,
789 40 * 8 + 4,
790 "deep frozen of a tiny scalar must be counted accurately, not floored"
791 );
792 }
793
794 #[test]
795 fn test_memtable_mixed_nested_collections() {
796 use crate::types::{UdtField, UdtValue};
797
798 let mut nested_value = Value::Text("base".to_string());
800
801 for i in 0..50 {
803 nested_value = match i % 5 {
804 0 => Value::List(vec![nested_value]),
805 1 => Value::Set(vec![nested_value]),
806 2 => Value::Map(vec![(Value::Integer(i), nested_value)]),
807 3 => Value::Tuple(vec![nested_value]),
808 4 => Value::Udt(UdtValue {
809 type_name: format!("type_{}", i),
810 keyspace: "test_ks".to_string(),
811 fields: vec![UdtField {
812 name: "f".to_string(),
813 value: Some(nested_value),
814 }],
815 }),
816 _ => unreachable!(),
817 };
818 }
819
820 let size = Memtable::estimate_value_size(&nested_value);
822 assert!(size > 0);
823 }
824
825 #[test]
826 fn test_memtable_depth_limit_exact_boundary() {
827 let mut nested_value = Value::Integer(1);
831 for _ in 0..32 {
832 nested_value = Value::List(vec![nested_value]);
833 }
834
835 let size = Memtable::estimate_value_size(&nested_value);
836 assert_eq!(size, 32 * 16 + 4);
837
838 nested_value = Value::List(vec![nested_value]);
840 let size_over = Memtable::estimate_value_size(&nested_value);
841 assert_eq!(size_over, size + 16);
842 }
843
844 #[test]
845 fn test_estimate_mutation_size_matches_insert_accounting() {
846 let mut memtable = Memtable::new();
850 let (key, mutation) = create_test_mutation(7, "some data here", Some(42));
851
852 let before = memtable.size_bytes();
853 let predicted = memtable.estimate_mutation_size(&mutation);
854 memtable.insert_with_key(key, mutation).unwrap();
855 let actual_delta = memtable.size_bytes() - before;
856
857 assert_eq!(
858 predicted, actual_delta,
859 "estimate_mutation_size must equal the size delta insert applies"
860 );
861 assert!(predicted > 0);
862 }
863
864 #[test]
865 fn test_deep_wide_collection_counted_accurately() {
866 let wide = Value::List((0..500).map(|_| Value::Text("y".repeat(200))).collect());
871 let mut nested = wide;
872 for _ in 0..32 {
873 nested = Value::List(vec![nested]);
874 }
875
876 let size = Memtable::estimate_value_size(&nested);
877 let real_payload = 500 * 200; assert!(
879 size >= real_payload,
880 "wide collection must count real bytes at any depth (got {size})"
881 );
882 assert!(
885 size < real_payload + 10 * 1024,
886 "estimate must be accurate, not floor-inflated (got {size})"
887 );
888 }
889
890 #[test]
891 fn test_deep_narrow_collection_large_scalar_not_undercounted() {
892 let big = 128 * 1024;
900 let mut nested = Value::List(vec![Value::Text("x".repeat(big))]);
901 for _ in 0..32 {
902 nested = Value::List(vec![nested]);
903 }
904
905 let size = Memtable::estimate_value_size(&nested);
906 assert!(
907 size >= big,
908 "deep narrow collection with a large scalar must count the scalar's \
909 real heap size, not the old ~1KB floor (got {size}, expected >= {big})"
910 );
911 }
912
913 #[test]
914 fn test_large_scalar_buried_below_old_cap_counted() {
915 let big = 128 * 1024;
920 let mut nested = Value::Text("z".repeat(big));
921 for _ in 0..40 {
922 nested = Value::List(vec![nested]);
923 }
924
925 let size = Memtable::estimate_value_size(&nested);
926 assert!(
927 size >= big,
928 "scalar buried below the old depth cap must be counted at real size \
929 (got {size}, expected >= {big})"
930 );
931 }
932
933 #[test]
934 fn test_pathological_node_cap_returns_usize_max() {
935 let value = Value::List((0..1_000_001i32).map(Value::Integer).collect());
939 let size = Memtable::estimate_value_size(&value);
940 assert_eq!(
941 size,
942 usize::MAX,
943 "hitting the node cap must fail closed with usize::MAX (got {size})"
944 );
945 }
946
947 #[test]
948 fn test_insert_with_pathological_value_saturates_ledger_no_panic() {
949 let mut memtable = Memtable::new();
954
955 let pathological = Value::List(
957 (0..(Memtable::MAX_ESTIMATE_NODES as i32 + 5))
958 .map(Value::Integer)
959 .collect(),
960 );
961 let table_id = TableId::new("test_ks", "test_table");
962 let partition_key = PartitionKey::single("id", Value::Integer(1));
963 let key = DecoratedKey::from_key_bytes(1i32.to_be_bytes().to_vec()).unwrap();
964 let operations = vec![CellOperation::Write {
965 column: "big".to_string(),
966 value: pathological,
967 }];
968 let mutation = Mutation::new(table_id, partition_key, None, operations, 1, None);
969
970 assert_eq!(memtable.estimate_mutation_size(&mutation), usize::MAX);
972
973 memtable.insert_with_key(key, mutation).unwrap();
975 assert_eq!(memtable.size_bytes(), usize::MAX);
976 }
977
978 #[test]
979 fn test_insert_saturates_when_ledger_already_near_max() {
980 let mut memtable = Memtable::new();
983 memtable.set_size_bytes_for_test(usize::MAX - 3);
984
985 let (key, mutation) = create_test_mutation(1, "Alice", None);
986 memtable.insert_with_key(key, mutation).unwrap();
987
988 assert_eq!(
989 memtable.size_bytes(),
990 usize::MAX,
991 "ledger must saturate at usize::MAX, never wrap"
992 );
993 }
994
995 #[test]
996 fn test_wide_collection_fails_closed_before_enqueuing_children() {
997 use crate::types::{UdtField, UdtValue};
1002
1003 let over = Memtable::MAX_ESTIMATE_NODES + 5;
1004
1005 let list = Value::List((0..over as i32).map(Value::Integer).collect());
1007 assert_eq!(Memtable::estimate_value_size(&list), usize::MAX);
1008
1009 let map = Value::Map(
1011 (0..over as i32)
1012 .map(|i| (Value::Integer(i), Value::Integer(i)))
1013 .collect(),
1014 );
1015 assert_eq!(Memtable::estimate_value_size(&map), usize::MAX);
1016
1017 let udt = Value::Udt(UdtValue {
1019 type_name: "t".to_string(),
1020 keyspace: "ks".to_string(),
1021 fields: (0..over)
1022 .map(|i| UdtField {
1023 name: String::new(),
1024 value: Some(Value::Integer(i as i32)),
1025 })
1026 .collect(),
1027 });
1028 assert_eq!(Memtable::estimate_value_size(&udt), usize::MAX);
1029 }
1030
1031 #[test]
1032 fn test_memtable_shallow_collections_unaffected() {
1033 let simple_list = Value::List(vec![
1037 Value::Integer(1),
1038 Value::Integer(2),
1039 Value::Integer(3),
1040 ]);
1041 let size = Memtable::estimate_value_size(&simple_list);
1042 assert_eq!(size, 12 + 16); let shallow_nested =
1046 Value::List(vec![Value::List(vec![Value::List(vec![Value::Integer(
1047 1,
1048 )])])]);
1049 let size = Memtable::estimate_value_size(&shallow_nested);
1050 assert!(size > 0);
1051 assert!(size < 1024); }
1053}