rust_webx_host/
memory_cache.rs1use rust_webx_core::cache::options::DistributedCacheEntryOptions;
7use rust_webx_core::cache::trait_def::{CacheError, IDistributedCache, Result};
8use std::collections::{HashMap, VecDeque};
9use std::time::Instant;
10use tokio::sync::RwLock;
11
12struct CacheEntry {
13 data: Vec<u8>,
14 expires_at: Option<Instant>,
15 sliding_ttl: Option<std::time::Duration>,
16}
17
18impl CacheEntry {
19 fn new(data: Vec<u8>, options: &DistributedCacheEntryOptions) -> Self {
20 let expires_at = options
21 .absolute_expiration_relative_to_now
22 .map(|d| Instant::now() + d);
23 let sliding_ttl = options.sliding_expiration;
24 let expires_at = if expires_at.is_some() {
25 expires_at
26 } else {
27 sliding_ttl.map(|d| Instant::now() + d)
28 };
29 Self {
30 data,
31 expires_at,
32 sliding_ttl,
33 }
34 }
35 fn is_expired(&self) -> bool {
36 self.expires_at.is_some_and(|t| Instant::now() >= t)
37 }
38 fn refresh(&mut self) {
39 if let Some(ttl) = self.sliding_ttl {
40 self.expires_at = Some(Instant::now() + ttl);
41 }
42 }
43}
44
45struct CacheInner {
46 entries: HashMap<String, CacheEntry>,
47 insertion_order: VecDeque<String>,
48}
49
50impl CacheInner {
51 fn evict_expired(&mut self) {
52 let expired: Vec<String> = self
53 .entries
54 .iter()
55 .filter(|(_, v)| v.is_expired())
56 .map(|(k, _)| k.clone())
57 .collect();
58 for k in expired {
59 self.entries.remove(&k);
60 self.insertion_order.retain(|x| x != &k);
61 }
62 }
63}
64
65pub struct MemoryCache {
66 inner: RwLock<CacheInner>,
67 max_entries: usize,
68}
69
70impl MemoryCache {
71 pub fn new() -> Self {
72 Self {
73 inner: RwLock::new(CacheInner {
74 entries: HashMap::new(),
75 insertion_order: VecDeque::new(),
76 }),
77 max_entries: 0,
78 }
79 }
80 pub fn with_max_entries(mut self, n: usize) -> Self {
81 self.max_entries = n;
82 self
83 }
84 pub async fn compact(&self) {
85 let mut inner = self.inner.write().await;
86 inner.evict_expired();
87 }
88 pub async fn count(&self) -> usize {
89 self.inner.read().await.entries.len()
90 }
91}
92
93impl Default for MemoryCache {
94 fn default() -> Self {
95 Self::new()
96 }
97}
98
99#[async_trait::async_trait]
100impl IDistributedCache for MemoryCache {
101 async fn get(&self, key: &str) -> Result<Option<Vec<u8>>> {
102 {
104 let inner = self.inner.read().await;
105 match inner.entries.get(key) {
106 Some(e) if !e.is_expired() => {
107 let data = e.data.clone();
108 let needs_refresh = e.sliding_ttl.is_some();
109 drop(inner);
110 if needs_refresh {
111 let mut inner = self.inner.write().await;
112 if let Some(e) = inner.entries.get_mut(key) {
113 e.refresh();
114 }
115 }
116 return Ok(Some(data));
117 }
118 Some(_) => {}
119 None => return Ok(None),
120 }
121 }
122 let mut inner = self.inner.write().await;
124 match inner.entries.get_mut(key) {
125 Some(e) if !e.is_expired() => {
126 e.refresh();
127 Ok(Some(e.data.clone()))
128 }
129 Some(_) => {
130 inner.entries.remove(key);
131 inner.insertion_order.retain(|x| x != key);
132 Ok(None)
133 }
134 None => Ok(None),
135 }
136 }
137
138 async fn set(
139 &self,
140 key: &str,
141 value: Vec<u8>,
142 options: Option<&DistributedCacheEntryOptions>,
143 ) -> Result<()> {
144 let opts = options.cloned().unwrap_or_default();
145 if opts.size_limit > 0 && value.len() > opts.size_limit {
146 return Err(CacheError::Message(format!(
147 "size {} exceeds limit {}",
148 value.len(),
149 opts.size_limit
150 )));
151 }
152 let mut inner = self.inner.write().await;
153 let is_new = !inner.entries.contains_key(key);
154
155 if is_new && self.max_entries > 0 && inner.entries.len() >= self.max_entries {
156 inner.evict_expired();
157 while inner.entries.len() >= self.max_entries {
158 match inner.insertion_order.pop_front() {
159 Some(k) => {
160 inner.entries.remove(&k);
161 }
162 None => break,
163 }
164 }
165 }
166
167 inner
168 .entries
169 .insert(key.to_string(), CacheEntry::new(value, &opts));
170 if is_new {
171 inner.insertion_order.push_back(key.to_string());
172 }
173 Ok(())
174 }
175
176 async fn remove(&self, key: &str) -> Result<()> {
177 let mut inner = self.inner.write().await;
178 inner.entries.remove(key);
179 inner.insertion_order.retain(|x| x != key);
180 Ok(())
181 }
182
183 async fn refresh(&self, key: &str) -> Result<()> {
184 let mut inner = self.inner.write().await;
185 if let Some(e) = inner.entries.get_mut(key) {
186 e.refresh();
187 }
188 Ok(())
189 }
190
191 async fn exists(&self, key: &str) -> Result<bool> {
192 {
194 let inner = self.inner.read().await;
195 match inner.entries.get(key) {
196 Some(e) if !e.is_expired() => {
197 let needs_refresh = e.sliding_ttl.is_some();
198 drop(inner);
199 if needs_refresh {
200 let mut inner = self.inner.write().await;
201 if let Some(e) = inner.entries.get_mut(key) {
202 e.refresh();
203 }
204 }
205 return Ok(true);
206 }
207 Some(_) => {}
208 None => return Ok(false),
209 }
210 }
211 let mut inner = self.inner.write().await;
213 match inner.entries.get_mut(key) {
214 Some(e) if !e.is_expired() => {
215 e.refresh();
216 Ok(true)
217 }
218 Some(_) => {
219 inner.entries.remove(key);
220 inner.insertion_order.retain(|x| x != key);
221 Ok(false)
222 }
223 None => Ok(false),
224 }
225 }
226
227 async fn clear(&self) -> Result<()> {
228 let mut inner = self.inner.write().await;
229 inner.entries.clear();
230 inner.insertion_order.clear();
231 Ok(())
232 }
233}