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> {
203 entries: HashMap<K, V>,
204 known: HashSet<K>,
210 storage: Option<Box<dyn Storage>>,
211 namespace: Option<Namespace>,
212 cache: CacheOption,
213 loaded: bool,
216 generation: Option<u32>,
221}
222
223impl<K: StoreKey, V: StoreValue> Store<K, V> {
224 #[cfg_attr(
232 feature = "tracing",
233 tracing::instrument(level = "trace", skip_all, fields(options = ?options))
234 )]
235 pub fn new(options: StoreOptions) -> Self {
236 let (storage, namespace, generation) = match options.storage {
237 StorageOption::InMemory => (None, None, None),
238 StorageOption::Environment(namespace) => {
239 let generation = crate::environment::generation();
243 (
244 Some(super::storage::open(namespace.as_str())),
245 Some(namespace),
246 Some(generation),
247 )
248 }
249 StorageOption::Explicit(storage, namespace) => (Some(storage), Some(namespace), None),
250 };
251
252 let mut store = Self {
253 entries: HashMap::new(),
254 known: HashSet::new(),
255 storage,
256 namespace,
257 cache: options.cache,
258 loaded: false,
259 generation,
260 };
261
262 match (store.cache, &store.storage) {
263 (CacheOption::Eager, Some(_)) => store.sync(),
264 _ => store.loaded = true,
267 }
268
269 store
270 }
271
272 pub fn namespace(&self) -> Option<&Namespace> {
274 self.namespace.as_ref()
275 }
276
277 pub fn get(&self, key: &K) -> Option<&V> {
287 if self.stale() {
288 return None;
289 }
290
291 self.entries.get(key)
292 }
293
294 pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
299 self.reset_if_stale();
300 self.refresh_if_pending();
301
302 if matches!(self.cache, CacheOption::Lazy)
303 && !self.entries.contains_key(key)
304 && let Some(value) = self.fetch(key)
305 {
306 self.entries.insert(key.clone(), value);
307 }
308
309 self.entries.get_mut(key)
310 }
311
312 pub fn remove(&mut self, key: &K) -> Option<V> {
321 self.reset_if_stale();
322 self.refresh_if_pending();
323
324 let value = match self.entries.remove(key) {
325 Some(value) => Some(value),
326 None => match self.cache {
327 CacheOption::Eager => None,
328 CacheOption::Lazy => self.fetch(key),
329 },
330 };
331
332 if value.is_some() && self.storage.is_some() {
335 self.known.insert(key.clone());
336 }
337
338 value
339 }
340
341 pub fn insert(&mut self, key: K, value: V) -> Result<(), StoreError<K, V>> {
354 self.reset_if_stale();
355 self.refresh_if_pending();
356
357 let known = match self.entries.get(&key) {
358 Some(existing) if existing == &value => return Ok(()),
359 existing => existing.is_some() || self.known.contains(&key),
360 };
361
362 let Some(storage) = self.storage.as_deref() else {
363 return match self.entries.get(&key) {
364 Some(existing) => Err(StoreError::DuplicatedKey {
365 value_previous: existing.clone(),
366 value_updated: value,
367 key,
368 }),
369 None => {
370 self.entries.insert(key, value);
371 Ok(())
372 }
373 };
374 };
375
376 match write_through(storage, &key, &value) {
380 Written::Stored => {
381 self.record(key, value);
382 Ok(())
383 }
384 Written::Failed(error) => Err(StoreError::Backend { key, error }),
385 Written::Conflict(existing) => {
386 if matches!(self.cache, CacheOption::Eager) {
389 self.entries.insert(key.clone(), existing.clone());
390 } else {
391 self.known.insert(key.clone());
392 }
393
394 let (value_previous, value_updated) = (existing, value);
398 Err(if known {
399 StoreError::DuplicatedKey {
400 key,
401 value_previous,
402 value_updated,
403 }
404 } else {
405 StoreError::KeyOutOfSync {
406 key,
407 value_previous,
408 value_updated,
409 }
410 })
411 }
412 }
413 }
414
415 pub fn purge_key(&mut self, key: &K) -> Option<V> {
422 self.reset_if_stale();
423 self.refresh_if_pending();
424
425 let value = match self.entries.remove(key) {
426 Some(value) => Some(value),
427 None => match self.cache {
428 CacheOption::Eager => None,
429 CacheOption::Lazy => self.fetch(key),
430 },
431 };
432
433 self.known.remove(key);
436 if let Some(storage) = self.storage.as_deref() {
437 storage.purge_key(&encode(key));
438 }
439
440 value
441 }
442
443 pub fn clear(&mut self) {
450 self.reset_if_stale();
451
452 if self.storage.is_some() {
453 self.known.extend(self.entries.drain().map(|(key, _)| key));
454 } else {
455 self.entries.clear();
456 }
457 }
458
459 pub fn purge(&mut self) {
467 self.reset_if_stale();
468
469 self.entries.clear();
470 self.known.clear();
471
472 if let Some(storage) = self.storage.as_deref() {
473 storage.purge();
474 }
475 }
476
477 #[cfg_attr(
483 feature = "tracing",
484 tracing::instrument(level = "trace", skip_all, fields(namespace = ?self.namespace))
485 )]
486 pub fn sync(&mut self) {
487 self.reset_if_stale();
488
489 let Some(storage) = self.storage.as_deref() else {
490 self.loaded = true;
491 return;
492 };
493
494 let loading = storage.loading();
497 let entries = &mut self.entries;
498
499 storage.scan(&mut |key, value| {
500 if let Some((key, value)) = decode_entry::<K, V>(key, value) {
501 entries.insert(key, value);
502 }
503 });
504
505 self.loaded = !loading;
506 }
507
508 pub fn pending_load(&self) -> bool {
513 !self.loaded || self.stale()
514 }
515
516 pub fn scan<F: FnMut(K, V)>(&mut self, mut func: F) -> bool {
529 self.reset_if_stale();
530
531 let Some(storage) = self.storage.as_deref() else {
532 for (key, value) in self.entries.iter() {
533 func(key.clone(), value.clone());
534 }
535 return true;
536 };
537
538 let loading = storage.loading();
541
542 storage.scan(&mut |key, value| {
543 if let Some((key, value)) = decode_entry::<K, V>(key, value) {
544 func(key, value);
545 }
546 });
547
548 !loading
549 }
550
551 pub fn for_each<F: FnMut(&K, &V)>(&self, mut func: F) {
553 if self.stale() {
554 return;
555 }
556
557 for (key, value) in self.entries.iter() {
558 func(key, value);
559 }
560 }
561
562 pub fn len(&self) -> usize {
564 if self.stale() {
565 return 0;
566 }
567
568 self.entries.len()
569 }
570
571 pub fn is_empty(&self) -> bool {
573 self.len() == 0
574 }
575
576 fn fetch(&self, key: &K) -> Option<V> {
578 let bytes = self.storage.as_deref()?.get(&encode(key))?;
579 decode::<V>(&bytes)
580 }
581
582 fn record(&mut self, key: K, value: V) {
584 match self.cache {
585 CacheOption::Eager => {
586 self.entries.insert(key, value);
587 }
588 CacheOption::Lazy => {
591 self.known.insert(key);
592 }
593 }
594 }
595
596 fn refresh_if_pending(&mut self) {
599 if !self.loaded {
600 self.sync();
601 }
602 }
603
604 fn stale(&self) -> bool {
608 match self.generation {
609 Some(generation) => generation != crate::environment::generation(),
610 None => false,
611 }
612 }
613
614 fn reset_if_stale(&mut self) {
621 if !self.stale() {
622 return;
623 }
624
625 let (Some(namespace), Some(_)) = (&self.namespace, self.generation) else {
627 return;
628 };
629
630 log::debug!("Environment switched, resetting the store for {namespace}");
631
632 self.generation = Some(crate::environment::generation());
635 self.storage = Some(super::storage::open(namespace.as_str()));
636 self.entries.clear();
637 self.known.clear();
638 self.loaded = matches!(self.cache, CacheOption::Lazy);
639 }
640}
641
642impl<K, V> core::fmt::Debug for Store<K, V> {
643 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
644 f.debug_struct("Store")
645 .field("namespace", &self.namespace)
646 .field("cache", &self.cache)
647 .field("entries", &self.entries.len())
648 .field("known", &self.known.len())
649 .field("storage", &self.storage)
650 .field("loaded", &self.loaded)
651 .finish()
652 }
653}
654
655impl<K: StoreKey, V: StoreValue> Display for Store<K, V> {
656 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
657 match (&self.namespace, &self.storage) {
658 (Some(namespace), Some(storage)) => write!(
659 f,
660 "{namespace} ({} entries in {})",
661 self.len(),
662 storage.describe()
663 ),
664 _ => write!(f, "in-memory ({} entries)", self.len()),
665 }
666 }
667}
668
669pub(crate) enum Written<V> {
671 Stored,
673 Conflict(V),
675 Failed(String),
677}
678
679pub(crate) fn write_through<K: StoreKey, V: StoreValue>(
686 storage: &dyn Storage,
687 key: &K,
688 value: &V,
689) -> Written<V> {
690 let key_bytes = encode(key);
691
692 match storage.insert(&key_bytes, encode(value), Origin::Local) {
693 Insertion::Stored => Written::Stored,
694 Insertion::Failed(error) => Written::Failed(error),
695 Insertion::Conflict(existing) => match decode::<V>(&existing) {
696 Some(existing) if &existing != value => Written::Conflict(existing),
697 Some(_) => Written::Stored,
698 None => match storage.replace(&key_bytes, encode(value), Origin::Local) {
703 Insertion::Failed(error) => Written::Failed(error),
704 _ => Written::Stored,
705 },
706 },
707 }
708}
709
710pub(crate) fn encode<T: Serialize>(value: &T) -> Bytes {
720 let mut bytes = Vec::new();
721 ciborium::ser::into_writer(value, &mut bytes).expect("Can serialize data");
722 Bytes::from_bytes_vec(bytes)
723}
724
725pub(crate) fn decode<T: DeserializeOwned>(bytes: &[u8]) -> Option<T> {
728 match ciborium::de::from_reader(bytes) {
729 Ok(value) => Some(value),
730 Err(err) => {
731 log::warn!("Corrupted cache entry, ignoring it: {err}");
732 None
733 }
734 }
735}
736
737fn decode_entry<K: StoreKey, V: StoreValue>(key: &[u8], value: &[u8]) -> Option<(K, V)> {
738 Some((decode::<K>(key)?, decode::<V>(value)?))
739}
740
741#[cfg(all(test, feature = "cache"))]
742mod tests {
743 use std::string::ToString;
744 use std::vec;
745
746 use super::*;
747
748 fn eager(path: &str) -> StoreOptions {
749 StoreOptions::new().storage(Namespace::new(path))
750 }
751
752 fn lazy(path: &str) -> StoreOptions {
753 eager(path).cache(CacheOption::Lazy)
754 }
755
756 #[test_log::test]
757 #[serial_test::serial]
758 #[cfg_attr(miri, ignore)]
759 fn test_cache_simple() {
760 let dir = tempfile::tempdir().unwrap();
761 crate::environment::set_root(dir.path());
762
763 let key1 = || "key1".to_string();
764 let key2 = || "key2".to_string();
765
766 let value1 = || "value1".to_string();
767 let value2 = || "value2".to_string();
768
769 let mut cache = Store::<String, String>::new(eager("test"));
770 cache.insert(key1(), value1()).unwrap();
771 cache.insert(key2(), value2()).unwrap();
772
773 let result = cache.insert(key1(), value2());
774 assert!(
775 result.is_err(),
776 "Can't reinsert the same key with a different value."
777 );
778
779 assert_eq!(cache.len(), 2);
780
781 let value1_actual = cache.get(&key1()).unwrap();
782 assert_eq!(value1_actual, &value1());
783
784 let value2_actual = cache.get(&key2()).unwrap();
785 assert_eq!(value2_actual, &value2());
786 }
787
788 #[test_log::test]
792 #[serial_test::serial]
793 #[cfg_attr(miri, ignore)]
794 fn test_on_disk_format_is_stable() {
795 use super::super::sqlite::{Database, db_file_name};
796
797 let dir = tempfile::tempdir().unwrap();
798 crate::environment::set_root(dir.path());
799 let namespace = Namespace::scoped("golden", "device0/matmul");
800
801 let mut cache = Store::<String, u32>::new(StoreOptions::new().storage(namespace));
802 cache.insert("shape=2x2".to_string(), 42).unwrap();
803
804 let expected_namespace =
805 std::format!("golden/{}/device0/matmul", env!("CARGO_PKG_VERSION"));
806 assert_eq!(cache.namespace().unwrap().as_str(), expected_namespace);
807
808 let path = dir.path().join(db_file_name(&crate::environment::active()));
809 assert!(path.exists(), "Database missing at {path:?}");
810
811 let database = Database::open(&path, true).unwrap();
814 let stored = database
815 .get(&expected_namespace, &encode(&"shape=2x2".to_string()))
816 .expect("Entry should be stored");
817 assert_eq!(decode::<u32>(&stored), Some(42));
818 }
819
820 #[test_log::test]
823 #[serial_test::serial]
824 #[cfg_attr(miri, ignore)]
825 fn test_entries_survive_reopen() {
826 let dir = tempfile::tempdir().unwrap();
827 crate::environment::set_root(dir.path());
828
829 let mut cache = Store::<String, u32>::new(eager("reopen"));
830 cache.insert("key".to_string(), 7).unwrap();
831 drop(cache);
832
833 let cache = Store::<String, u32>::new(eager("reopen"));
834 assert_eq!(cache.get(&"key".to_string()), Some(&7));
835 }
836
837 #[test_log::test]
839 #[serial_test::serial]
840 #[cfg_attr(miri, ignore)]
841 fn test_stores_are_isolated() {
842 let dir = tempfile::tempdir().unwrap();
843 crate::environment::set_root(dir.path());
844
845 let mut first = Store::<String, u32>::new(eager("device0/matmul"));
846 first.insert("key".to_string(), 1).unwrap();
847
848 let mut second = Store::<String, u32>::new(eager("device1/matmul"));
849 assert_eq!(second.get(&"key".to_string()), None);
850 second.insert("key".to_string(), 2).unwrap();
851
852 assert_eq!(first.get(&"key".to_string()), Some(&1));
853 assert_eq!(second.get(&"key".to_string()), Some(&2));
854 }
855
856 #[test_log::test]
857 #[serial_test::serial]
858 #[cfg_attr(miri, ignore)]
859 fn lazy_values_survive_reopen_and_load_lazily() {
860 let dir = tempfile::tempdir().unwrap();
861 crate::environment::set_root(dir.path());
862
863 let mut cache = Store::<String, Bytes>::new(lazy("ptx_sm90"));
864 cache
865 .insert(
866 "kernel_a".to_string(),
867 Bytes::from_bytes_vec(std::vec![1, 2, 3]),
868 )
869 .unwrap();
870 cache
871 .insert(
872 "kernel_b".to_string(),
873 Bytes::from_bytes_vec(std::vec![4, 5]),
874 )
875 .unwrap();
876 assert!(cache.is_empty());
878 drop(cache);
879
880 let mut cache = Store::<String, Bytes>::new(lazy("ptx_sm90"));
881 assert!(cache.is_empty());
883
884 assert_eq!(
885 cache.get_mut(&"kernel_a".to_string()).map(|v| v.to_vec()),
886 Some(std::vec![1, 2, 3])
887 );
888 assert_eq!(cache.len(), 1, "get_mut memoizes");
889 assert_eq!(
890 cache.remove(&"kernel_b".to_string()).map(|v| v.to_vec()),
891 Some(std::vec![4, 5])
892 );
893 assert_eq!(cache.len(), 1, "remove reads through without memoizing");
894 assert_eq!(cache.get_mut(&"missing".to_string()), None);
895 }
896
897 #[test_log::test]
898 #[serial_test::serial]
899 #[cfg_attr(miri, ignore)]
900 fn lazy_reinserting_a_different_value_errors() {
901 let dir = tempfile::tempdir().unwrap();
902 crate::environment::set_root(dir.path());
903
904 let mut cache = Store::<String, Bytes>::new(lazy("ptx_sm90"));
905 let kernel = |byte: u8| Bytes::from_bytes_vec(std::vec![byte]);
906 cache.insert("kernel".to_string(), kernel(1)).unwrap();
907
908 assert!(cache.insert("kernel".to_string(), kernel(1)).is_ok());
909 let error = cache.insert("kernel".to_string(), kernel(2));
910 assert!(matches!(error, Err(StoreError::DuplicatedKey { .. })));
911
912 assert!(cache.remove(&"kernel".to_string()).is_some());
915 let error = cache.insert("kernel".to_string(), kernel(2));
916 assert!(matches!(error, Err(StoreError::DuplicatedKey { .. })));
917 }
918
919 #[test_log::test]
923 #[serial_test::serial]
924 #[cfg_attr(miri, ignore)]
925 fn switching_environments_resets_bound_stores() {
926 let first = tempfile::tempdir().unwrap();
927 let second = tempfile::tempdir().unwrap();
928
929 crate::environment::set_root(first.path());
930 let mut store = Store::<String, u32>::new(eager("reset"));
931 store.insert("key".to_string(), 1).unwrap();
932 assert_eq!(store.get(&"key".to_string()), Some(&1));
933
934 crate::environment::set_root(second.path());
936 assert_eq!(store.get(&"key".to_string()), None);
937 assert_eq!(store.len(), 0);
938 assert!(store.pending_load());
939
940 store.insert("key".to_string(), 2).unwrap();
943 assert_eq!(store.get(&"key".to_string()), Some(&2));
944
945 crate::environment::set_root(first.path());
947 store.sync();
948 assert_eq!(store.get(&"key".to_string()), Some(&1));
949 }
950
951 #[test_log::test]
954 #[serial_test::serial]
955 #[cfg_attr(miri, ignore)]
956 fn unbound_stores_survive_environment_switches() {
957 let root = tempfile::tempdir().unwrap();
958
959 let mut store = Store::<String, u32>::new(StoreOptions::new());
960 store.insert("key".to_string(), 1).unwrap();
961
962 crate::environment::set_root(root.path());
963 assert_eq!(store.get(&"key".to_string()), Some(&1));
964 }
965
966 #[test_log::test]
969 #[serial_test::serial]
970 #[cfg_attr(miri, ignore)]
971 fn purge_key_deletes_one_entry_durably() {
972 let dir = tempfile::tempdir().unwrap();
973 crate::environment::set_root(dir.path());
974
975 let mut store = Store::<String, u32>::new(eager("purge_key"));
976 store.insert("gone".to_string(), 1).unwrap();
977 store.insert("kept".to_string(), 2).unwrap();
978
979 assert_eq!(store.purge_key(&"gone".to_string()), Some(1));
980 store.insert("gone".to_string(), 3).unwrap();
982 assert_eq!(store.purge_key(&"gone".to_string()), Some(3));
983 drop(store);
984
985 let store = Store::<String, u32>::new(eager("purge_key"));
986 assert_eq!(store.get(&"gone".to_string()), None);
987 assert_eq!(store.get(&"kept".to_string()), Some(&2));
988 }
989
990 #[test_log::test]
993 #[serial_test::serial]
994 #[cfg_attr(miri, ignore)]
995 fn clear_evicts_memory_but_not_the_storage() {
996 let dir = tempfile::tempdir().unwrap();
997 crate::environment::set_root(dir.path());
998
999 let mut store = Store::<String, u32>::new(eager("clear"));
1000 store.insert("key".to_string(), 1).unwrap();
1001
1002 store.clear();
1003 assert!(store.is_empty());
1004 assert!(matches!(
1007 store.insert("key".to_string(), 2),
1008 Err(StoreError::DuplicatedKey { .. })
1009 ));
1010
1011 store.sync();
1012 assert_eq!(store.get(&"key".to_string()), Some(&1));
1013 }
1014
1015 #[test_log::test]
1018 #[serial_test::serial]
1019 #[cfg_attr(miri, ignore)]
1020 fn purge_deletes_durably_and_frees_the_keys() {
1021 let dir = tempfile::tempdir().unwrap();
1022 crate::environment::set_root(dir.path());
1023
1024 let mut store = Store::<String, u32>::new(eager("purge"));
1025 store.insert("kept".to_string(), 1).unwrap();
1026 store.insert("gone".to_string(), 2).unwrap();
1027
1028 let mut other = Store::<String, u32>::new(eager("other"));
1030 other.insert("kept".to_string(), 9).unwrap();
1031
1032 store.purge();
1033 assert!(store.is_empty());
1034
1035 store.insert("kept".to_string(), 3).unwrap();
1037 drop(store);
1038
1039 let store = Store::<String, u32>::new(eager("purge"));
1040 assert_eq!(store.get(&"kept".to_string()), Some(&3));
1041 assert_eq!(store.get(&"gone".to_string()), None);
1042 assert_eq!(
1043 Store::<String, u32>::new(eager("other")).get(&"kept".to_string()),
1044 Some(&9)
1045 );
1046 }
1047
1048 #[test_log::test]
1051 #[serial_test::serial]
1052 #[cfg_attr(miri, ignore)]
1053 fn scan_visits_the_storage_without_retaining() {
1054 let dir = tempfile::tempdir().unwrap();
1055 crate::environment::set_root(dir.path());
1056
1057 let mut store = Store::<String, u32>::new(lazy("scan"));
1058 store.insert("a".to_string(), 1).unwrap();
1059 store.insert("b".to_string(), 2).unwrap();
1060
1061 let mut seen = std::vec::Vec::new();
1062 let complete = store.scan(|key, value| seen.push((key, value)));
1063 seen.sort();
1064
1065 assert!(complete, "a synchronous storage is scanned in full");
1066 assert_eq!(seen, std::vec![("a".to_string(), 1), ("b".to_string(), 2)]);
1067 assert!(store.is_empty(), "nothing stays resident after a scan");
1068 }
1069
1070 #[test]
1071 fn in_memory_store_needs_no_storage() {
1072 let mut store = Store::<String, u32>::new(StoreOptions::new());
1073
1074 store.insert("key".to_string(), 1).unwrap();
1075 assert_eq!(store.get(&"key".to_string()), Some(&1));
1076 assert!(store.insert("key".to_string(), 1).is_ok());
1077 assert!(matches!(
1078 store.insert("key".to_string(), 2),
1079 Err(StoreError::DuplicatedKey { .. })
1080 ));
1081
1082 assert_eq!(store.remove(&"key".to_string()), Some(1));
1085 store.insert("key".to_string(), 2).unwrap();
1086 assert_eq!(store.get(&"key".to_string()), Some(&2));
1087 }
1088}