Skip to main content

sockudo_cache/
memory_cache_manager.rs

1use async_trait::async_trait;
2use moka::future::Cache;
3use sockudo_core::cache::CacheManager;
4use sockudo_core::error::Result;
5use sockudo_core::options::MemoryCacheOptions;
6use std::time::Duration;
7
8/// A Memory-based implementation of the CacheManager trait using Moka.
9#[derive(Clone)]
10pub struct MemoryCacheManager {
11    /// Moka async cache for storing entries. Key and Value are Strings.
12    cache: Cache<String, String, ahash::RandomState>,
13    /// Configuration options for this cache instance.
14    options: MemoryCacheOptions,
15    /// Prefix for all keys in this cache instance.
16    prefix: String,
17}
18
19impl MemoryCacheManager {
20    /// Creates a new Memory cache manager with Moka configuration.
21    pub fn new(prefix: String, options: MemoryCacheOptions) -> Self {
22        let cache_builder = Cache::builder()
23            .max_capacity(options.max_capacity)
24            .name(format!("sockudo-memory-cache-{prefix}").as_str());
25
26        let cache = if options.ttl > 0 {
27            cache_builder.time_to_live(Duration::from_secs(options.ttl))
28        } else {
29            cache_builder
30        }
31        .build_with_hasher(ahash::RandomState::new());
32
33        Self {
34            cache,
35            options,
36            prefix,
37        }
38    }
39
40    /// Get the prefixed key.
41    fn prefixed_key(&self, key: &str) -> String {
42        format!("{}:{}", self.prefix, key)
43    }
44}
45
46#[async_trait]
47impl CacheManager for MemoryCacheManager {
48    async fn has(&self, key: &str) -> Result<bool> {
49        let prefixed_key = self.prefixed_key(key);
50        let exists = self.cache.get(&prefixed_key).await.is_some();
51        Ok(exists)
52    }
53
54    async fn get(&self, key: &str) -> Result<Option<String>> {
55        let prefixed_key = self.prefixed_key(key);
56        Ok(self.cache.get(&prefixed_key).await)
57    }
58
59    async fn set(&self, key: &str, value: &str, _ttl_seconds: u64) -> Result<()> {
60        let prefixed_key = self.prefixed_key(key);
61        let value_string = value.to_string();
62
63        self.cache.insert(prefixed_key, value_string).await;
64        Ok(())
65    }
66
67    async fn remove(&self, key: &str) -> Result<()> {
68        let prefixed_key = self.prefixed_key(key);
69        self.cache.invalidate(&prefixed_key).await;
70        Ok(())
71    }
72
73    async fn disconnect(&self) -> Result<()> {
74        self.cache.invalidate_all();
75        Ok(())
76    }
77
78    async fn ttl(&self, key: &str) -> Result<Option<Duration>> {
79        let prefixed_key = self.prefixed_key(key);
80        if self.cache.contains_key(&prefixed_key) {
81            if self.options.ttl > 0 {
82                Ok(Some(Duration::from_secs(self.options.ttl)))
83            } else {
84                Ok(None)
85            }
86        } else {
87            Ok(None)
88        }
89    }
90}
91
92impl MemoryCacheManager {
93    /// Delete a key from the cache.
94    pub async fn delete(&mut self, key: &str) -> Result<bool> {
95        let prefixed_key = self.prefixed_key(key);
96        if self.cache.contains_key(&prefixed_key) {
97            self.cache.invalidate(&prefixed_key).await;
98            Ok(true)
99        } else {
100            Ok(false)
101        }
102    }
103
104    /// Get multiple keys at once.
105    pub async fn get_many(&mut self, keys: &[&str]) -> Result<Vec<Option<String>>> {
106        let mut results = Vec::with_capacity(keys.len());
107        for &key in keys {
108            results.push(self.get(key).await?);
109        }
110        Ok(results)
111    }
112
113    /// Set multiple key-value pairs at once.
114    pub async fn set_many(&mut self, pairs: &[(&str, &str)], _ttl_seconds: u64) -> Result<()> {
115        for (key, value) in pairs {
116            let prefixed_key = self.prefixed_key(key);
117            let value_string = value.to_string();
118            self.cache.insert(prefixed_key, value_string).await;
119        }
120        Ok(())
121    }
122
123    /// Get all entries from the cache as (key, value, ttl) tuples.
124    /// Returns entries without the prefix.
125    ///
126    /// Note: Moka doesn't support per-entry TTL tracking, so this returns the
127    /// cache's default TTL for all entries. When syncing to another cache system,
128    /// this means all entries will get the same TTL, not their remaining time.
129    pub async fn get_all_entries(&self) -> Vec<(String, String, Option<Duration>)> {
130        let mut entries = Vec::new();
131        let prefix_len = self.prefix.len() + 1; // +1 for the colon separator
132
133        for (key, value) in self.cache.iter() {
134            if key.starts_with(&format!("{}:", self.prefix)) {
135                let unprefixed_key = key[prefix_len..].to_string();
136                let ttl = if self.options.ttl > 0 {
137                    Some(Duration::from_secs(self.options.ttl))
138                } else {
139                    None
140                };
141                entries.push((unprefixed_key, value.clone(), ttl));
142            }
143        }
144
145        entries
146    }
147}