1use core::{fmt::Display, hash::Hash};
2
3use alloc::boxed::Box;
4use alloc::string::String;
5use alloc::vec::Vec;
6
7use hashbrown::{HashMap, HashSet};
8use serde::{Serialize, de::DeserializeOwned};
9
10use super::namespace::Namespace;
11use super::storage::{Insertion, Origin, Storage};
12use crate::bytes::Bytes;
13
14#[derive(Debug)]
16pub enum StoreError<K, V> {
17 #[allow(missing_docs)]
20 DuplicatedKey {
21 key: K,
22 value_previous: V,
23 value_updated: V,
24 },
25 #[allow(missing_docs)]
29 KeyOutOfSync {
30 key: K,
31 value_previous: V,
32 value_updated: V,
33 },
34 #[allow(missing_docs)]
37 Backend { key: K, error: String },
38}
39
40impl<K, V> StoreError<K, V> {
41 pub fn reason(&self) -> &str {
48 match self {
49 Self::DuplicatedKey { .. } => "the key was already stored with a different value",
50 Self::KeyOutOfSync { .. } => "another process stored the key first",
51 Self::Backend { error, .. } => error,
52 }
53 }
54}
55
56impl<K: core::fmt::Debug, V: core::fmt::Debug> Display for StoreError<K, V> {
57 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
58 match self {
59 Self::DuplicatedKey {
60 key,
61 value_previous,
62 value_updated,
63 } => write!(
64 f,
65 "key {key:?} was already stored with a different value: \
66 kept {value_previous:?}, dropped {value_updated:?}"
67 ),
68 Self::KeyOutOfSync {
69 key,
70 value_previous,
71 value_updated,
72 } => write!(
73 f,
74 "key {key:?} was stored concurrently: kept {value_previous:?}, \
75 dropped {value_updated:?}"
76 ),
77 Self::Backend { key, error } => {
78 write!(f, "storing key {key:?} failed: {error}")
79 }
80 }
81 }
82}
83
84impl<K: core::fmt::Debug, V: core::fmt::Debug> core::error::Error for StoreError<K, V> {}
85
86pub trait StoreKey: Serialize + DeserializeOwned + PartialEq + Eq + Hash + Clone {}
88pub trait StoreValue: Serialize + DeserializeOwned + PartialEq + Eq + Clone {}
90
91impl<T: Serialize + DeserializeOwned + PartialEq + Eq + Clone + Hash> StoreKey for T {}
92impl<T: Serialize + DeserializeOwned + PartialEq + Eq + Clone> StoreValue for T {}
93
94#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
96pub enum CacheOption {
97 #[default]
100 Eager,
101 Lazy,
105}
106
107#[derive(Debug, Default)]
109enum StorageOption {
110 #[default]
112 InMemory,
113 Environment(Namespace),
115 Explicit(Box<dyn Storage>, Namespace),
117}
118
119#[derive(Debug, Default)]
121pub struct StoreOptions {
122 storage: StorageOption,
123 cache: CacheOption,
124}
125
126impl StoreOptions {
127 pub fn new() -> Self {
129 Self::default()
130 }
131
132 pub fn storage<N: Into<Namespace>>(mut self, namespace: N) -> Self {
134 self.storage = StorageOption::Environment(namespace.into());
135 self
136 }
137
138 pub fn storage_with<N: Into<Namespace>>(
141 mut self,
142 storage: Box<dyn Storage>,
143 namespace: N,
144 ) -> Self {
145 self.storage = StorageOption::Explicit(storage, namespace.into());
146 self
147 }
148
149 pub fn cache(mut self, cache: CacheOption) -> Self {
152 self.cache = cache;
153 self
154 }
155}
156
157pub struct Store<K, V> {
199 entries: HashMap<K, MemoryEntry<V>>,
200 known: HashSet<K>,
206 storage: Option<Box<dyn Storage>>,
207 namespace: Option<Namespace>,
208 cache: CacheOption,
209 generation: Option<u32>,
214}
215
216impl<K: StoreKey, V: StoreValue> Store<K, V> {
217 #[cfg_attr(
222 feature = "tracing",
223 tracing::instrument(level = "trace", skip_all, fields(options = ?options))
224 )]
225 pub fn new(options: StoreOptions) -> Self {
226 let (storage, namespace, generation) = match options.storage {
227 StorageOption::InMemory => (None, None, None),
228 StorageOption::Environment(namespace) => {
229 let generation = crate::environment::generation();
233 (
234 Some(super::storage::open(namespace.as_str())),
235 Some(namespace),
236 Some(generation),
237 )
238 }
239 StorageOption::Explicit(storage, namespace) => (Some(storage), Some(namespace), None),
240 };
241
242 let mut store = Self {
243 entries: HashMap::new(),
244 known: HashSet::new(),
245 storage,
246 namespace,
247 cache: options.cache,
248 generation,
249 };
250
251 if matches!(store.cache, CacheOption::Eager) && store.storage.is_some() {
252 store.ingest();
253 }
254
255 store
256 }
257
258 pub fn namespace(&self) -> Option<&Namespace> {
260 self.namespace.as_ref()
261 }
262
263 pub fn get(&self, key: &K) -> Option<&V> {
273 if self.stale() {
274 return None;
275 }
276
277 self.entries.get(key).map(MemoryEntry::value)
278 }
279
280 pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
285 self.reset_if_stale();
286
287 if matches!(self.cache, CacheOption::Lazy)
288 && !self.entries.contains_key(key)
289 && let Some(value) = self.fetch(key)
290 {
291 self.entries.insert(key.clone(), MemoryEntry::Stored(value));
292 }
293
294 self.entries.get_mut(key).map(MemoryEntry::value_mut)
295 }
296
297 pub fn remove(&mut self, key: &K) -> Option<V> {
308 self.reset_if_stale();
309
310 let entry = self.take(key);
311
312 if self.storage.is_some() && entry.as_ref().is_some_and(MemoryEntry::is_stored) {
316 self.known.insert(key.clone());
317 }
318
319 entry.map(MemoryEntry::into_value)
320 }
321
322 pub fn insert(&mut self, key: K, value: V) -> Result<(), StoreError<K, V>> {
339 self.reset_if_stale();
340
341 let known = match self.entries.get(&key) {
342 Some(MemoryEntry::Stored(existing)) if existing == &value => return Ok(()),
343 Some(MemoryEntry::Stored(_)) => true,
344 Some(MemoryEntry::Unwritten(existing)) if existing != &value => {
346 return Err(StoreError::DuplicatedKey {
347 value_previous: existing.clone(),
348 value_updated: value,
349 key,
350 });
351 }
352 Some(MemoryEntry::Unwritten(_)) | None => self.known.contains(&key),
355 };
356
357 let Some(storage) = self.storage.as_deref() else {
358 return match self.entries.get(&key) {
359 Some(existing) => Err(StoreError::DuplicatedKey {
360 value_previous: existing.value().clone(),
361 value_updated: value,
362 key,
363 }),
364 None => {
365 self.entries.insert(key, MemoryEntry::Stored(value));
366 Ok(())
367 }
368 };
369 };
370
371 match write_through(storage, &key, &value) {
375 Written::Stored => {
376 self.record(key, value);
377 Ok(())
378 }
379 Written::Failed(error) => {
380 if matches!(self.cache, CacheOption::Eager) {
384 self.entries
385 .insert(key.clone(), MemoryEntry::Unwritten(value));
386 }
387 Err(StoreError::Backend { key, error })
388 }
389 Written::Conflict(existing) => {
390 if matches!(self.cache, CacheOption::Eager) {
393 self.entries
394 .insert(key.clone(), MemoryEntry::Stored(existing.clone()));
395 } else {
396 self.known.insert(key.clone());
397 }
398
399 let (value_previous, value_updated) = (existing, value);
403 Err(if known {
404 StoreError::DuplicatedKey {
405 key,
406 value_previous,
407 value_updated,
408 }
409 } else {
410 StoreError::KeyOutOfSync {
411 key,
412 value_previous,
413 value_updated,
414 }
415 })
416 }
417 }
418 }
419
420 pub fn purge_key(&mut self, key: &K) -> Option<V> {
427 self.reset_if_stale();
428
429 let value = self.take(key).map(MemoryEntry::into_value);
430
431 self.known.remove(key);
434 if let Some(storage) = self.storage.as_deref() {
435 storage.purge_key(&encode(key));
436 }
437
438 value
439 }
440
441 pub fn clear(&mut self) {
449 self.reset_if_stale();
450
451 if self.storage.is_some() {
452 self.known.extend(
453 self.entries
454 .drain()
455 .filter(|(_, entry)| entry.is_stored())
456 .map(|(key, _)| key),
457 );
458 } else {
459 self.entries.clear();
460 }
461 }
462
463 pub fn purge(&mut self) {
471 self.reset_if_stale();
472
473 self.entries.clear();
474 self.known.clear();
475
476 if let Some(storage) = self.storage.as_deref() {
477 storage.purge();
478 }
479 }
480
481 #[cfg_attr(
487 feature = "tracing",
488 tracing::instrument(level = "trace", skip_all, fields(namespace = ?self.namespace))
489 )]
490 pub fn sync(&mut self) {
491 if self.reset_if_stale() && matches!(self.cache, CacheOption::Eager) {
493 return;
494 }
495 self.ingest();
496 }
497
498 fn ingest(&mut self) {
500 let Some(storage) = self.storage.as_deref() else {
501 return;
502 };
503 let entries = &mut self.entries;
504
505 storage.scan(&mut |key, value| {
506 if let Some((key, value)) = decode_entry::<K, V>(key, value) {
507 entries.insert(key, MemoryEntry::Stored(value));
509 }
510 });
511 }
512
513 pub fn scan<F: FnMut(K, V)>(&mut self, mut func: F) {
522 self.reset_if_stale();
523
524 let Some(storage) = self.storage.as_deref() else {
525 for (key, entry) in self.entries.iter() {
526 func(key.clone(), entry.value().clone());
527 }
528 return;
529 };
530
531 storage.scan(&mut |key, value| {
532 if let Some((key, value)) = decode_entry::<K, V>(key, value) {
533 func(key, value);
534 }
535 });
536 }
537
538 pub fn for_each<F: FnMut(&K, &V)>(&self, mut func: F) {
540 if self.stale() {
541 return;
542 }
543
544 for (key, entry) in self.entries.iter() {
545 func(key, entry.value());
546 }
547 }
548
549 pub fn len(&self) -> usize {
551 if self.stale() {
552 return 0;
553 }
554
555 self.entries.len()
556 }
557
558 pub fn is_empty(&self) -> bool {
560 self.len() == 0
561 }
562
563 fn take(&mut self, key: &K) -> Option<MemoryEntry<V>> {
566 match self.entries.remove(key) {
567 Some(entry) => Some(entry),
568 None => match self.cache {
569 CacheOption::Eager => None,
570 CacheOption::Lazy => self.fetch(key).map(MemoryEntry::Stored),
571 },
572 }
573 }
574
575 fn fetch(&self, key: &K) -> Option<V> {
577 let bytes = self.storage.as_deref()?.get(&encode(key))?;
578 decode::<V>(&bytes)
579 }
580
581 fn record(&mut self, key: K, value: V) {
583 match self.cache {
584 CacheOption::Eager => {
585 self.entries.insert(key, MemoryEntry::Stored(value));
586 }
587 CacheOption::Lazy => {
590 self.known.insert(key);
591 }
592 }
593 }
594
595 fn stale(&self) -> bool {
599 match self.generation {
600 Some(generation) => generation != crate::environment::generation(),
601 None => false,
602 }
603 }
604
605 fn reset_if_stale(&mut self) -> bool {
609 if !self.stale() {
610 return false;
611 }
612
613 let (Some(namespace), Some(_)) = (&self.namespace, self.generation) else {
615 return false;
616 };
617
618 log::debug!("Environment switched, resetting the store for {namespace}");
619
620 self.generation = Some(crate::environment::generation());
623 self.storage = Some(super::storage::open(namespace.as_str()));
624 self.entries.clear();
625 self.known.clear();
626 if matches!(self.cache, CacheOption::Eager) {
627 self.ingest();
628 }
629
630 true
631 }
632}
633
634impl<K, V> core::fmt::Debug for Store<K, V> {
635 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
636 f.debug_struct("Store")
637 .field("namespace", &self.namespace)
638 .field("cache", &self.cache)
639 .field("entries", &self.entries.len())
640 .field("known", &self.known.len())
641 .field("storage", &self.storage)
642 .finish()
643 }
644}
645
646impl<K: StoreKey, V: StoreValue> Display for Store<K, V> {
647 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
648 match (&self.namespace, &self.storage) {
649 (Some(namespace), Some(storage)) => write!(
650 f,
651 "{namespace} ({} entries in {})",
652 self.len(),
653 storage.describe()
654 ),
655 _ => write!(f, "in-memory ({} entries)", self.len()),
656 }
657 }
658}
659
660enum MemoryEntry<V> {
662 Stored(V),
664 Unwritten(V),
668}
669
670impl<V> MemoryEntry<V> {
671 fn value(&self) -> &V {
672 match self {
673 Self::Stored(value) | Self::Unwritten(value) => value,
674 }
675 }
676
677 fn value_mut(&mut self) -> &mut V {
678 match self {
679 Self::Stored(value) | Self::Unwritten(value) => value,
680 }
681 }
682
683 fn into_value(self) -> V {
684 match self {
685 Self::Stored(value) | Self::Unwritten(value) => value,
686 }
687 }
688
689 fn is_stored(&self) -> bool {
690 matches!(self, Self::Stored(_))
691 }
692}
693
694pub(crate) enum Written<V> {
696 Stored,
698 Conflict(V),
700 Failed(String),
702}
703
704pub(crate) fn write_through<K: StoreKey, V: StoreValue>(
711 storage: &dyn Storage,
712 key: &K,
713 value: &V,
714) -> Written<V> {
715 let key_bytes = encode(key);
716
717 match storage.insert(&key_bytes, encode(value), Origin::Local) {
718 Insertion::Stored => Written::Stored,
719 Insertion::Failed(error) => Written::Failed(error),
720 Insertion::Conflict(existing) => match decode::<V>(&existing) {
721 Some(existing) if &existing != value => Written::Conflict(existing),
722 Some(_) => Written::Stored,
723 None => match storage.replace(&key_bytes, encode(value), Origin::Local) {
728 Insertion::Failed(error) => Written::Failed(error),
729 _ => Written::Stored,
730 },
731 },
732 }
733}
734
735pub(crate) fn encode<T: Serialize>(value: &T) -> Bytes {
745 let mut bytes = Vec::new();
746 ciborium::ser::into_writer(value, &mut bytes).expect("Can serialize data");
747 Bytes::from_bytes_vec(bytes)
748}
749
750pub(crate) fn decode<T: DeserializeOwned>(bytes: &[u8]) -> Option<T> {
753 match ciborium::de::from_reader(bytes) {
754 Ok(value) => Some(value),
755 Err(err) => {
756 log::warn!("Corrupted cache entry, ignoring it: {err}");
757 None
758 }
759 }
760}
761
762fn decode_entry<K: StoreKey, V: StoreValue>(key: &[u8], value: &[u8]) -> Option<(K, V)> {
763 Some((decode::<K>(key)?, decode::<V>(value)?))
764}
765
766#[cfg(all(test, native_cache))]
767mod tests {
768 use std::string::ToString;
769 use std::vec;
770
771 use super::*;
772
773 fn eager(path: &str) -> StoreOptions {
774 StoreOptions::new().storage(Namespace::new(path))
775 }
776
777 fn lazy(path: &str) -> StoreOptions {
778 eager(path).cache(CacheOption::Lazy)
779 }
780
781 #[test_log::test]
782 #[serial_test::serial]
783 #[cfg_attr(miri, ignore)]
784 fn test_cache_simple() {
785 let dir = tempfile::tempdir().unwrap();
786 crate::environment::set_root(dir.path());
787
788 let key1 = || "key1".to_string();
789 let key2 = || "key2".to_string();
790
791 let value1 = || "value1".to_string();
792 let value2 = || "value2".to_string();
793
794 let mut cache = Store::<String, String>::new(eager("test"));
795 cache.insert(key1(), value1()).unwrap();
796 cache.insert(key2(), value2()).unwrap();
797
798 let result = cache.insert(key1(), value2());
799 assert!(
800 result.is_err(),
801 "Can't reinsert the same key with a different value."
802 );
803
804 assert_eq!(cache.len(), 2);
805
806 let value1_actual = cache.get(&key1()).unwrap();
807 assert_eq!(value1_actual, &value1());
808
809 let value2_actual = cache.get(&key2()).unwrap();
810 assert_eq!(value2_actual, &value2());
811 }
812
813 #[test_log::test]
817 #[serial_test::serial]
818 #[cfg_attr(miri, ignore)]
819 fn test_on_disk_format_is_stable() {
820 let dir = tempfile::tempdir().unwrap();
821 crate::environment::set_root(dir.path());
822 let namespace = Namespace::scoped("golden", "device0/matmul");
823
824 let mut cache = Store::<String, u32>::new(StoreOptions::new().storage(namespace));
825 cache.insert("shape=2x2".to_string(), 42).unwrap();
826
827 let expected_namespace =
828 std::format!("golden/{}/device0/matmul", env!("CARGO_PKG_VERSION"));
829 assert_eq!(cache.namespace().unwrap().as_str(), expected_namespace);
830
831 let path = crate::environment::path();
832 assert!(path.exists(), "Database missing at {path:?}");
833
834 let reopened = Store::<String, u32>::new(
837 StoreOptions::new().storage(Namespace::scoped("golden", "device0/matmul")),
838 );
839 assert_eq!(reopened.get(&"shape=2x2".to_string()), Some(&42));
840 }
841
842 #[test_log::test]
845 #[serial_test::serial]
846 #[cfg_attr(miri, ignore)]
847 fn test_entries_survive_reopen() {
848 let dir = tempfile::tempdir().unwrap();
849 crate::environment::set_root(dir.path());
850
851 let mut cache = Store::<String, u32>::new(eager("reopen"));
852 cache.insert("key".to_string(), 7).unwrap();
853 drop(cache);
854
855 let cache = Store::<String, u32>::new(eager("reopen"));
856 assert_eq!(cache.get(&"key".to_string()), Some(&7));
857 }
858
859 #[test_log::test]
861 #[serial_test::serial]
862 #[cfg_attr(miri, ignore)]
863 fn test_stores_are_isolated() {
864 let dir = tempfile::tempdir().unwrap();
865 crate::environment::set_root(dir.path());
866
867 let mut first = Store::<String, u32>::new(eager("device0/matmul"));
868 first.insert("key".to_string(), 1).unwrap();
869
870 let mut second = Store::<String, u32>::new(eager("device1/matmul"));
871 assert_eq!(second.get(&"key".to_string()), None);
872 second.insert("key".to_string(), 2).unwrap();
873
874 assert_eq!(first.get(&"key".to_string()), Some(&1));
875 assert_eq!(second.get(&"key".to_string()), Some(&2));
876 }
877
878 #[test_log::test]
879 #[serial_test::serial]
880 #[cfg_attr(miri, ignore)]
881 fn lazy_values_survive_reopen_and_load_lazily() {
882 let dir = tempfile::tempdir().unwrap();
883 crate::environment::set_root(dir.path());
884
885 let mut cache = Store::<String, Bytes>::new(lazy("ptx_sm90"));
886 cache
887 .insert(
888 "kernel_a".to_string(),
889 Bytes::from_bytes_vec(std::vec![1, 2, 3]),
890 )
891 .unwrap();
892 cache
893 .insert(
894 "kernel_b".to_string(),
895 Bytes::from_bytes_vec(std::vec![4, 5]),
896 )
897 .unwrap();
898 assert!(cache.is_empty());
900 drop(cache);
901
902 let mut cache = Store::<String, Bytes>::new(lazy("ptx_sm90"));
903 assert!(cache.is_empty());
905
906 assert_eq!(
907 cache.get_mut(&"kernel_a".to_string()).map(|v| v.to_vec()),
908 Some(std::vec![1, 2, 3])
909 );
910 assert_eq!(cache.len(), 1, "get_mut memoizes");
911 assert_eq!(
912 cache.remove(&"kernel_b".to_string()).map(|v| v.to_vec()),
913 Some(std::vec![4, 5])
914 );
915 assert_eq!(cache.len(), 1, "remove reads through without memoizing");
916 assert_eq!(cache.get_mut(&"missing".to_string()), None);
917 }
918
919 #[test_log::test]
920 #[serial_test::serial]
921 #[cfg_attr(miri, ignore)]
922 fn lazy_reinserting_a_different_value_errors() {
923 let dir = tempfile::tempdir().unwrap();
924 crate::environment::set_root(dir.path());
925
926 let mut cache = Store::<String, Bytes>::new(lazy("ptx_sm90"));
927 let kernel = |byte: u8| Bytes::from_bytes_vec(std::vec![byte]);
928 cache.insert("kernel".to_string(), kernel(1)).unwrap();
929
930 assert!(cache.insert("kernel".to_string(), kernel(1)).is_ok());
931 let error = cache.insert("kernel".to_string(), kernel(2));
932 assert!(matches!(error, Err(StoreError::DuplicatedKey { .. })));
933
934 assert!(cache.remove(&"kernel".to_string()).is_some());
937 let error = cache.insert("kernel".to_string(), kernel(2));
938 assert!(matches!(error, Err(StoreError::DuplicatedKey { .. })));
939 }
940
941 #[test_log::test]
945 #[serial_test::serial]
946 #[cfg_attr(miri, ignore)]
947 fn switching_environments_resets_bound_stores() {
948 let first = tempfile::tempdir().unwrap();
949 let second = tempfile::tempdir().unwrap();
950
951 crate::environment::set_root(first.path());
952 let mut store = Store::<String, u32>::new(eager("reset"));
953 store.insert("key".to_string(), 1).unwrap();
954 assert_eq!(store.get(&"key".to_string()), Some(&1));
955
956 crate::environment::set_root(second.path());
958 assert_eq!(store.get(&"key".to_string()), None);
959 assert_eq!(store.len(), 0);
960
961 store.insert("key".to_string(), 2).unwrap();
964 assert_eq!(store.get(&"key".to_string()), Some(&2));
965
966 crate::environment::set_root(first.path());
968 store.sync();
969 assert_eq!(store.get(&"key".to_string()), Some(&1));
970 }
971
972 #[test_log::test]
975 #[serial_test::serial]
976 #[cfg_attr(miri, ignore)]
977 fn unbound_stores_survive_environment_switches() {
978 let root = tempfile::tempdir().unwrap();
979
980 let mut store = Store::<String, u32>::new(StoreOptions::new());
981 store.insert("key".to_string(), 1).unwrap();
982
983 crate::environment::set_root(root.path());
984 assert_eq!(store.get(&"key".to_string()), Some(&1));
985 }
986
987 #[test_log::test]
990 #[serial_test::serial]
991 #[cfg_attr(miri, ignore)]
992 fn purge_key_deletes_one_entry_durably() {
993 let dir = tempfile::tempdir().unwrap();
994 crate::environment::set_root(dir.path());
995
996 let mut store = Store::<String, u32>::new(eager("purge_key"));
997 store.insert("gone".to_string(), 1).unwrap();
998 store.insert("kept".to_string(), 2).unwrap();
999
1000 assert_eq!(store.purge_key(&"gone".to_string()), Some(1));
1001 store.insert("gone".to_string(), 3).unwrap();
1003 assert_eq!(store.purge_key(&"gone".to_string()), Some(3));
1004 drop(store);
1005
1006 let store = Store::<String, u32>::new(eager("purge_key"));
1007 assert_eq!(store.get(&"gone".to_string()), None);
1008 assert_eq!(store.get(&"kept".to_string()), Some(&2));
1009 }
1010
1011 #[test_log::test]
1014 #[serial_test::serial]
1015 #[cfg_attr(miri, ignore)]
1016 fn clear_evicts_memory_but_not_the_storage() {
1017 let dir = tempfile::tempdir().unwrap();
1018 crate::environment::set_root(dir.path());
1019
1020 let mut store = Store::<String, u32>::new(eager("clear"));
1021 store.insert("key".to_string(), 1).unwrap();
1022
1023 store.clear();
1024 assert!(store.is_empty());
1025 assert!(matches!(
1028 store.insert("key".to_string(), 2),
1029 Err(StoreError::DuplicatedKey { .. })
1030 ));
1031
1032 store.sync();
1033 assert_eq!(store.get(&"key".to_string()), Some(&1));
1034 }
1035
1036 #[test_log::test]
1039 #[serial_test::serial]
1040 #[cfg_attr(miri, ignore)]
1041 fn purge_deletes_durably_and_frees_the_keys() {
1042 let dir = tempfile::tempdir().unwrap();
1043 crate::environment::set_root(dir.path());
1044
1045 let mut store = Store::<String, u32>::new(eager("purge"));
1046 store.insert("kept".to_string(), 1).unwrap();
1047 store.insert("gone".to_string(), 2).unwrap();
1048
1049 let mut other = Store::<String, u32>::new(eager("other"));
1051 other.insert("kept".to_string(), 9).unwrap();
1052
1053 store.purge();
1054 assert!(store.is_empty());
1055
1056 store.insert("kept".to_string(), 3).unwrap();
1058 drop(store);
1059
1060 let store = Store::<String, u32>::new(eager("purge"));
1061 assert_eq!(store.get(&"kept".to_string()), Some(&3));
1062 assert_eq!(store.get(&"gone".to_string()), None);
1063 assert_eq!(
1064 Store::<String, u32>::new(eager("other")).get(&"kept".to_string()),
1065 Some(&9)
1066 );
1067 }
1068
1069 #[test_log::test]
1072 #[serial_test::serial]
1073 #[cfg_attr(miri, ignore)]
1074 fn scan_visits_the_storage_without_retaining() {
1075 let dir = tempfile::tempdir().unwrap();
1076 crate::environment::set_root(dir.path());
1077
1078 let mut store = Store::<String, u32>::new(lazy("scan"));
1079 store.insert("a".to_string(), 1).unwrap();
1080 store.insert("b".to_string(), 2).unwrap();
1081
1082 let mut seen = std::vec::Vec::new();
1083 store.scan(|key, value| seen.push((key, value)));
1084 seen.sort();
1085
1086 assert_eq!(seen, std::vec![("a".to_string(), 1), ("b".to_string(), 2)]);
1087 assert!(store.is_empty(), "nothing stays resident after a scan");
1088 }
1089
1090 #[test]
1091 fn in_memory_store_needs_no_storage() {
1092 let mut store = Store::<String, u32>::new(StoreOptions::new());
1093
1094 store.insert("key".to_string(), 1).unwrap();
1095 assert_eq!(store.get(&"key".to_string()), Some(&1));
1096 assert!(store.insert("key".to_string(), 1).is_ok());
1097 assert!(matches!(
1098 store.insert("key".to_string(), 2),
1099 Err(StoreError::DuplicatedKey { .. })
1100 ));
1101
1102 assert_eq!(store.remove(&"key".to_string()), Some(1));
1105 store.insert("key".to_string(), 2).unwrap();
1106 assert_eq!(store.get(&"key".to_string()), Some(&2));
1107 }
1108}