Last active
September 7, 2023 04:40
-
-
Save fatfingers23/3999cbeeaae4aefd00f990647d1e128e to your computer and use it in GitHub Desktop.
Simple Actix cache with a TTL
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use actix_web::web::Data; | |
use std::collections::HashMap; | |
use std::sync::Mutex; | |
use std::time::{Duration, Instant}; | |
use tokio::time::sleep; | |
type CacheItem = HashMap<String, (Instant, String)>; | |
#[derive(Clone)] | |
pub struct Cache { | |
store: Data<Mutex<CacheItem>>, | |
ttl: Duration, | |
} | |
impl Cache { | |
pub async fn clean_expired(&mut self) { | |
loop { | |
sleep(self.ttl).await; | |
let mut store = self.store.lock().unwrap(); | |
store.retain(|_, (timestamp, _)| timestamp.elapsed() <= self.ttl); | |
} | |
} | |
pub fn new(ttl: Duration) -> Self { | |
let data = Data::new(Mutex::new(CacheItem::new())); | |
Cache { store: data, ttl } | |
} | |
async fn set_value(&self, key: String, value: String) { | |
self.store | |
.lock() | |
.unwrap() | |
.insert(key, (Instant::now(), value)); | |
} | |
async fn get_value(&self, key: String) -> Option<String> { | |
if let Some((time_stamp, value)) = self.store.lock().unwrap().get(&key) { | |
if time_stamp.elapsed() > self.ttl { | |
return None; | |
} | |
return Some(value.clone()); | |
} | |
None | |
} | |
} | |
#[cfg(test)] | |
mod tests { | |
use super::*; | |
#[tokio::test] | |
async fn test_set_and_get() { | |
let cache = Cache::new(Duration::from_millis(20)); | |
let key = "key".to_string(); | |
let value = "value".to_string(); | |
cache.set_value(key, value).await; | |
sleep(Duration::from_millis(5)).await; | |
let result = cache.get_value("key".to_string()).await; | |
assert_eq!(result, Some("value".to_string())); | |
} | |
#[tokio::test] | |
async fn test_cannot_get_expired() { | |
let cache = Cache::new(Duration::from_millis(10)); | |
let key = "key".to_string(); | |
let value = "value".to_string(); | |
cache.set_value(key, value).await; | |
sleep(Duration::from_millis(20)).await; | |
let result = cache.get_value("key".to_string()).await; | |
assert_eq!(result, None); | |
} | |
#[tokio::test] | |
async fn test_clean_expired() { | |
let cache = Cache::new(Duration::from_millis(10)); | |
let key = "key".to_string(); | |
let value = "value".to_string(); | |
cache.set_value(key, value).await; | |
let cloned_cache = cache.clone(); | |
tokio::spawn(async move { | |
cloned_cache.clone().clean_expired().await; | |
}); | |
sleep(Duration::from_millis(20)).await; | |
let result = cache.get_value("key".to_string()).await; | |
assert_eq!(result, None); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment