Skip to main content

tork_core/cache/
memory.rs

1//! The default in-memory cache store.
2
3use std::time::{Duration, Instant};
4
5use moka::future::Cache as MokaCache;
6use moka::Expiry;
7
8use crate::error::Result;
9use crate::router::BoxFuture;
10
11use super::store::CacheStore;
12
13/// Default maximum number of entries before the least-used are evicted.
14const DEFAULT_MAX_CAPACITY: u64 = 10_000;
15
16/// A stored value together with its optional time-to-live.
17#[derive(Clone)]
18struct Entry {
19    data: Vec<u8>,
20    ttl: Option<Duration>,
21}
22
23/// Expires each entry after its own TTL (`None` means no time-based expiry, so the
24/// entry stays until it is evicted by the capacity limit).
25struct PerEntryTtl;
26
27impl Expiry<String, Entry> for PerEntryTtl {
28    fn expire_after_create(
29        &self,
30        _key: &String,
31        value: &Entry,
32        _created_at: Instant,
33    ) -> Option<Duration> {
34        value.ttl
35    }
36
37    fn expire_after_update(
38        &self,
39        _key: &String,
40        value: &Entry,
41        _updated_at: Instant,
42        _duration_until_expiry: Option<Duration>,
43    ) -> Option<Duration> {
44        // Re-setting a key adopts the new value's TTL.
45        value.ttl
46    }
47}
48
49/// An in-memory [`CacheStore`] with per-entry TTL, backed by `moka`.
50///
51/// Entries expire after their TTL and the least-recently-used entries are evicted
52/// once the capacity limit is reached, so the cache stays bounded.
53#[derive(Clone)]
54pub struct MemoryStore {
55    inner: MokaCache<String, Entry>,
56}
57
58impl MemoryStore {
59    /// Creates a store holding up to [`DEFAULT_MAX_CAPACITY`] entries.
60    pub fn new() -> Self {
61        Self::with_capacity(DEFAULT_MAX_CAPACITY)
62    }
63
64    /// Creates a store holding up to `max_capacity` entries.
65    pub fn with_capacity(max_capacity: u64) -> Self {
66        let inner = MokaCache::builder()
67            .max_capacity(max_capacity)
68            .expire_after(PerEntryTtl)
69            .build();
70        Self { inner }
71    }
72}
73
74impl Default for MemoryStore {
75    fn default() -> Self {
76        Self::new()
77    }
78}
79
80impl CacheStore for MemoryStore {
81    fn get<'a>(&'a self, key: &'a str) -> BoxFuture<'a, Result<Option<Vec<u8>>>> {
82        Box::pin(async move { Ok(self.inner.get(key).await.map(|entry| entry.data)) })
83    }
84
85    fn set(&self, key: String, value: Vec<u8>, ttl: Option<Duration>) -> BoxFuture<'_, Result<()>> {
86        Box::pin(async move {
87            self.inner.insert(key, Entry { data: value, ttl }).await;
88            Ok(())
89        })
90    }
91
92    fn delete<'a>(&'a self, key: &'a str) -> BoxFuture<'a, Result<()>> {
93        Box::pin(async move {
94            self.inner.invalidate(key).await;
95            Ok(())
96        })
97    }
98
99    fn clear(&self) -> BoxFuture<'_, Result<()>> {
100        Box::pin(async move {
101            self.inner.invalidate_all();
102            Ok(())
103        })
104    }
105}