use std::collections::HashMap;
use std::sync::RwLock;
use std::sync::atomic::{AtomicUsize, Ordering};
use super::data::SessionData;
#[derive(Debug, Clone, Copy)]
pub struct SessionStoreKey;
impl reinhardt_di::InjectableKey for SessionStoreKey {}
#[derive(Debug)]
pub struct SessionStore {
pub(super) sessions: RwLock<HashMap<String, SessionData>>,
max_sessions_before_cleanup: AtomicUsize,
next_cleanup_session_count: AtomicUsize,
}
impl Default for SessionStore {
fn default() -> Self {
Self::new()
}
}
impl SessionStore {
pub const DEFAULT_CLEANUP_THRESHOLD: usize = 10_000;
pub fn new() -> Self {
Self::with_cleanup_threshold(Self::DEFAULT_CLEANUP_THRESHOLD)
}
pub fn with_cleanup_threshold(threshold: usize) -> Self {
Self {
sessions: RwLock::new(HashMap::new()),
max_sessions_before_cleanup: AtomicUsize::new(threshold),
next_cleanup_session_count: AtomicUsize::new(threshold),
}
}
pub fn set_cleanup_threshold(&self, threshold: usize) {
self.max_sessions_before_cleanup
.store(threshold, Ordering::Relaxed);
self.next_cleanup_session_count
.store(threshold, Ordering::Relaxed);
}
pub fn get(&self, id: &str) -> Option<SessionData> {
let sessions = self.sessions.read().unwrap_or_else(|e| e.into_inner());
sessions.get(id).cloned()
}
pub fn save(&self, session: SessionData) {
let mut sessions = self.sessions.write().unwrap_or_else(|e| e.into_inner());
sessions.insert(session.id.clone(), session);
let threshold = self.max_sessions_before_cleanup.load(Ordering::Relaxed);
let next_cleanup_session_count = self.next_cleanup_session_count.load(Ordering::Relaxed);
if sessions.len() > threshold && sessions.len() >= next_cleanup_session_count {
sessions.retain(|_, s| s.is_valid());
let cleanup_interval = threshold.max(1);
self.next_cleanup_session_count.store(
sessions.len().saturating_add(cleanup_interval),
Ordering::Relaxed,
);
}
}
pub fn delete(&self, id: &str) {
let mut sessions = self.sessions.write().unwrap_or_else(|e| e.into_inner());
sessions.remove(id);
}
pub fn cleanup(&self) {
let mut sessions = self.sessions.write().unwrap_or_else(|e| e.into_inner());
sessions.retain(|_, session| session.is_valid());
}
pub fn clear(&self) {
let mut sessions = self.sessions.write().unwrap_or_else(|e| e.into_inner());
sessions.clear();
}
pub fn len(&self) -> usize {
let sessions = self.sessions.read().unwrap_or_else(|e| e.into_inner());
sessions.len()
}
pub fn is_empty(&self) -> bool {
let sessions = self.sessions.read().unwrap_or_else(|e| e.into_inner());
sessions.is_empty()
}
}