Skip to main content

actix_cloud/memorydb/
default.rs

1use std::{
2    cmp::{max, Reverse},
3    collections::HashMap,
4    sync::Arc,
5    time::Duration,
6};
7
8use anyhow::bail;
9use async_trait::async_trait;
10use chrono::Utc;
11use glob::Pattern;
12use parking_lot::{RwLock, RwLockWriteGuard};
13use priority_queue::PriorityQueue;
14
15use super::interface::MemoryDB;
16use crate::Result;
17
18/// Internal entry: `(value, absolute expiration timestamp in ms)`. `None` means no TTL.
19struct Data(String, Option<i64>);
20
21impl Data {
22    fn now() -> i64 {
23        Utc::now().timestamp_millis()
24    }
25
26    fn parse_ttl(ttl: Option<i64>) -> Option<i64> {
27        ttl.map(|x| Self::now().saturating_add(x))
28    }
29
30    fn new<S>(value: S, ttl: Option<i64>) -> Self
31    where
32        S: Into<String>,
33    {
34        Self(value.into(), Self::parse_ttl(ttl))
35    }
36
37    fn set_ttl(&mut self, ttl: Option<i64>) {
38        self.1 = Self::parse_ttl(ttl);
39    }
40
41    fn get_ttl(&self) -> Option<i64> {
42        self.1.map(|x| x.saturating_sub(Self::now()))
43    }
44
45    fn valid(&self) -> bool {
46        if let Some(x) = self.1 {
47            x > Self::now()
48        } else {
49            true
50        }
51    }
52}
53
54/// Built-in in-process memory database backend.
55///
56/// Keys and values live in a `HashMap` guarded by an `RwLock`; expired entries are
57/// removed lazily on access, plus eagerly during capacity-driven GC.
58///
59/// # Warning
60/// The default backend is only for demo purpose. The performance is not guaranteed.
61/// Use [`RedisBackend`](super::redis::RedisBackend) or your own [`MemoryDB`]
62/// implementation for production workloads.
63///
64/// When `capacity` is set and the database is full, the eviction policy is:
65/// - Evict 10% keys at a time (at least 1).
66/// - Expired keys go first, then keys having TTL attribute, sorted by TTL from shortest to longest.
67/// - If no keys are evicted, an error will be returned.
68#[derive(Clone)]
69pub struct DefaultBackend {
70    data: Arc<RwLock<HashMap<String, Data>>>,
71    capacity: Option<usize>,
72}
73
74impl DefaultBackend {
75    /// Create a new backend.
76    ///
77    /// `capacity` limits the maximum number of entries (`None` means unbounded —
78    /// be aware that this allows memory exhaustion).
79    pub fn new(capacity: Option<usize>) -> Self {
80        Self {
81            data: Default::default(),
82            capacity,
83        }
84    }
85
86    /// Evict `num` keys from memory. Return evicted number.
87    ///
88    /// - Evict any expired keys (`x`).
89    /// - If `x < num`, evict at most `num-x` keys sorted by TTL.
90    fn gc(&self, wlock: &mut RwLockWriteGuard<HashMap<String, Data>>, num: usize) -> usize {
91        let mut queue = PriorityQueue::new();
92        let mut delete = Vec::new();
93        for (k, v) in wlock.iter() {
94            if !v.valid() {
95                delete.push(k.to_owned());
96            } else if let Some(x) = v.1 {
97                queue.push(k.to_owned(), Reverse(x));
98            }
99        }
100        for i in &delete {
101            wlock.remove(i);
102        }
103        let mut ret = delete.len();
104        if ret < num {
105            let remain = num - ret;
106            for _ in 0..remain {
107                if let Some(k) = queue.pop() {
108                    wlock.remove(&k.0);
109                    ret += 1;
110                } else {
111                    return ret;
112                }
113            }
114        }
115        ret
116    }
117}
118
119impl Default for DefaultBackend {
120    fn default() -> Self {
121        Self::new(None)
122    }
123}
124
125#[async_trait]
126impl MemoryDB for DefaultBackend {
127    async fn set(&self, key: &str, value: &str) -> Result<()> {
128        let mut wlock = self.data.write();
129        // full
130        if let Some(x) = self.capacity {
131            if x >= wlock.len()
132                && self.gc(&mut wlock, max(x / 10, 1)) == 0
133                && wlock.get(key).is_none()
134            {
135                bail!("Capacity is full");
136            }
137        }
138        wlock.insert(key.to_owned(), Data::new(value, None));
139        Ok(())
140    }
141
142    async fn get(&self, key: &str) -> Result<Option<String>> {
143        let rlock = self.data.read();
144        if let Some(v) = rlock.get(key) {
145            if v.valid() {
146                Ok(Some(v.0.to_owned()))
147            } else {
148                drop(rlock);
149                self.data.write().remove(key);
150                Ok(None)
151            }
152        } else {
153            Ok(None)
154        }
155    }
156
157    async fn get_del(&self, key: &str) -> Result<Option<String>> {
158        let v = self.data.write().remove(key);
159        if let Some(v) = v {
160            if v.valid() {
161                return Ok(Some(v.0));
162            }
163        }
164        Ok(None)
165    }
166
167    async fn get_ex(&self, key: &str, ttl: &Duration) -> Result<Option<String>> {
168        let mut wlock = self.data.write();
169        if let Some(v) = wlock.get_mut(key) {
170            if v.valid() {
171                let ret = v.0.to_owned();
172                let ms = ttl.as_millis();
173                if ms == 0 {
174                    wlock.remove(key);
175                } else {
176                    v.set_ttl(Some(ms.try_into()?));
177                }
178                Ok(Some(ret))
179            } else {
180                wlock.remove(key);
181                Ok(None)
182            }
183        } else {
184            Ok(None)
185        }
186    }
187
188    async fn set_ex(&self, key: &str, value: &str, ttl: &Duration) -> Result<()> {
189        let ms = ttl.as_millis();
190        if ms == 0 {
191            return self.del(key).await.map(|_| ());
192        }
193        let mut wlock = self.data.write();
194        // full
195        if let Some(x) = self.capacity {
196            if x >= wlock.len()
197                && self.gc(&mut wlock, max(x / 10, 1)) == 0
198                && wlock.get(key).is_none()
199            {
200                bail!("Capacity is full");
201            }
202        }
203        wlock.insert(key.to_owned(), Data::new(value, Some(ms.try_into()?)));
204        Ok(())
205    }
206
207    async fn del(&self, key: &str) -> Result<bool> {
208        Ok(self.data.write().remove(key).is_some())
209    }
210
211    async fn expire(&self, key: &str, ttl: &Duration) -> Result<bool> {
212        let ms = ttl.as_millis();
213        if ms == 0 {
214            self.del(key).await
215        } else {
216            let mut wlock = self.data.write();
217            if let Some(v) = wlock.get_mut(key) {
218                if v.valid() {
219                    v.set_ttl(Some(ms.try_into()?));
220                    Ok(true)
221                } else {
222                    wlock.remove(key);
223                    Ok(false)
224                }
225            } else {
226                Ok(false)
227            }
228        }
229    }
230
231    async fn flush(&self) -> Result<()> {
232        self.data.write().clear();
233        Ok(())
234    }
235
236    async fn keys(&self, key: &str) -> Result<Vec<String>> {
237        let mut ret = Vec::new();
238        let p = Pattern::new(key)?;
239        for (k, v) in self.data.read().iter() {
240            if v.valid() && p.matches(k) {
241                ret.push(k.to_owned());
242            }
243        }
244        Ok(ret)
245    }
246
247    async fn dels(&self, keys: &[String]) -> Result<u64> {
248        let mut wlock = self.data.write();
249        let mut sum = 0;
250        for i in keys {
251            if wlock.remove(i).is_some() {
252                sum += 1;
253            }
254        }
255        Ok(sum)
256    }
257
258    async fn ttl(&self, key: &str) -> Result<Option<Duration>> {
259        let rlock = self.data.read();
260        if let Some(v) = rlock.get(key) {
261            if v.valid() {
262                v.get_ttl()
263                    .map(|x| Ok(Duration::from_millis(x.try_into()?)))
264                    .transpose()
265            } else {
266                drop(rlock);
267                self.data.write().remove(key);
268                Ok(None)
269            }
270        } else {
271            Ok(None)
272        }
273    }
274}