use crate::OxidArt;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::Mutex;
pub type SharedArt = Arc<Mutex<OxidArt>>;
impl OxidArt {
pub async fn shared_with_ticker(interval: Duration) -> SharedArt {
let art = Arc::new(Mutex::new(Self::new()));
art.lock().await.tick(); spawn_ticker(art.clone(), interval);
art
}
pub async fn shared_with_evictor(
tick_interval: Duration,
evict_interval: Duration,
) -> SharedArt {
let art = Arc::new(Mutex::new(Self::new()));
art.lock().await.tick(); spawn_ticker(art.clone(), tick_interval);
spawn_evictor(art.clone(), evict_interval);
art
}
#[inline]
pub fn tick(&mut self) {
self.now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.expect("system time before UNIX epoch")
.as_secs();
}
}
pub fn spawn_ticker(
art: Arc<Mutex<OxidArt>>,
interval: Duration,
) -> tokio::task::JoinHandle<()> {
tokio::spawn(async move {
let mut interval_timer = tokio::time::interval(interval);
loop {
interval_timer.tick().await;
art.lock().await.tick();
}
})
}
pub fn spawn_evictor(
art: Arc<Mutex<OxidArt>>,
interval: Duration,
) -> tokio::task::JoinHandle<()> {
tokio::spawn(async move {
let mut interval_timer = tokio::time::interval(interval);
loop {
interval_timer.tick().await;
art.lock().await.evict_expired();
}
})
}
#[cfg(test)]
mod tests {
use super::*;
use bytes::Bytes;
#[tokio::test]
async fn test_ttl_expiration_with_ticker() {
let art = Arc::new(Mutex::new(OxidArt::new()));
let _handle = spawn_ticker(art.clone(), Duration::from_millis(100));
art.lock().await.tick();
art.lock().await.set_ttl(
Bytes::from_static(b"batch:1"),
Duration::from_secs(1),
Bytes::from_static(b"expires_soon"),
);
art.lock().await.set(
Bytes::from_static(b"batch:2"),
Bytes::from_static(b"forever"),
);
let results = art.lock().await.getn(Bytes::from_static(b"batch:"));
assert_eq!(results.len(), 2, "should have 2 entries before expiration");
tokio::time::sleep(Duration::from_secs(2)).await;
tokio::time::sleep(Duration::from_millis(150)).await;
let results = art.lock().await.getn(Bytes::from_static(b"batch:"));
assert_eq!(results.len(), 1, "should have 1 entry after expiration");
assert_eq!(
results[0],
(
Bytes::from_static(b"batch:2"),
Bytes::from_static(b"forever")
)
);
}
#[tokio::test]
async fn test_shared_with_ticker_constructor() {
let art = OxidArt::shared_with_ticker(Duration::from_millis(100)).await;
art.lock().await.set_ttl(
Bytes::from_static(b"test"),
Duration::from_secs(1),
Bytes::from_static(b"value"),
);
assert!(art.lock().await.get(Bytes::from_static(b"test")).is_some());
tokio::time::sleep(Duration::from_secs(2)).await;
tokio::time::sleep(Duration::from_millis(150)).await;
assert!(art.lock().await.get(Bytes::from_static(b"test")).is_none());
}
}