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, 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)
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(), value);
292 }
293
294 self.entries.get_mut(key)
295 }
296
297 pub fn remove(&mut self, key: &K) -> Option<V> {
306 self.reset_if_stale();
307
308 let value = match self.entries.remove(key) {
309 Some(value) => Some(value),
310 None => match self.cache {
311 CacheOption::Eager => None,
312 CacheOption::Lazy => self.fetch(key),
313 },
314 };
315
316 if value.is_some() && self.storage.is_some() {
319 self.known.insert(key.clone());
320 }
321
322 value
323 }
324
325 pub fn insert(&mut self, key: K, value: V) -> Result<(), StoreError<K, V>> {
338 self.reset_if_stale();
339
340 let known = match self.entries.get(&key) {
341 Some(existing) if existing == &value => return Ok(()),
342 existing => existing.is_some() || self.known.contains(&key),
343 };
344
345 let Some(storage) = self.storage.as_deref() else {
346 return match self.entries.get(&key) {
347 Some(existing) => Err(StoreError::DuplicatedKey {
348 value_previous: existing.clone(),
349 value_updated: value,
350 key,
351 }),
352 None => {
353 self.entries.insert(key, value);
354 Ok(())
355 }
356 };
357 };
358
359 match write_through(storage, &key, &value) {
363 Written::Stored => {
364 self.record(key, value);
365 Ok(())
366 }
367 Written::Failed(error) => Err(StoreError::Backend { key, error }),
368 Written::Conflict(existing) => {
369 if matches!(self.cache, CacheOption::Eager) {
372 self.entries.insert(key.clone(), existing.clone());
373 } else {
374 self.known.insert(key.clone());
375 }
376
377 let (value_previous, value_updated) = (existing, value);
381 Err(if known {
382 StoreError::DuplicatedKey {
383 key,
384 value_previous,
385 value_updated,
386 }
387 } else {
388 StoreError::KeyOutOfSync {
389 key,
390 value_previous,
391 value_updated,
392 }
393 })
394 }
395 }
396 }
397
398 pub fn purge_key(&mut self, key: &K) -> Option<V> {
405 self.reset_if_stale();
406
407 let value = match self.entries.remove(key) {
408 Some(value) => Some(value),
409 None => match self.cache {
410 CacheOption::Eager => None,
411 CacheOption::Lazy => self.fetch(key),
412 },
413 };
414
415 self.known.remove(key);
418 if let Some(storage) = self.storage.as_deref() {
419 storage.purge_key(&encode(key));
420 }
421
422 value
423 }
424
425 pub fn clear(&mut self) {
432 self.reset_if_stale();
433
434 if self.storage.is_some() {
435 self.known.extend(self.entries.drain().map(|(key, _)| key));
436 } else {
437 self.entries.clear();
438 }
439 }
440
441 pub fn purge(&mut self) {
449 self.reset_if_stale();
450
451 self.entries.clear();
452 self.known.clear();
453
454 if let Some(storage) = self.storage.as_deref() {
455 storage.purge();
456 }
457 }
458
459 #[cfg_attr(
465 feature = "tracing",
466 tracing::instrument(level = "trace", skip_all, fields(namespace = ?self.namespace))
467 )]
468 pub fn sync(&mut self) {
469 if self.reset_if_stale() && matches!(self.cache, CacheOption::Eager) {
471 return;
472 }
473 self.ingest();
474 }
475
476 fn ingest(&mut self) {
478 let Some(storage) = self.storage.as_deref() else {
479 return;
480 };
481 let entries = &mut self.entries;
482
483 storage.scan(&mut |key, value| {
484 if let Some((key, value)) = decode_entry::<K, V>(key, value) {
485 entries.insert(key, value);
486 }
487 });
488 }
489
490 pub fn scan<F: FnMut(K, V)>(&mut self, mut func: F) {
499 self.reset_if_stale();
500
501 let Some(storage) = self.storage.as_deref() else {
502 for (key, value) in self.entries.iter() {
503 func(key.clone(), value.clone());
504 }
505 return;
506 };
507
508 storage.scan(&mut |key, value| {
509 if let Some((key, value)) = decode_entry::<K, V>(key, value) {
510 func(key, value);
511 }
512 });
513 }
514
515 pub fn for_each<F: FnMut(&K, &V)>(&self, mut func: F) {
517 if self.stale() {
518 return;
519 }
520
521 for (key, value) in self.entries.iter() {
522 func(key, value);
523 }
524 }
525
526 pub fn len(&self) -> usize {
528 if self.stale() {
529 return 0;
530 }
531
532 self.entries.len()
533 }
534
535 pub fn is_empty(&self) -> bool {
537 self.len() == 0
538 }
539
540 fn fetch(&self, key: &K) -> Option<V> {
542 let bytes = self.storage.as_deref()?.get(&encode(key))?;
543 decode::<V>(&bytes)
544 }
545
546 fn record(&mut self, key: K, value: V) {
548 match self.cache {
549 CacheOption::Eager => {
550 self.entries.insert(key, value);
551 }
552 CacheOption::Lazy => {
555 self.known.insert(key);
556 }
557 }
558 }
559
560 fn stale(&self) -> bool {
564 match self.generation {
565 Some(generation) => generation != crate::environment::generation(),
566 None => false,
567 }
568 }
569
570 fn reset_if_stale(&mut self) -> bool {
574 if !self.stale() {
575 return false;
576 }
577
578 let (Some(namespace), Some(_)) = (&self.namespace, self.generation) else {
580 return false;
581 };
582
583 log::debug!("Environment switched, resetting the store for {namespace}");
584
585 self.generation = Some(crate::environment::generation());
588 self.storage = Some(super::storage::open(namespace.as_str()));
589 self.entries.clear();
590 self.known.clear();
591 if matches!(self.cache, CacheOption::Eager) {
592 self.ingest();
593 }
594
595 true
596 }
597}
598
599impl<K, V> core::fmt::Debug for Store<K, V> {
600 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
601 f.debug_struct("Store")
602 .field("namespace", &self.namespace)
603 .field("cache", &self.cache)
604 .field("entries", &self.entries.len())
605 .field("known", &self.known.len())
606 .field("storage", &self.storage)
607 .finish()
608 }
609}
610
611impl<K: StoreKey, V: StoreValue> Display for Store<K, V> {
612 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
613 match (&self.namespace, &self.storage) {
614 (Some(namespace), Some(storage)) => write!(
615 f,
616 "{namespace} ({} entries in {})",
617 self.len(),
618 storage.describe()
619 ),
620 _ => write!(f, "in-memory ({} entries)", self.len()),
621 }
622 }
623}
624
625pub(crate) enum Written<V> {
627 Stored,
629 Conflict(V),
631 Failed(String),
633}
634
635pub(crate) fn write_through<K: StoreKey, V: StoreValue>(
642 storage: &dyn Storage,
643 key: &K,
644 value: &V,
645) -> Written<V> {
646 let key_bytes = encode(key);
647
648 match storage.insert(&key_bytes, encode(value), Origin::Local) {
649 Insertion::Stored => Written::Stored,
650 Insertion::Failed(error) => Written::Failed(error),
651 Insertion::Conflict(existing) => match decode::<V>(&existing) {
652 Some(existing) if &existing != value => Written::Conflict(existing),
653 Some(_) => Written::Stored,
654 None => match storage.replace(&key_bytes, encode(value), Origin::Local) {
659 Insertion::Failed(error) => Written::Failed(error),
660 _ => Written::Stored,
661 },
662 },
663 }
664}
665
666pub(crate) fn encode<T: Serialize>(value: &T) -> Bytes {
676 let mut bytes = Vec::new();
677 ciborium::ser::into_writer(value, &mut bytes).expect("Can serialize data");
678 Bytes::from_bytes_vec(bytes)
679}
680
681pub(crate) fn decode<T: DeserializeOwned>(bytes: &[u8]) -> Option<T> {
684 match ciborium::de::from_reader(bytes) {
685 Ok(value) => Some(value),
686 Err(err) => {
687 log::warn!("Corrupted cache entry, ignoring it: {err}");
688 None
689 }
690 }
691}
692
693fn decode_entry<K: StoreKey, V: StoreValue>(key: &[u8], value: &[u8]) -> Option<(K, V)> {
694 Some((decode::<K>(key)?, decode::<V>(value)?))
695}
696
697#[cfg(all(test, native_cache))]
698mod tests {
699 use std::string::ToString;
700 use std::vec;
701
702 use super::*;
703
704 fn eager(path: &str) -> StoreOptions {
705 StoreOptions::new().storage(Namespace::new(path))
706 }
707
708 fn lazy(path: &str) -> StoreOptions {
709 eager(path).cache(CacheOption::Lazy)
710 }
711
712 #[test_log::test]
713 #[serial_test::serial]
714 #[cfg_attr(miri, ignore)]
715 fn test_cache_simple() {
716 let dir = tempfile::tempdir().unwrap();
717 crate::environment::set_root(dir.path());
718
719 let key1 = || "key1".to_string();
720 let key2 = || "key2".to_string();
721
722 let value1 = || "value1".to_string();
723 let value2 = || "value2".to_string();
724
725 let mut cache = Store::<String, String>::new(eager("test"));
726 cache.insert(key1(), value1()).unwrap();
727 cache.insert(key2(), value2()).unwrap();
728
729 let result = cache.insert(key1(), value2());
730 assert!(
731 result.is_err(),
732 "Can't reinsert the same key with a different value."
733 );
734
735 assert_eq!(cache.len(), 2);
736
737 let value1_actual = cache.get(&key1()).unwrap();
738 assert_eq!(value1_actual, &value1());
739
740 let value2_actual = cache.get(&key2()).unwrap();
741 assert_eq!(value2_actual, &value2());
742 }
743
744 #[test_log::test]
748 #[serial_test::serial]
749 #[cfg_attr(miri, ignore)]
750 fn test_on_disk_format_is_stable() {
751 let dir = tempfile::tempdir().unwrap();
752 crate::environment::set_root(dir.path());
753 let namespace = Namespace::scoped("golden", "device0/matmul");
754
755 let mut cache = Store::<String, u32>::new(StoreOptions::new().storage(namespace));
756 cache.insert("shape=2x2".to_string(), 42).unwrap();
757
758 let expected_namespace =
759 std::format!("golden/{}/device0/matmul", env!("CARGO_PKG_VERSION"));
760 assert_eq!(cache.namespace().unwrap().as_str(), expected_namespace);
761
762 let path = crate::environment::path();
763 assert!(path.exists(), "Database missing at {path:?}");
764
765 let reopened = Store::<String, u32>::new(
768 StoreOptions::new().storage(Namespace::scoped("golden", "device0/matmul")),
769 );
770 assert_eq!(reopened.get(&"shape=2x2".to_string()), Some(&42));
771 }
772
773 #[test_log::test]
776 #[serial_test::serial]
777 #[cfg_attr(miri, ignore)]
778 fn test_entries_survive_reopen() {
779 let dir = tempfile::tempdir().unwrap();
780 crate::environment::set_root(dir.path());
781
782 let mut cache = Store::<String, u32>::new(eager("reopen"));
783 cache.insert("key".to_string(), 7).unwrap();
784 drop(cache);
785
786 let cache = Store::<String, u32>::new(eager("reopen"));
787 assert_eq!(cache.get(&"key".to_string()), Some(&7));
788 }
789
790 #[test_log::test]
792 #[serial_test::serial]
793 #[cfg_attr(miri, ignore)]
794 fn test_stores_are_isolated() {
795 let dir = tempfile::tempdir().unwrap();
796 crate::environment::set_root(dir.path());
797
798 let mut first = Store::<String, u32>::new(eager("device0/matmul"));
799 first.insert("key".to_string(), 1).unwrap();
800
801 let mut second = Store::<String, u32>::new(eager("device1/matmul"));
802 assert_eq!(second.get(&"key".to_string()), None);
803 second.insert("key".to_string(), 2).unwrap();
804
805 assert_eq!(first.get(&"key".to_string()), Some(&1));
806 assert_eq!(second.get(&"key".to_string()), Some(&2));
807 }
808
809 #[test_log::test]
810 #[serial_test::serial]
811 #[cfg_attr(miri, ignore)]
812 fn lazy_values_survive_reopen_and_load_lazily() {
813 let dir = tempfile::tempdir().unwrap();
814 crate::environment::set_root(dir.path());
815
816 let mut cache = Store::<String, Bytes>::new(lazy("ptx_sm90"));
817 cache
818 .insert(
819 "kernel_a".to_string(),
820 Bytes::from_bytes_vec(std::vec![1, 2, 3]),
821 )
822 .unwrap();
823 cache
824 .insert(
825 "kernel_b".to_string(),
826 Bytes::from_bytes_vec(std::vec![4, 5]),
827 )
828 .unwrap();
829 assert!(cache.is_empty());
831 drop(cache);
832
833 let mut cache = Store::<String, Bytes>::new(lazy("ptx_sm90"));
834 assert!(cache.is_empty());
836
837 assert_eq!(
838 cache.get_mut(&"kernel_a".to_string()).map(|v| v.to_vec()),
839 Some(std::vec![1, 2, 3])
840 );
841 assert_eq!(cache.len(), 1, "get_mut memoizes");
842 assert_eq!(
843 cache.remove(&"kernel_b".to_string()).map(|v| v.to_vec()),
844 Some(std::vec![4, 5])
845 );
846 assert_eq!(cache.len(), 1, "remove reads through without memoizing");
847 assert_eq!(cache.get_mut(&"missing".to_string()), None);
848 }
849
850 #[test_log::test]
851 #[serial_test::serial]
852 #[cfg_attr(miri, ignore)]
853 fn lazy_reinserting_a_different_value_errors() {
854 let dir = tempfile::tempdir().unwrap();
855 crate::environment::set_root(dir.path());
856
857 let mut cache = Store::<String, Bytes>::new(lazy("ptx_sm90"));
858 let kernel = |byte: u8| Bytes::from_bytes_vec(std::vec![byte]);
859 cache.insert("kernel".to_string(), kernel(1)).unwrap();
860
861 assert!(cache.insert("kernel".to_string(), kernel(1)).is_ok());
862 let error = cache.insert("kernel".to_string(), kernel(2));
863 assert!(matches!(error, Err(StoreError::DuplicatedKey { .. })));
864
865 assert!(cache.remove(&"kernel".to_string()).is_some());
868 let error = cache.insert("kernel".to_string(), kernel(2));
869 assert!(matches!(error, Err(StoreError::DuplicatedKey { .. })));
870 }
871
872 #[test_log::test]
876 #[serial_test::serial]
877 #[cfg_attr(miri, ignore)]
878 fn switching_environments_resets_bound_stores() {
879 let first = tempfile::tempdir().unwrap();
880 let second = tempfile::tempdir().unwrap();
881
882 crate::environment::set_root(first.path());
883 let mut store = Store::<String, u32>::new(eager("reset"));
884 store.insert("key".to_string(), 1).unwrap();
885 assert_eq!(store.get(&"key".to_string()), Some(&1));
886
887 crate::environment::set_root(second.path());
889 assert_eq!(store.get(&"key".to_string()), None);
890 assert_eq!(store.len(), 0);
891
892 store.insert("key".to_string(), 2).unwrap();
895 assert_eq!(store.get(&"key".to_string()), Some(&2));
896
897 crate::environment::set_root(first.path());
899 store.sync();
900 assert_eq!(store.get(&"key".to_string()), Some(&1));
901 }
902
903 #[test_log::test]
906 #[serial_test::serial]
907 #[cfg_attr(miri, ignore)]
908 fn unbound_stores_survive_environment_switches() {
909 let root = tempfile::tempdir().unwrap();
910
911 let mut store = Store::<String, u32>::new(StoreOptions::new());
912 store.insert("key".to_string(), 1).unwrap();
913
914 crate::environment::set_root(root.path());
915 assert_eq!(store.get(&"key".to_string()), Some(&1));
916 }
917
918 #[test_log::test]
921 #[serial_test::serial]
922 #[cfg_attr(miri, ignore)]
923 fn purge_key_deletes_one_entry_durably() {
924 let dir = tempfile::tempdir().unwrap();
925 crate::environment::set_root(dir.path());
926
927 let mut store = Store::<String, u32>::new(eager("purge_key"));
928 store.insert("gone".to_string(), 1).unwrap();
929 store.insert("kept".to_string(), 2).unwrap();
930
931 assert_eq!(store.purge_key(&"gone".to_string()), Some(1));
932 store.insert("gone".to_string(), 3).unwrap();
934 assert_eq!(store.purge_key(&"gone".to_string()), Some(3));
935 drop(store);
936
937 let store = Store::<String, u32>::new(eager("purge_key"));
938 assert_eq!(store.get(&"gone".to_string()), None);
939 assert_eq!(store.get(&"kept".to_string()), Some(&2));
940 }
941
942 #[test_log::test]
945 #[serial_test::serial]
946 #[cfg_attr(miri, ignore)]
947 fn clear_evicts_memory_but_not_the_storage() {
948 let dir = tempfile::tempdir().unwrap();
949 crate::environment::set_root(dir.path());
950
951 let mut store = Store::<String, u32>::new(eager("clear"));
952 store.insert("key".to_string(), 1).unwrap();
953
954 store.clear();
955 assert!(store.is_empty());
956 assert!(matches!(
959 store.insert("key".to_string(), 2),
960 Err(StoreError::DuplicatedKey { .. })
961 ));
962
963 store.sync();
964 assert_eq!(store.get(&"key".to_string()), Some(&1));
965 }
966
967 #[test_log::test]
970 #[serial_test::serial]
971 #[cfg_attr(miri, ignore)]
972 fn purge_deletes_durably_and_frees_the_keys() {
973 let dir = tempfile::tempdir().unwrap();
974 crate::environment::set_root(dir.path());
975
976 let mut store = Store::<String, u32>::new(eager("purge"));
977 store.insert("kept".to_string(), 1).unwrap();
978 store.insert("gone".to_string(), 2).unwrap();
979
980 let mut other = Store::<String, u32>::new(eager("other"));
982 other.insert("kept".to_string(), 9).unwrap();
983
984 store.purge();
985 assert!(store.is_empty());
986
987 store.insert("kept".to_string(), 3).unwrap();
989 drop(store);
990
991 let store = Store::<String, u32>::new(eager("purge"));
992 assert_eq!(store.get(&"kept".to_string()), Some(&3));
993 assert_eq!(store.get(&"gone".to_string()), None);
994 assert_eq!(
995 Store::<String, u32>::new(eager("other")).get(&"kept".to_string()),
996 Some(&9)
997 );
998 }
999
1000 #[test_log::test]
1003 #[serial_test::serial]
1004 #[cfg_attr(miri, ignore)]
1005 fn scan_visits_the_storage_without_retaining() {
1006 let dir = tempfile::tempdir().unwrap();
1007 crate::environment::set_root(dir.path());
1008
1009 let mut store = Store::<String, u32>::new(lazy("scan"));
1010 store.insert("a".to_string(), 1).unwrap();
1011 store.insert("b".to_string(), 2).unwrap();
1012
1013 let mut seen = std::vec::Vec::new();
1014 store.scan(|key, value| seen.push((key, value)));
1015 seen.sort();
1016
1017 assert_eq!(seen, std::vec![("a".to_string(), 1), ("b".to_string(), 2)]);
1018 assert!(store.is_empty(), "nothing stays resident after a scan");
1019 }
1020
1021 #[test]
1022 fn in_memory_store_needs_no_storage() {
1023 let mut store = Store::<String, u32>::new(StoreOptions::new());
1024
1025 store.insert("key".to_string(), 1).unwrap();
1026 assert_eq!(store.get(&"key".to_string()), Some(&1));
1027 assert!(store.insert("key".to_string(), 1).is_ok());
1028 assert!(matches!(
1029 store.insert("key".to_string(), 2),
1030 Err(StoreError::DuplicatedKey { .. })
1031 ));
1032
1033 assert_eq!(store.remove(&"key".to_string()), Some(1));
1036 store.insert("key".to_string(), 2).unwrap();
1037 assert_eq!(store.get(&"key".to_string()), Some(&2));
1038 }
1039}