From 947c87c215f35a58355cc47f7f6ed0d5d34d5534 Mon Sep 17 00:00:00 2001 From: Jeff Baskin Date: Sat, 2 May 2026 14:04:42 -0400 Subject: [PATCH] Got session expiration working. --- src/queue/data_director.rs | 70 +++++++++++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 8 deletions(-) diff --git a/src/queue/data_director.rs b/src/queue/data_director.rs index 1b6b64e..c7d1fd9 100644 --- a/src/queue/data_director.rs +++ b/src/queue/data_director.rs @@ -11,6 +11,7 @@ use std::{ collections::{HashMap, HashSet}, sync::mpsc::Receiver, thread::spawn, + time::Duration, }; use uuid::Uuid; @@ -492,21 +493,28 @@ struct SessionEntry { } impl SessionEntry { + const EXPIRE_IN: Duration = Duration::from_hours(1); + + fn new(id: Field) -> Self { + Self { + id: id, + expire_time: Utc::now() + SessionEntry::EXPIRE_IN, + } + } + fn id(&self) -> &Field { &self.id } + fn extend(&mut self) { + self.expire_time = Utc::now() + SessionEntry::EXPIRE_IN; + } + fn is_expired(&self) -> bool { Utc::now() > self.expire_time } } -impl SessionEntry { - fn new(id: Field) -> Self { - Self { id: id, expire_time: Utc::now(), } - } -} - #[cfg(test)] mod session_entries { use super::*; @@ -516,8 +524,12 @@ mod session_entries { #[test] fn does_entry_return_id() { let id: Field = Uuid::new_v4().into(); + let start = Utc::now() + SessionEntry::EXPIRE_IN; let entry = SessionEntry::new(id.clone()); + let end = Utc::now() + SessionEntry::EXPIRE_IN; assert_eq!(entry.id(), &id); + assert!(start < entry.expire_time, "{:?} should have been after {:?}", entry.expire_time, start); + assert!(end > entry.expire_time, "{:?} should have been after {:?}", entry.expire_time, end); } #[test] @@ -529,6 +541,17 @@ mod session_entries { entry.expire_time = data - Duration::from_secs(1); assert!(entry.is_expired(), "entry should be expired"); } + + #[test] + fn can_expiration_be_reset() { + let mut entry = SessionEntry::new(Uuid::nil().into()); + entry.expire_time = Utc::now(); + let start = Utc::now() + SessionEntry::EXPIRE_IN; + entry.extend(); + let end = Utc::now() + SessionEntry::EXPIRE_IN; + assert!(start < entry.expire_time, "{:?} should have been after {:?}", entry.expire_time, start); + assert!(end > entry.expire_time, "{:?} should have been after {:?}", entry.expire_time, end); + } } struct Session { @@ -551,8 +574,11 @@ impl Session { }, _ => Field::None, }; - match self.entries.get(&converted) { - Some(data) => data.clone(), + match self.entries.get_mut(&converted) { + Some(data) => { + data.extend(); + data.clone() + } None => { let mut new_id: Field = Uuid::new_v4().into(); while self.entries.contains_key(&new_id) { @@ -564,6 +590,18 @@ impl Session { } } } + + fn expire(&mut self) { + let mut remove: Vec = Vec::new(); + for (id, session) in self.entries.iter() { + if session.is_expired() { + remove.push(id.clone()); + } + } + for id in remove.iter() { + self.entries.remove(id); + } + } } #[cfg(test)] @@ -590,8 +628,13 @@ mod sessions { fn are_valid_uuids_returned() { let mut sess = Session::new(); let data = sess.get(&Field::None); + let start = Utc::now() + SessionEntry::EXPIRE_IN; let result = sess.get(data.id()); + let end = Utc::now() + SessionEntry::EXPIRE_IN; assert_eq!(result.id(), data.id()); + let entry = sess.entries.get(data.id()).unwrap(); + assert!(start < entry.expire_time, "{:?} should have been after {:?}", entry.expire_time, start); + assert!(end > entry.expire_time, "{:?} should have been after {:?}", entry.expire_time, end); } #[test] @@ -646,6 +689,17 @@ mod sessions { _ => panic!("session id should always return a uuid field"), } } + + #[test] + fn are_expired_sessions_removed() { + let mut sess = Session::new(); + let lose = sess.get(&Field::None); + sess.entries.get_mut(&lose.id).unwrap().expire_time = Utc::now() - SessionEntry::EXPIRE_IN; + let keep = sess.get(&Field::None); + sess.expire(); + assert_eq!(sess.entries.len(), 1, "should only contain one entry, had {:?}", sess.entries); + assert!(sess.entries.contains_key(&keep.id), "had {:?}, expected {:?}", sess.entries.keys(), &keep.id) + } } pub struct DocRegistry {