From cd48477fb1aebec01ff3c58820b9e69b25dac7b5 Mon Sep 17 00:00:00 2001 From: Brian Caswell Date: Mon, 15 Jul 2024 16:49:14 -0400 Subject: [PATCH] refactor to have a consistent expiration window --- src/lib.rs | 50 +++++++++++++++------------------------------ src/test.rs | 58 +++++++++++++++++++++++++++-------------------------- 2 files changed, 46 insertions(+), 62 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 608b379..adda0b6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,8 +4,9 @@ //! ```rust //! use std::time::Duration; //! use expiringmap::ExpiringMap; -//! let mut map = ExpiringMap::new(); -//! map.insert("key", "value", Duration::from_millis(50)); +//! let mut map = ExpiringMap::new(Duration::from_millis(50)); +//! map.insert("key", "value"); +//! assert!(map.get(&"key").is_some()); //! std::thread::sleep(Duration::from_millis(60)); //! assert!(map.get(&"key").is_none()); //! ``` @@ -76,6 +77,7 @@ impl ExpiryValue { #[derive(Debug)] pub struct ExpiringMap { last_size: usize, + duration: Duration, inner: ExpiringMapInner, } @@ -102,15 +104,16 @@ impl ExpiringMap { const MINIMUM_VACUUM_SIZE: usize = 8; /// Create a new [`ExpiringMap`] - pub fn new() -> Self { - Self::with_capacity(0) + pub fn new(duration: Duration) -> Self { + Self::with_capacity(duration, 0) } /// Create a new [`ExpiringMap`] with the specified capacity - pub fn with_capacity(capacity: usize) -> Self { + pub fn with_capacity(duration: Duration, capacity: usize) -> Self { Self { inner: ExpiringMapInner::with_capacity(capacity), last_size: Self::MINIMUM_VACUUM_SIZE, + duration, } } @@ -194,11 +197,11 @@ impl ExpiringMap { } /// Insert a value into the map, returning the old value if it has not expired and existed - pub fn insert(&mut self, key: K, value: V, ttl: Duration) -> Option> { + pub fn insert(&mut self, key: K, value: V) -> Option> { self.vacuum_if_needed(); let entry = ExpiryValue { inserted: Instant::now(), - ttl, + ttl: self.duration, value, }; self.inner @@ -270,27 +273,18 @@ impl ExpiringMap { impl ExpiringSet { /// Create a new [`ExpiringSet`] - pub fn new() -> Self { - Self::with_capacity(0) + pub fn new(duration: Duration) -> Self { + Self::with_capacity(duration, 0) } /// Create a new [`ExpiringSet`] with the specified capacity - pub fn with_capacity(capacity: usize) -> Self { - Self(ExpiringMap::with_capacity(capacity)) + pub fn with_capacity(duration: Duration, capacity: usize) -> Self { + Self(ExpiringMap::with_capacity(duration, capacity)) } /// Returns true if the set contains this value - pub fn insert(&mut self, key: K, ttl: Duration) -> bool { - self.vacuum_if_needed(); - let entry = ExpiryValue { - inserted: Instant::now(), - ttl, - value: (), - }; - self.inner - .insert(key, entry) - .filter(ExpiryValue::not_expired) - .is_some() + pub fn insert(&mut self, key: K) -> bool { + self.0.insert(key, ()).is_some() } /// Returns true if the set contains this value @@ -328,15 +322,3 @@ impl ExpiringSet { self.0.shrink_to(min_capacity); } } - -impl Default for ExpiringMap { - fn default() -> Self { - Self::new() - } -} - -impl Default for ExpiringSet { - fn default() -> Self { - Self::new() - } -} diff --git a/src/test.rs b/src/test.rs index bac169e..002ba11 100644 --- a/src/test.rs +++ b/src/test.rs @@ -3,8 +3,8 @@ use std::{thread::sleep, time::Duration}; use crate::{ExpiringMap, ExpiringSet}; #[test] fn map_works() { - let mut m = ExpiringMap::new(); - m.insert("v", "x", Duration::from_millis(50)); + let mut m = ExpiringMap::new(Duration::from_millis(50)); + m.insert("v", "x"); assert_eq!(m.get(&"v"), Some(&"x")); sleep(Duration::from_millis(75)); assert!(!m.contains_key(&"v")); @@ -12,8 +12,8 @@ fn map_works() { #[test] fn set_works() { - let mut m = ExpiringSet::new(); - m.insert("v", Duration::from_millis(50)); + let mut m = ExpiringSet::new(Duration::from_millis(50)); + m.insert("v"); assert!(m.contains_key(&"v")); sleep(Duration::from_millis(75)); assert!(!m.contains_key(&"v")); @@ -21,29 +21,32 @@ fn set_works() { #[test] fn remaining_calc() { - let mut m = ExpiringSet::new(); - m.insert("v", Duration::from_millis(50)); + let mut m = ExpiringSet::new(Duration::from_millis(50)); + m.insert("v"); let meta = m.get_meta(&"v").unwrap(); - dbg!(meta.remaining()); - // we allow 10ms of slop here. Should be enough; - assert!(meta.remaining() > Duration::from_millis(40)); - assert!(meta.remaining() < Duration::from_millis(60)); + for _ in 0..3 { + let a = meta.remaining(); + sleep(Duration::from_millis(1)); + let b = meta.remaining(); + assert!(a > b, "{:?} !> {:?}", a, b); + } + sleep(Duration::from_millis(75)); assert!(!m.contains_key(&"v")); } #[test] fn vacuum_keeps() { - let mut m = ExpiringSet::new(); - m.insert("v", Duration::from_secs(50)); + let mut m = ExpiringSet::new(Duration::from_secs(50)); + m.insert("v"); m.vacuum(); assert!(m.get(&"v").is_some()); } #[test] fn vacuum_sweeps() { - let mut m = ExpiringSet::new(); - m.insert("v", Duration::from_millis(50)); + let mut m = ExpiringSet::new(Duration::from_millis(50)); + m.insert("v"); sleep(Duration::from_millis(75)); m.vacuum(); assert!(!m.inner.contains_key(&"v")); @@ -51,31 +54,30 @@ fn vacuum_sweeps() { #[test] fn insert_replace() { - let mut m = ExpiringMap::new(); - m.insert("v", "x", Duration::from_secs(5)); - assert_eq!( - m.insert("v", "y", Duration::from_secs(5)).unwrap().value, - "x" - ); + let mut m = ExpiringMap::new(Duration::from_secs(5)); + m.insert("v", "x"); + assert_eq!(m.insert("v", "y").map(|v| v.value), Some("x")); } #[test] fn insert_replace_sweep() { - let mut m = ExpiringMap::new(); - m.insert("v", "x", Duration::ZERO); - assert!(m.insert("v", "y", Duration::from_secs(100)).is_none()); - assert!(m.insert("v", "z", Duration::from_secs(1)).is_some()); + let mut m = ExpiringMap::new(Duration::from_millis(50)); + m.insert("v", "x"); + assert_eq!(m.insert("v", "y").map(|v| v.value), Some("x")); + sleep(Duration::from_millis(75)); + assert!(m.insert("v", "z").is_none()); + assert_eq!(m.get("v"), Some(&"z")) } #[test] fn test_borrow() { - let mut m: ExpiringMap = ExpiringMap::new(); - m.insert(String::from("x"), 1, Duration::from_secs(5)); + let mut m: ExpiringMap = ExpiringMap::new(Duration::from_secs(5)); + m.insert(String::from("x"), 1); assert_eq!(m.get("x"), Some(&1)); assert_eq!(m.get(&String::from("x")), Some(&1)); - let mut m: ExpiringSet = ExpiringSet::new(); - m.insert(String::from("x"), Duration::from_secs(5)); + let mut m: ExpiringSet = ExpiringSet::new(Duration::from_secs(5)); + m.insert(String::from("x")); assert!(m.contains("x")); assert!(m.contains(&String::from("x"))); }