Skip to main content

luct_store/
lru.rs

1use lru::LruCache;
2use luct_core::store::{
3    AppendableStore, AsyncStoreRead, AsyncStoreWrite, OrderedStoreRead, SearchableStoreRead, Store,
4    StoreBase, StoreRead, StoreWrite,
5};
6use std::{
7    cell::RefCell,
8    fmt::Debug,
9    hash::Hash,
10    ops::{Deref, DerefMut},
11};
12
13/// A [`Store`](luct_core::store::Store) implementation that wraps an inner [`Store`](luct_core::store::Store)
14/// and ads an LRU (least-recently-used) cache around it.
15///
16/// The cache is write-through, i.e. there is no speedup when writing to the store.
17/// Furthermore, the implementation is not [`Send`] or [`Sync`].
18/// A common patthern would be to have one [`LruCacheStore`] per thread wrapping an inner store.
19pub struct LruCacheStore<S>
20where
21    S: StoreBase,
22{
23    cache: RefCell<LruCache<S::Key, S::Value>>,
24    inner: S,
25}
26
27impl<S> Debug for LruCacheStore<S>
28where
29    S: StoreBase<Key: Debug, Value: Debug> + Debug,
30{
31    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32        f.debug_struct("LruCacheStore")
33            .field("inner", &self.inner)
34            .finish()
35    }
36}
37
38impl<S> Deref for LruCacheStore<S>
39where
40    S: StoreBase,
41{
42    type Target = S;
43
44    fn deref(&self) -> &Self::Target {
45        &self.inner
46    }
47}
48
49impl<S> DerefMut for LruCacheStore<S>
50where
51    S: Store,
52{
53    fn deref_mut(&mut self) -> &mut Self::Target {
54        &mut self.inner
55    }
56}
57
58impl<S> LruCacheStore<S>
59where
60    S: StoreBase<Key: Hash + Eq>,
61{
62    pub fn new(store: S, caps: usize) -> Self {
63        Self {
64            cache: RefCell::new(LruCache::new(caps.try_into().unwrap())),
65            inner: store,
66        }
67    }
68}
69
70impl<S> StoreBase for LruCacheStore<S>
71where
72    S: StoreBase,
73{
74    type Key = S::Key;
75    type Value = S::Value;
76}
77
78impl<S> StoreRead for LruCacheStore<S>
79where
80    S: StoreRead<Key: Clone + Hash + Eq, Value: Clone>,
81{
82    fn get(&self, key: &Self::Key) -> Option<Self::Value> {
83        if let Some(val) = self.cache.borrow_mut().get(key) {
84            Some(val.clone())
85        } else {
86            let val = self.inner.get(key)?;
87            self.cache.borrow_mut().put(key.clone(), val.clone());
88            Some(val)
89        }
90    }
91
92    fn len(&self) -> usize {
93        self.inner.len()
94    }
95}
96
97impl<S> StoreWrite for LruCacheStore<S>
98where
99    S: StoreWrite<Key: Hash + Eq>,
100{
101    fn insert(&self, key: Self::Key, value: Self::Value) {
102        self.cache.borrow_mut().pop(&key);
103        self.inner.insert(key, value);
104    }
105
106    fn delete(&self, key: &Self::Key) -> bool {
107        let contained = self.inner.delete(key);
108        self.cache.borrow_mut().pop(key);
109        contained
110    }
111}
112
113impl<S> OrderedStoreRead for LruCacheStore<S>
114where
115    S: OrderedStoreRead<Key: Clone + Hash, Value: Clone>,
116{
117    fn last(&self) -> Option<(Self::Key, Self::Value)> {
118        self.inner.last()
119    }
120}
121
122impl<S> AppendableStore for LruCacheStore<S>
123where
124    S: AppendableStore<Key: Clone + Hash, Value: Clone>,
125{
126    fn append(&self, value: Self::Value) -> Self::Key {
127        self.inner.append(value)
128    }
129}
130
131impl<S> SearchableStoreRead for LruCacheStore<S>
132where
133    S: SearchableStoreRead<Key: Clone + Hash, Value: Clone>,
134{
135    fn filter(
136        &self,
137        pred: impl FnMut(&Self::Key, &Self::Value) -> bool,
138    ) -> Vec<(Self::Key, Self::Value)> {
139        self.inner.filter(pred)
140    }
141
142    fn find(
143        &self,
144        pred: impl FnMut(&Self::Key, &Self::Value) -> bool,
145    ) -> Option<(Self::Key, Self::Value)> {
146        self.inner.find(pred)
147    }
148}
149
150impl<S> AsyncStoreRead for LruCacheStore<S>
151where
152    S: AsyncStoreRead<Key: Clone + Hash + Eq, Value: Clone>,
153{
154    async fn get(&self, key: Self::Key) -> Option<Self::Value> {
155        if let Some(val) = self.cache.borrow_mut().get(&key) {
156            Some(val.clone())
157        } else {
158            let val = self.inner.get(key.clone()).await?;
159            self.cache.borrow_mut().put(key, val.clone());
160            Some(val)
161        }
162    }
163
164    async fn len(&self) -> usize {
165        self.inner.len().await
166    }
167}
168
169impl<S> AsyncStoreWrite for LruCacheStore<S>
170where
171    S: AsyncStoreWrite<Key: Clone + Hash + Eq, Value: Clone>,
172{
173    async fn insert(&self, key: Self::Key, value: Self::Value) {
174        self.cache.borrow_mut().pop(&key);
175        self.inner.insert(key, value).await
176    }
177}
178
179#[cfg(test)]
180mod tests {
181    use super::*;
182    use luct_core::store::MemoryStore;
183    use luct_test::store::{ordered_store_test, searchable_store_test, store_test};
184
185    #[test]
186    fn lru_cache_store() {
187        let store = LruCacheStore::new(MemoryStore::<u64, String>::default(), 1000);
188        store_test(store);
189    }
190
191    #[test]
192    fn lru_cache_ordered_store() {
193        let store = LruCacheStore::new(MemoryStore::<u64, String>::default(), 1000);
194        ordered_store_test(store);
195    }
196
197    #[test]
198    fn lru_cache_searchable_store() {
199        let store = LruCacheStore::new(MemoryStore::<u64, String>::default(), 1000);
200        searchable_store_test(store);
201    }
202}