dmsc/cache/backends/memory_backend.rs
1//! Copyright © 2025-2026 Wenze Wei. All Rights Reserved.
2//!
3//! This file is part of DMSC.
4//! The DMSC project belongs to the Dunimd Team.
5//!
6//! Licensed under the Apache License, Version 2.0 (the "License");
7//! You may not use this file except in compliance with the License.
8//! You may obtain a copy of the License at
9//!
10//! http://www.apache.org/licenses/LICENSE-2.0
11//!
12//! Unless required by applicable law or agreed to in writing, software
13//! distributed under the License is distributed on an "AS IS" BASIS,
14//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15//! See the License for the specific language governing permissions and
16//! limitations under the License.
17
18#![allow(non_snake_case)]
19
20//! # In-memory Cache Backend
21//!
22//! This module provides an in-memory cache implementation using DashMap for high performance
23//! and thread safety. It implements the DMSCCache trait, providing all standard cache operations
24//! with automatic expiration handling and comprehensive statistics.
25//!
26//! ## Key Features
27//!
28//! - **High Performance**: Uses DashMap for concurrent access without blocking
29//! - **Automatic Expiration**: Automatically removes expired entries on access
30//! - **Comprehensive Statistics**: Tracks hit count, miss count, and eviction count
31//! - **Thread Safe**: Safe for concurrent access from multiple threads
32//! - **LRU-like Behavior**: Touches entries on access to support LRU eviction (if implemented)
33//! - **Expired Entry Cleanup**: Provides a method to explicitly cleanup all expired entries
34//!
35//! ## Design Principles
36//!
37//! 1. **Non-blocking**: Uses DashMap for lock-free concurrent access
38//! 2. **Automatic Expiration**: Expired entries are removed when accessed
39//! 3. **Statistics-driven**: Comprehensive cache statistics for monitoring
40//! 4. **Simple API**: Implements the standard DMSCCache trait
41//! 5. **Memory Efficient**: Automatically cleans up expired entries
42//! 6. **Thread-safe**: Safe for use in multi-threaded applications
43//! 7. **Fast Access**: In-memory storage for minimal latency
44//! 8. **Easy to Use**: Simple constructor with no configuration required
45//!
46//! ## Usage
47//!
48//! ```rust
49//! use dmsc::prelude::*;
50//! use std::time::Duration;
51//!
52//! async fn example() -> DMSCResult<()> {
53//! // Create a new in-memory cache
54//! let cache = DMSCMemoryCache::new();
55//!
56//! // Create a cached value with 1-hour expiration
57//! let value = DMSCCachedValue::new(b"test_value".to_vec(), Duration::from_secs(3600));
58//!
59//! // Set the value in the cache
60//! cache.set("test_key", value).await?;
61//!
62//! // Get the value from the cache
63//! if let Some(retrieved_value) = cache.get("test_key").await {
64//! println!("Retrieved value: {:?}", retrieved_value.payload);
65//! }
66//!
67//! // Check if a key exists
68//! if cache.exists("test_key").await {
69//! println!("Key exists in cache");
70//! }
71//!
72//! // Get cache statistics
73//! let stats = cache.stats().await;
74//! println!("Cache hit rate: {:.2}%", stats.avg_hit_rate * 100.0);
75//!
76//! // Cleanup expired entries
77//! let cleaned = cache.cleanup_expired().await?;
78//! println!("Cleaned up {} expired entries", cleaned);
79//!
80//! Ok(())
81//! }
82//! ```
83
84use dashmap::DashMap;
85use std::sync::Arc;
86use tokio::sync::RwLock;
87use crate::cache::{DMSCCache, DMSCCachedValue, DMSCCacheStats};
88use crate::core::DMSCResult;
89
90/// In-memory cache implementation using DashMap for high performance and thread safety.
91///
92/// This struct provides an in-memory cache with automatic expiration handling, comprehensive
93/// statistics, and thread-safe concurrent access.
94pub struct DMSCMemoryCache {
95 /// Underlying storage using DashMap for concurrent access
96 store: Arc<DashMap<String, DMSCCachedValue>>,
97 /// Cache statistics tracking hit count, miss count, and eviction count
98 stats: Arc<RwLock<DMSCCacheStats>>,
99}
100
101impl Default for DMSCMemoryCache {
102 fn default() -> Self {
103 Self::new()
104 }
105}
106
107impl DMSCMemoryCache {
108 /// Creates a new in-memory cache instance.
109 ///
110 /// # Returns
111 ///
112 /// A new DMSCMemoryCache instance
113 pub fn new() -> Self {
114 DMSCMemoryCache {
115 store: Arc::new(DashMap::new()),
116 stats: Arc::new(RwLock::new(DMSCCacheStats::default())),
117 }
118 }
119}
120
121#[async_trait::async_trait]
122impl DMSCCache for DMSCMemoryCache {
123 /// Gets a value from the cache by key.
124 ///
125 /// This method checks if the value exists and is not expired. If the value is expired,
126 /// it is removed from the cache and None is returned. Otherwise, the value is returned
127 /// and its last access time is updated.
128 ///
129 /// # Parameters
130 ///
131 /// - `key`: The key to retrieve
132 ///
133 /// # Returns
134 ///
135 /// An `Option<DMSCCachedValue>` containing the value if it exists and is not expired, or None otherwise
136 async fn get(&self, key: &str) -> DMSCResult<Option<String>> {
137 match self.store.get(key) {
138 Some(entry) => {
139 let value = entry.clone();
140 if value.is_expired() {
141 drop(entry);
142 self.store.remove(key);
143 let mut stats = self.stats.write().await;
144 stats.misses += 1;
145
146 Ok(None)
147 } else {
148 let mut stats = self.stats.write().await;
149 stats.hits += 1;
150 Ok(Some(value.value))
151 }
152 }
153 None => {
154 let mut stats = self.stats.write().await;
155 stats.misses += 1;
156 Ok(None)
157 }
158 }
159 }
160
161 /// Sets a value in the cache with the given key.
162 ///
163 /// # Parameters
164 ///
165 /// - `key`: The key to set
166 /// - `value`: The cached value to store
167 ///
168 /// # Returns
169 ///
170 /// A `DMSCResult<()>` indicating success or failure
171 async fn set(&self, key: &str, value: &str, ttl_seconds: Option<u64>) -> crate::core::DMSCResult<()> {
172 let cached_value = DMSCCachedValue::new(value.to_string(), ttl_seconds);
173 self.store.insert(key.to_string(), cached_value);
174 Ok(())
175 }
176
177 /// Deletes a value from the cache by key.
178 ///
179 /// # Parameters
180 ///
181 /// - `key`: The key to delete
182 ///
183 /// # Returns
184 ///
185 /// A `DMSCResult<bool>` indicating whether the key was found and deleted
186 async fn delete(&self, key: &str) -> crate::core::DMSCResult<bool> {
187 Ok(self.store.remove(key).is_some())
188 }
189
190 /// Checks if a key exists in the cache and is not expired.
191 ///
192 /// If the key exists but the value is expired, it is removed from the cache and false is returned.
193 ///
194 /// # Parameters
195 ///
196 /// - `key`: The key to check
197 ///
198 /// # Returns
199 ///
200 /// `true` if the key exists and is not expired, `false` otherwise
201 async fn exists(&self, key: &str) -> bool {
202 if let Some(entry) = self.store.get(key) {
203 if entry.is_expired() {
204 drop(entry);
205 self.store.remove(key);
206 false
207 } else {
208 true
209 }
210 } else {
211 false
212 }
213 }
214
215 /// Clears all entries from the cache.
216 ///
217 /// # Returns
218 ///
219 /// A `DMSCResult<()>` indicating success or failure
220 async fn clear(&self) -> crate::core::DMSCResult<()> {
221 self.store.clear();
222 Ok(())
223 }
224
225 /// Gets cache statistics.
226 ///
227 /// # Returns
228 ///
229 /// A `DMSCCacheStats` struct containing cache statistics
230 async fn stats(&self) -> DMSCCacheStats {
231 *self.stats.read().await
232 }
233
234 /// Cleans up all expired entries from the cache.
235 ///
236 /// # Returns
237 ///
238 /// A `DMSCResult<usize>` containing the number of expired entries cleaned up
239 async fn cleanup_expired(&self) -> crate::core::DMSCResult<usize> {
240 let mut cleaned = 0;
241 let keys: Vec<String> = self.store.iter().map(|entry| entry.key().clone()).collect();
242
243 for key in keys {
244 if let Some(entry) = self.store.get(&key) {
245 if entry.is_expired() {
246 drop(entry);
247 self.store.remove(&key);
248 cleaned += 1;
249 }
250 }
251 }
252
253 Ok(cleaned)
254 }
255
256 /// Gets all keys from the cache.
257 ///
258 /// # Returns
259 ///
260 /// A `DMSCResult<Vec<String>>` containing all cache keys
261 async fn keys(&self) -> crate::core::DMSCResult<Vec<String>> {
262 let keys: Vec<String> = self.store.iter().map(|entry| entry.key().clone()).collect();
263 Ok(keys)
264 }
265}