Skip to content

Commit

Permalink
introduce new trait for getting expiration
Browse files Browse the repository at this point in the history
  • Loading branch information
Raz-Hemo authored and Raz-Hemo committed Jan 20, 2024
1 parent fbebed9 commit d03536b
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 38 deletions.
9 changes: 5 additions & 4 deletions cached_proc_macro/src/cached.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,16 +255,16 @@ pub fn cached(args: TokenStream, input: TokenStream) -> TokenStream {
quote! {
let old_val = {
#lock
let old_val = cache.get_store().get(&key).cloned();
if let Some(result) = cache.cache_get(&key) {
let (result, has_expired) = cache.cache_get_expired(&key);
if let (Some(result), false) = (result, has_expired) {
#return_cache_block
}
old_val
result
};
#function_call
#lock
let result = match (result.is_err(), old_val) {
(true, Some((_, result))) => {
(true, Some(result)) => {
Ok(result)
}
_ => result
Expand Down Expand Up @@ -315,6 +315,7 @@ pub fn cached(args: TokenStream, input: TokenStream) -> TokenStream {
#(#attributes)*
#visibility #signature_no_muts {
use cached::Cached;
use cached::CloneCached;
let key = #key_convert_block;
#do_set_return_block
}
Expand Down
9 changes: 9 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,15 @@ pub trait Cached<K, V> {
}
}

/// Extra cache operations for types that implement `Clone`
pub trait CloneCached<K, V> {
/// Attempt to retrieve a cached value and indicate whether that value was evicted.
fn cache_get_expired<Q>(&mut self, _key: &Q) -> (Option<V>, bool)
where
K: std::borrow::Borrow<Q>,
Q: std::hash::Hash + Eq + ?Sized;
}

#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
#[async_trait]
Expand Down
25 changes: 24 additions & 1 deletion src/stores/expiring_value_cache.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::{Cached, SizedCache};
use crate::stores::timed::Status;
use crate::{stores::timed::Status, CloneCached};
use std::hash::Hash;

/// The `CanExpire` trait defines a function for implementations to determine if
Expand Down Expand Up @@ -147,6 +147,29 @@ impl<K: Hash + Eq + Clone, V: CanExpire> Cached<K, V> for ExpiringValueCache<K,
}
}

impl<K: Hash + Eq + Clone, V: CanExpire + Clone> CloneCached<K, V> for ExpiringValueCache<K, V> {
fn cache_get_expired<Q>(&mut self, k: &Q) -> (Option<V>, bool)
where
K: std::borrow::Borrow<Q>,
Q: std::hash::Hash + Eq + ?Sized,
{
match self.status(k) {
Status::NotFound => {
self.misses += 1;
(None, false)
}
Status::Found => {
self.hits += 1;
(self.store.cache_get(k).cloned(), false)
}
Status::Expired => {
self.misses += 1;
(self.store.cache_remove(k), true)
}
}
}
}

#[cfg(test)]
/// Expiring Value Cache tests
mod tests {
Expand Down
62 changes: 46 additions & 16 deletions src/stores/timed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ use std::collections::{hash_map::Entry, HashMap};
#[cfg(feature = "async")]
use {super::CachedAsync, async_trait::async_trait, futures::Future};

use crate::CloneCached;

use super::Cached;

/// Enum used for defining the status of time-cached values
Expand Down Expand Up @@ -99,30 +101,35 @@ impl<K: Hash + Eq, V> TimedCache<K, V> {
self.store
.retain(|_, (instant, _)| instant.elapsed().as_secs() < seconds);
}
}

impl<K: Hash + Eq, V> Cached<K, V> for TimedCache<K, V> {
fn cache_get<Q>(&mut self, key: &Q) -> Option<&V>
fn status<Q>(&mut self, key: &Q) -> Status
where
K: std::borrow::Borrow<Q>,
Q: std::hash::Hash + Eq + ?Sized,
{
let status = {
let mut val = self.store.get_mut(key);
if let Some(&mut (instant, _)) = val.as_mut() {
if instant.elapsed().as_secs() < self.seconds {
if self.refresh {
*instant = Instant::now();
}
Status::Found
} else {
Status::Expired
let mut val = self.store.get_mut(key);
if let Some(&mut (instant, _)) = val.as_mut() {
if instant.elapsed().as_secs() < self.seconds {
if self.refresh {
*instant = Instant::now();
}
Status::Found
} else {
Status::NotFound
Status::Expired
}
};
match status {
} else {
Status::NotFound
}
}
}

impl<K: Hash + Eq, V> Cached<K, V> for TimedCache<K, V> {
fn cache_get<Q>(&mut self, key: &Q) -> Option<&V>
where
K: std::borrow::Borrow<Q>,
Q: std::hash::Hash + Eq + ?Sized,
{
match self.status(key) {
Status::NotFound => {
self.misses += 1;
None
Expand Down Expand Up @@ -252,6 +259,29 @@ impl<K: Hash + Eq, V> Cached<K, V> for TimedCache<K, V> {
}
}

impl<K: Hash + Eq + Clone, V: Clone> CloneCached<K, V> for TimedCache<K, V> {
fn cache_get_expired<Q>(&mut self, k: &Q) -> (Option<V>, bool)
where
K: std::borrow::Borrow<Q>,
Q: std::hash::Hash + Eq + ?Sized,
{
match self.status(k) {
Status::NotFound => {
self.misses += 1;
(None, false)
}
Status::Found => {
self.hits += 1;
(self.store.get(k).map(|stamped| &stamped.1).cloned(), false)
}
Status::Expired => {
self.misses += 1;
(self.store.remove(k).map(|stamped| stamped.1), true)
}
}
}
}

#[cfg(feature = "async")]
#[async_trait]
impl<K, V> CachedAsync<K, V> for TimedCache<K, V>
Expand Down
65 changes: 48 additions & 17 deletions src/stores/timed_sized.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use instant::Instant;
#[cfg(feature = "async")]
use {super::CachedAsync, async_trait::async_trait, futures::Future};

use crate::stores::timed::Status;
use crate::{stores::timed::Status, CloneCached};

use super::{Cached, SizedCache};

Expand Down Expand Up @@ -127,30 +127,35 @@ impl<K: Hash + Eq + Clone, V> TimedSizedCache<K, V> {
self.store
.retain(|_, (instant, _)| instant.elapsed().as_secs() < seconds);
}
}

impl<K: Hash + Eq + Clone, V> Cached<K, V> for TimedSizedCache<K, V> {
fn cache_get<Q>(&mut self, key: &Q) -> Option<&V>
fn status<Q>(&mut self, key: &Q) -> Status
where
K: std::borrow::Borrow<Q>,
Q: std::hash::Hash + Eq + ?Sized,
{
let status = {
let mut val = self.store.get_mut_if(key, |_| true);
if let Some(&mut (instant, _)) = val.as_mut() {
if instant.elapsed().as_secs() < self.seconds {
if self.refresh {
*instant = Instant::now();
}
Status::Found
} else {
Status::Expired
let mut val = self.store.get_mut_if(key, |_| true);
if let Some(&mut (instant, _)) = val.as_mut() {
if instant.elapsed().as_secs() < self.seconds {
if self.refresh {
*instant = Instant::now();
}
Status::Found
} else {
Status::NotFound
Status::Expired
}
};
match status {
} else {
Status::NotFound
}
}
}

impl<K: Hash + Eq + Clone, V> Cached<K, V> for TimedSizedCache<K, V> {
fn cache_get<Q>(&mut self, key: &Q) -> Option<&V>
where
K: std::borrow::Borrow<Q>,
Q: std::hash::Hash + Eq + ?Sized,
{
match self.status(key) {
Status::NotFound => {
self.misses += 1;
None
Expand Down Expand Up @@ -279,6 +284,32 @@ impl<K: Hash + Eq + Clone, V> Cached<K, V> for TimedSizedCache<K, V> {
}
}

impl<K: Hash + Eq + Clone, V: Clone> CloneCached<K, V> for TimedSizedCache<K, V> {
fn cache_get_expired<Q>(&mut self, k: &Q) -> (Option<V>, bool)
where
K: std::borrow::Borrow<Q>,
Q: std::hash::Hash + Eq + ?Sized,
{
match self.status(k) {
Status::NotFound => {
self.misses += 1;
(None, false)
}
Status::Found => {
self.hits += 1;
(
self.store.cache_get(k).map(|stamped| stamped.1.clone()),
false,
)
}
Status::Expired => {
self.misses += 1;
(self.store.cache_remove(k).map(|stamped| stamped.1), true)
}
}
}
}

#[cfg(feature = "async")]
#[async_trait]
impl<K, V> CachedAsync<K, V> for TimedSizedCache<K, V>
Expand Down

0 comments on commit d03536b

Please sign in to comment.