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}