Skip to main content

luct_store/
lru.rs

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