engine/store/storage/cache.rs
1// Copyright 2020-2021 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::store::storage::Value;
5
6use serde::{Deserialize, Serialize};
7
8use std::{
9 collections::{hash_map::Entry, HashMap},
10 fmt::Debug,
11 hash::Hash,
12 time::{Duration, SystemTime},
13};
14
15/// The [`Cache`] struct used to store the data in an ordered format.
16// #[deprecated(note = "use [`stronghold_iota_new::client_new::types::cache::Cache")]
17#[derive(Serialize, Deserialize, Clone, Debug)]
18pub struct Cache<K, V>
19where
20 K: Hash + Eq + Clone,
21 V: Clone + Debug,
22{
23 // hashmap of data.
24 table: HashMap<K, Value<V>>,
25 // the scan frequency for removing data based on the expiration time.
26 scan_freq: Option<Duration>,
27 // a created at timestamp.
28 created_at: SystemTime,
29 // a last scan timestamp.
30 last_scan_at: Option<SystemTime>,
31}
32
33impl<K: Hash + Eq + Clone, V: Clone + Debug> Cache<K, V> {
34 /// creates a new empty [`Cache`]
35 /// # Example
36 /// ```
37 /// use engine::store::Cache;
38 /// use std::time::Duration;
39 ///
40 /// let mut cache = Cache::new();
41 ///
42 /// let key: Vec<u8> = b"key".to_vec();
43 /// let value: Vec<u8> = b"value".to_vec();
44 ///
45 /// cache.insert(key.clone(), value.clone(), None);
46 ///
47 /// assert_eq!(cache.get(&key), Some(&value))
48 /// ```
49 pub fn new() -> Self {
50 Self {
51 table: HashMap::new(),
52 scan_freq: None,
53 created_at: SystemTime::now(),
54 last_scan_at: None,
55 }
56 }
57
58 /// creates an empty [`Cache`] with a periodic scanner which identifies expired entries.
59 ///
60 /// # Example
61 /// ```
62 /// use engine::store::Cache;
63 /// use std::time::Duration;
64 ///
65 /// let scan_freq = Duration::from_secs(60);
66 ///
67 /// let mut cache = Cache::create_with_scanner(scan_freq);
68 ///
69 /// let key: &'static str = "key";
70 /// let value: &'static str = "value";
71 ///
72 /// cache.insert(key, value, None);
73 ///
74 /// assert_eq!(cache.get(&key), Some(&value));
75 /// ```
76 pub fn create_with_scanner(scan_freq: Duration) -> Self {
77 Self {
78 table: HashMap::new(),
79 scan_freq: Some(scan_freq),
80 created_at: SystemTime::now(),
81 last_scan_at: None,
82 }
83 }
84
85 /// Gets the value associated with the specified key.
86 ///
87 /// # Example
88 /// ```
89 /// use engine::store::Cache;
90 /// use std::time::Duration;
91 ///
92 /// let mut cache = Cache::new();
93 ///
94 /// let key: &'static str = "key";
95 /// let value: &'static str = "value";
96 ///
97 /// cache.insert(key, value, None);
98 ///
99 /// assert_eq!(cache.get(&key), Some(&value))
100 /// ```
101 pub fn get(&self, key: &K) -> Option<&V> {
102 let now = SystemTime::now();
103
104 self.table
105 .get(key)
106 .filter(|value| !value.has_expired(now))
107 .map(|value| &value.val)
108 }
109
110 /// Gets the value associated with the specified key. If the key could not be found in the [`Cache`], creates and
111 /// inserts the value using a specified `func` function. # Example
112 /// ```
113 /// use engine::store::Cache;
114 /// use std::time::Duration;
115 ///
116 /// let mut cache = Cache::new();
117 ///
118 /// let key = "key";
119 /// let value = "value";
120 ///
121 /// assert_eq!(cache.get_or_insert(key, move || value, None), &value);
122 /// assert!(cache.contains_key(&key));
123 /// ```
124 pub fn get_or_insert<F>(&mut self, key: K, func: F, lifetime: Option<Duration>) -> &V
125 where
126 F: Fn() -> V,
127 {
128 let now = SystemTime::now();
129
130 self.try_remove_expired_items(now);
131
132 match self.table.entry(key) {
133 Entry::Occupied(mut occ) => {
134 if occ.get().has_expired(now) {
135 occ.insert(Value::new(func(), lifetime));
136 }
137
138 &occ.into_mut().val
139 }
140 Entry::Vacant(vac) => &vac.insert(Value::new(func(), lifetime)).val,
141 }
142 }
143
144 /// Insert a key-value pair into the cache.
145 /// If key was not present, a [`None`] is returned, else the value is updated and the old value is returned.
146 ///
147 /// # Example
148 /// ```
149 /// use engine::store::Cache;
150 /// use std::time::Duration;
151 ///
152 /// let mut cache = Cache::new();
153 ///
154 /// let key: &'static str = "key";
155 /// let value: &'static str = "value";
156 ///
157 /// let insert = cache.insert(key, value, None);
158 ///
159 /// assert_eq!(cache.get(&key), Some(&value));
160 /// assert!(insert.is_none());
161 /// ```
162 pub fn insert(&mut self, key: K, value: V, lifetime: Option<Duration>) -> Option<V> {
163 let now = SystemTime::now();
164
165 self.try_remove_expired_items(now);
166
167 self.table
168 .insert(key, Value::new(value, lifetime))
169 .filter(|value| !value.has_expired(now))
170 .map(|value| value.val)
171 }
172
173 /// Removes a key from the cache. Returns the value from the key if the key existed in the cache.
174 ///
175 /// # Example
176 ///
177 /// ```
178 /// use engine::store::Cache;
179 /// use std::time::Duration;
180 ///
181 /// let mut cache = Cache::new();
182 ///
183 /// let key: &'static str = "key";
184 /// let value: &'static str = "value";
185 ///
186 /// let insert = cache.insert(key, value, None);
187 /// assert_eq!(cache.remove(&key), Some(value));
188 /// assert!(!cache.contains_key(&key));
189 /// ```
190 pub fn remove(&mut self, key: &K) -> Option<V> {
191 let now = SystemTime::now();
192
193 self.try_remove_expired_items(now);
194
195 self.table
196 .remove(key)
197 .filter(|value| !value.has_expired(now))
198 .map(|value| value.val)
199 }
200
201 // Check if the [`Cache<K, V>`] contains a specific key.
202 pub fn contains_key(&self, key: &K) -> bool {
203 let now = SystemTime::now();
204
205 self.table.get(key).filter(|value| !value.has_expired(now)).is_some()
206 }
207
208 // Get the last scanned at time.
209 pub fn get_last_scanned_at(&self) -> Option<SystemTime> {
210 self.last_scan_at
211 }
212
213 /// Get the cache's scan frequency.
214 ///
215 /// # Example
216 /// ```
217 /// use engine::store::Cache;
218 /// use std::time::Duration;
219 ///
220 /// let scan_freq = Duration::from_secs(60);
221 ///
222 /// let mut cache = Cache::create_with_scanner(scan_freq);
223 ///
224 /// let key: &'static str = "key";
225 /// let value: &'static str = "value";
226 ///
227 /// cache.insert(key, value, None);
228 ///
229 /// assert_eq!(cache.get_scan_freq(), Some(scan_freq));
230 /// ```
231 pub fn get_scan_freq(&self) -> Option<Duration> {
232 self.scan_freq
233 }
234
235 /// Clear the stored cache and reset.
236 pub fn clear(&mut self) {
237 self.table.clear();
238 self.scan_freq = None;
239 self.created_at = SystemTime::now();
240 self.last_scan_at = None;
241 }
242
243 /// Returns a list of all keys inside the [`Cache`] as references
244 pub fn keys(&self) -> Vec<K> {
245 self.table.keys().cloned().collect()
246 }
247
248 /// attempts to remove expired items based on the current system time provided.
249 fn try_remove_expired_items(&mut self, now: SystemTime) {
250 if let Some(frequency) = self.scan_freq {
251 let since = now
252 .duration_since(self.last_scan_at.unwrap_or(self.created_at))
253 .expect("System time is before the scanned time");
254
255 if since >= frequency {
256 self.table.retain(|_, value| !value.has_expired(now));
257
258 self.last_scan_at = Some(now)
259 }
260 }
261 }
262}
263
264/// Default implementation for [`Cache<K, V>`]
265impl<K: Hash + Eq + Clone, V: Clone + Debug> Default for Cache<K, V> {
266 fn default() -> Self {
267 Cache::new()
268 }
269}