Skip to content

Commit

Permalink
Size-aware cache management
Browse files Browse the repository at this point in the history
- Add `weighter` method to the `CacheBuilder`s.
- Add unit tests for `sync::Cache` with weighter closure registered.
  • Loading branch information
tatsuya6502 committed Aug 16, 2021
1 parent dd35403 commit 45ee2a9
Show file tree
Hide file tree
Showing 7 changed files with 210 additions and 18 deletions.
45 changes: 45 additions & 0 deletions src/common/deque.rs
Expand Up @@ -654,6 +654,51 @@ mod tests {
assert!((&mut deque).next().is_none());
}

#[test]
fn next_node() {
let mut deque: Deque<String> = Deque::new(MainProbation);

let node1 = DeqNode::new(MainProbation, "a".into());
deque.push_back(Box::new(node1));
let node2 = DeqNode::new(MainProbation, "b".into());
let node2_ptr = deque.push_back(Box::new(node2));
let node3 = DeqNode::new(MainProbation, "c".into());
let node3_ptr = deque.push_back(Box::new(node3));

// -------------------------------------------------------
// First iteration.
// peek_front() -> node1
let node1a = deque.peek_front().unwrap();
assert_eq!(node1a.element, "a".to_string());
let node2a = node1a.next_node().unwrap();
assert_eq!(node2a.element, "b".to_string());
let node3a = node2a.next_node().unwrap();
assert_eq!(node3a.element, "c".to_string());
assert!(node3a.next_node().is_none());

// -------------------------------------------------------
// Iterate after a move_to_back.
// Move "b" to the back. So now "a" -> "c" -> "b".
unsafe { deque.move_to_back(node2_ptr) };
let node1a = deque.peek_front().unwrap();
assert_eq!(node1a.element, "a".to_string());
let node3a = node1a.next_node().unwrap();
assert_eq!(node3a.element, "c".to_string());
let node2a = node3a.next_node().unwrap();
assert_eq!(node2a.element, "b".to_string());
assert!(node2a.next_node().is_none());

// -------------------------------------------------------
// Iterate after an unlink.
// Unlink the second node "c". Now "a" -> "c".
unsafe { deque.unlink(node3_ptr) };
let node1a = deque.peek_front().unwrap();
assert_eq!(node1a.element, "a".to_string());
let node2a = node1a.next_node().unwrap();
assert_eq!(node2a.element, "b".to_string());
assert!(node2a.next_node().is_none());
}

#[test]
fn drop() {
use std::{cell::RefCell, rc::Rc};
Expand Down
28 changes: 25 additions & 3 deletions src/future/builder.rs
@@ -1,3 +1,5 @@
use crate::common::Weighter;

use super::Cache;

use std::{
Expand Down Expand Up @@ -36,16 +38,17 @@ use std::{
/// // after 30 minutes (TTL) from the insert().
/// ```
///
pub struct CacheBuilder<C> {
pub struct CacheBuilder<K, V, C> {
max_capacity: Option<usize>,
initial_capacity: Option<usize>,
weighter: Option<Weighter<K, V>>,
time_to_live: Option<Duration>,
time_to_idle: Option<Duration>,
invalidator_enabled: bool,
cache_type: PhantomData<C>,
}

impl<K, V> CacheBuilder<Cache<K, V, RandomState>>
impl<K, V> CacheBuilder<K, V, Cache<K, V, RandomState>>
where
K: Eq + Hash + Send + Sync + 'static,
V: Clone + Send + Sync + 'static,
Expand All @@ -54,6 +57,7 @@ where
Self {
max_capacity: None,
initial_capacity: None,
weighter: None,
time_to_live: None,
time_to_idle: None,
invalidator_enabled: false,
Expand All @@ -77,6 +81,7 @@ where
self.max_capacity,
self.initial_capacity,
build_hasher,
self.weighter,
self.time_to_live,
self.time_to_idle,
self.invalidator_enabled,
Expand All @@ -92,14 +97,23 @@ where
self.max_capacity,
self.initial_capacity,
hasher,
self.weighter,
self.time_to_live,
self.time_to_idle,
self.invalidator_enabled,
)
}
}

impl<C> CacheBuilder<C> {
impl<K, V, C> CacheBuilder<K, V, C> {
/// Sets the max capacity of the cache.
pub fn max_capacity(self, max_capacity: usize) -> Self {
Self {
max_capacity: Some(max_capacity),
..self
}
}

/// Sets the initial capacity of the cache.
pub fn initial_capacity(self, capacity: usize) -> Self {
Self {
Expand All @@ -108,6 +122,14 @@ impl<C> CacheBuilder<C> {
}
}

/// Sets the weighter closure of the cache.
pub fn weighter(self, weighter: Weighter<K, V>) -> Self {
Self {
weighter: Some(weighter),
..self
}
}

/// Sets the time to live of the cache.
///
/// A cached entry will be expired after the specified duration past from
Expand Down
15 changes: 13 additions & 2 deletions src/future/cache.rs
Expand Up @@ -3,6 +3,7 @@ use super::{
CacheBuilder, ConcurrentCacheExt,
};
use crate::{
common::Weighter,
sync::{
base_cache::{BaseCache, HouseKeeperArc, MAX_SYNC_REPEATS, WRITE_RETRY_INTERVAL_MICROS},
housekeeper::InnerSync,
Expand Down Expand Up @@ -223,10 +224,18 @@ where
/// [builder-struct]: ./struct.CacheBuilder.html
pub fn new(max_capacity: usize) -> Self {
let build_hasher = RandomState::default();
Self::with_everything(Some(max_capacity), None, build_hasher, None, None, false)
Self::with_everything(
Some(max_capacity),
None,
build_hasher,
None,
None,
None,
false,
)
}

pub fn builder() -> CacheBuilder<Cache<K, V, RandomState>> {
pub fn builder() -> CacheBuilder<K, V, Cache<K, V, RandomState>> {
CacheBuilder::unbound()
}
}
Expand All @@ -241,6 +250,7 @@ where
max_capacity: Option<usize>,
initial_capacity: Option<usize>,
build_hasher: S,
weighter: Option<Weighter<K, V>>,
time_to_live: Option<Duration>,
time_to_idle: Option<Duration>,
invalidator_enabled: bool,
Expand All @@ -250,6 +260,7 @@ where
max_capacity,
initial_capacity,
build_hasher.clone(),
weighter,
time_to_live,
time_to_idle,
invalidator_enabled,
Expand Down
5 changes: 4 additions & 1 deletion src/sync/base_cache.rs
Expand Up @@ -86,6 +86,7 @@ where
max_capacity: Option<usize>,
initial_capacity: Option<usize>,
build_hasher: S,
weighter: Option<Weighter<K, V>>,
time_to_live: Option<Duration>,
time_to_idle: Option<Duration>,
invalidator_enabled: bool,
Expand All @@ -96,6 +97,7 @@ where
max_capacity,
initial_capacity,
build_hasher,
weighter,
r_rcv,
w_rcv,
time_to_live,
Expand Down Expand Up @@ -454,6 +456,7 @@ where
max_capacity: Option<usize>,
initial_capacity: Option<usize>,
build_hasher: S,
weighter: Option<Weighter<K, V>>,
read_op_ch: Receiver<ReadOp<K, V>>,
write_op_ch: Receiver<WriteOp<K, V>>,
time_to_live: Option<Duration>,
Expand Down Expand Up @@ -484,7 +487,7 @@ where
time_to_live,
time_to_idle,
valid_after: AtomicU64::new(0),
weighter: None,
weighter,
invalidator_enabled,
// When enabled, this field will be set later via the set_invalidator method.
invalidator: RwLock::new(None),
Expand Down
51 changes: 42 additions & 9 deletions src/sync/builder.rs
@@ -1,3 +1,5 @@
use crate::common::Weighter;

use super::{Cache, SegmentedCache};

use std::{
Expand Down Expand Up @@ -38,47 +40,60 @@ use std::{
/// // after 30 minutes (TTL) from the insert().
/// ```
///
pub struct CacheBuilder<C> {
pub struct CacheBuilder<K, V, C> {
max_capacity: Option<usize>,
initial_capacity: Option<usize>,
num_segments: Option<usize>,
weighter: Option<Weighter<K, V>>,
time_to_live: Option<Duration>,
time_to_idle: Option<Duration>,
invalidator_enabled: bool,
cache_type: PhantomData<C>,
}

impl<K, V> CacheBuilder<Cache<K, V, RandomState>>
impl<K, V> CacheBuilder<K, V, Cache<K, V, RandomState>>
where
K: Eq + Hash + Send + Sync + 'static,
V: Clone + Send + Sync + 'static,
{
/// Construct a new `CacheBuilder` that will be used to build a `Cache` or
/// `SegmentedCache` holding up to `max_capacity` entries.
pub fn new(max_capacity: usize) -> Self {
pub(crate) fn unbound() -> Self {
Self {
max_capacity: Some(max_capacity),
max_capacity: None,
initial_capacity: None,
num_segments: None,
weighter: None,
time_to_live: None,
time_to_idle: None,
invalidator_enabled: false,
cache_type: PhantomData::default(),
}
}

/// Construct a new `CacheBuilder` that will be used to build a `Cache` or
/// `SegmentedCache` holding up to `max_capacity` entries.
pub fn new(max_capacity: usize) -> Self {
Self {
max_capacity: Some(max_capacity),
..Self::unbound()
}
}

/// Sets the number of segments of the cache.
///
/// # Panics
///
/// Panics if `num_segments` is less than or equals to 1.
pub fn segments(self, num_segments: usize) -> CacheBuilder<SegmentedCache<K, V, RandomState>> {
pub fn segments(
self,
num_segments: usize,
) -> CacheBuilder<K, V, SegmentedCache<K, V, RandomState>> {
assert!(num_segments > 1);

CacheBuilder {
max_capacity: self.max_capacity,
initial_capacity: self.initial_capacity,
num_segments: Some(num_segments),
weighter: None,
time_to_live: self.time_to_live,
time_to_idle: self.time_to_idle,
invalidator_enabled: self.invalidator_enabled,
Expand All @@ -96,6 +111,7 @@ where
self.max_capacity,
self.initial_capacity,
build_hasher,
self.weighter,
self.time_to_live,
self.time_to_idle,
self.invalidator_enabled,
Expand All @@ -114,14 +130,15 @@ where
self.max_capacity,
self.initial_capacity,
hasher,
self.weighter,
self.time_to_live,
self.time_to_idle,
self.invalidator_enabled,
)
}
}

impl<K, V> CacheBuilder<SegmentedCache<K, V, RandomState>>
impl<K, V> CacheBuilder<K, V, SegmentedCache<K, V, RandomState>>
where
K: Eq + Hash + Send + Sync + 'static,
V: Clone + Send + Sync + 'static,
Expand Down Expand Up @@ -163,7 +180,15 @@ where
}
}

impl<C> CacheBuilder<C> {
impl<K, V, C> CacheBuilder<K, V, C> {
/// Sets the max capacity of the cache.
pub fn max_capacity(self, max_capacity: usize) -> Self {
Self {
max_capacity: Some(max_capacity),
..self
}
}

/// Sets the initial capacity of the cache.
pub fn initial_capacity(self, capacity: usize) -> Self {
Self {
Expand All @@ -172,6 +197,14 @@ impl<C> CacheBuilder<C> {
}
}

/// Sets the weighter closure of the cache.
pub fn weighter(self, weighter: Weighter<K, V>) -> Self {
Self {
weighter: Some(weighter),
..self
}
}

/// Sets the time to live of the cache.
///
/// A cached entry will be expired after the specified duration past from
Expand Down

0 comments on commit 45ee2a9

Please sign in to comment.