Skip to main content

rust_cache_server/
store.rs

1use std::sync::atomic::{AtomicU64, Ordering};
2use std::sync::Arc;
3use std::time::{Duration, Instant};
4
5use dashmap::DashMap;
6use tokio::time;
7
8#[derive(Clone, Debug)]
9pub struct Value {
10    pub data: Vec<u8>,
11    pub expires_at: Option<Instant>,
12}
13
14impl Value {
15    pub fn new(data: Vec<u8>, ttl: Option<Duration>) -> Self {
16        let expires_at = ttl.map(|d| Instant::now() + d);
17        Self { data, expires_at }
18    }
19
20    pub fn is_expired(&self) -> bool {
21        if let Some(exp) = self.expires_at {
22            Instant::now() >= exp
23        } else {
24            false
25        }
26    }
27}
28
29#[derive(Default)]
30pub struct Store {
31    inner: DashMap<String, Value>,
32    max_memory_bytes: Option<u64>,
33    used_bytes: AtomicU64,
34}
35
36impl Store {
37    pub fn new(max_memory_bytes: Option<u64>) -> Self {
38        Self {
39            inner: DashMap::new(),
40            max_memory_bytes,
41            used_bytes: AtomicU64::new(0),
42        }
43    }
44
45    fn value_size(value: &Value) -> u64 {
46        value.data.len() as u64
47    }
48
49    pub fn get(&self, key: &str) -> Option<Vec<u8>> {
50        if let Some(entry) = self.inner.get(key) {
51            if entry.is_expired() {
52                drop(entry);
53                if let Some((_, old)) = self.inner.remove(key) {
54                    let size = Self::value_size(&old);
55                    self.used_bytes
56                        .fetch_sub(size, Ordering::Relaxed);
57                }
58                None
59            } else {
60                Some(entry.data.clone())
61            }
62        } else {
63            None
64        }
65    }
66
67    pub fn set(
68        &self,
69        key: String,
70        value: Vec<u8>,
71        ttl: Option<Duration>,
72    ) -> Result<(), ()> {
73        let new_value = Value::new(value, ttl);
74        let new_size = Self::value_size(&new_value);
75
76        let mut old_size = 0;
77        if let Some(old) = self.inner.get(&key) {
78            old_size = Self::value_size(&old);
79        }
80
81        // Adjust used bytes and check against limit.
82        let prev = self
83            .used_bytes
84            .fetch_add(new_size.saturating_sub(old_size), Ordering::Relaxed);
85        let after = prev + new_size.saturating_sub(old_size);
86
87        if let Some(limit) = self.max_memory_bytes {
88            if after > limit {
89                // Revert and reject.
90                self.used_bytes
91                    .fetch_sub(new_size.saturating_sub(old_size), Ordering::Relaxed);
92                return Err(());
93            }
94        }
95
96        self.inner.insert(key, new_value);
97        Ok(())
98    }
99
100    pub fn del(&self, keys: &[String]) -> usize {
101        let mut removed = 0;
102        for k in keys {
103            if let Some((_, v)) = self.inner.remove(k) {
104                let size = Self::value_size(&v);
105                self.used_bytes
106                    .fetch_sub(size, Ordering::Relaxed);
107                removed += 1;
108            }
109        }
110        removed
111    }
112
113    pub fn expire(&self, key: &str, ttl: Duration) -> bool {
114        if let Some(mut entry) = self.inner.get_mut(key) {
115            if entry.is_expired() {
116                drop(entry);
117                if let Some((_, v)) = self.inner.remove(key) {
118                    let size = Self::value_size(&v);
119                    self.used_bytes
120                        .fetch_sub(size, Ordering::Relaxed);
121                }
122                false
123            } else {
124                entry.expires_at = Some(Instant::now() + ttl);
125                true
126            }
127        } else {
128            false
129        }
130    }
131
132    pub fn ttl(&self, key: &str) -> Option<Duration> {
133        if let Some(entry) = self.inner.get(key) {
134            if let Some(exp) = entry.expires_at {
135                if Instant::now() >= exp {
136                    drop(entry);
137                    if let Some((_, v)) = self.inner.remove(key) {
138                        let size = Self::value_size(&v);
139                        self.used_bytes
140                            .fetch_sub(size, Ordering::Relaxed);
141                    }
142                    None
143                } else {
144                    Some(exp.saturating_duration_since(Instant::now()))
145                }
146            } else {
147                None
148            }
149        } else {
150            None
151        }
152    }
153
154    /// Return all non-expired keys. Used by the KEYS command.
155    pub fn keys(&self) -> Vec<String> {
156        let mut result = Vec::new();
157        for entry in self.inner.iter() {
158            if entry.is_expired() {
159                continue;
160            }
161            result.push(entry.key().clone());
162        }
163        result
164    }
165
166    /// Delete all keys and reset memory usage.
167    pub fn clear_all(&self) {
168        self.inner.clear();
169        self.used_bytes.store(0, Ordering::Relaxed);
170    }
171
172    /// Increment/decrement a numeric value atomically.
173    /// Returns the new value, or None if the key doesn't exist and create_if_missing is false,
174    /// or if the value is not a valid integer.
175    pub fn incr_by(&self, key: &str, delta: i64, create_if_missing: bool) -> Option<i64> {
176        // Try to get existing value
177        let current = if let Some(entry) = self.inner.get(key) {
178            if entry.is_expired() {
179                drop(entry);
180                if let Some((_, old)) = self.inner.remove(key) {
181                    let size = Self::value_size(&old);
182                    self.used_bytes.fetch_sub(size, Ordering::Relaxed);
183                }
184                None
185            } else {
186                // Try to parse as integer
187                match std::str::from_utf8(&entry.data) {
188                    Ok(s) => s.trim().parse::<i64>().ok(),
189                    Err(_) => None,
190                }
191            }
192        } else {
193            None
194        };
195
196        let new_value = if let Some(val) = current {
197            val.checked_add(delta)?
198        } else if create_if_missing {
199            delta
200        } else {
201            return None;
202        };
203
204        // Update or insert the new value
205        let new_data = new_value.to_string().into_bytes();
206        let new_value_obj = Value::new(new_data.clone(), None);
207        let new_size = Self::value_size(&new_value_obj);
208
209        let old_size = if let Some(old) = self.inner.get(key) {
210            Self::value_size(&old)
211        } else {
212            0
213        };
214
215        // Check memory limit
216        if let Some(limit) = self.max_memory_bytes {
217            let prev = self.used_bytes.load(Ordering::Relaxed);
218            let after = prev + new_size.saturating_sub(old_size);
219            if after > limit {
220                return None;
221            }
222        }
223
224        // Update memory counter
225        self.used_bytes
226            .fetch_add(new_size.saturating_sub(old_size), Ordering::Relaxed);
227
228        self.inner.insert(key.to_string(), new_value_obj);
229        Some(new_value)
230    }
231}
232
233pub fn spawn_cleanup(store: Arc<Store>, interval: Duration) {
234    tokio::spawn(async move {
235        let mut ticker = time::interval(interval);
236        loop {
237            ticker.tick().await;
238
239            // Collect expired keys first to avoid holding locks too long.
240            let mut expired_keys = Vec::new();
241            for entry in store.inner.iter() {
242                if entry.is_expired() {
243                    expired_keys.push(entry.key().clone());
244                }
245            }
246
247            for key in expired_keys {
248                if let Some((_, v)) = store.inner.remove(&key) {
249                    let size = Store::value_size(&v);
250                    store
251                        .used_bytes
252                        .fetch_sub(size, Ordering::Relaxed);
253                }
254            }
255        }
256    });
257}
258
259
260