Skip to content

Commit

Permalink
Support the plain LRU policy
Browse files Browse the repository at this point in the history
- Add the documentation for `EvictionPolicy`, etc.
- Change the `EvictionPolicy` from an enum to a struct, so that we can add more
  private fields in the future without breaking the API.
  • Loading branch information
tatsuya6502 committed Jan 28, 2024
1 parent b32f05b commit 34cc03b
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 16 deletions.
12 changes: 6 additions & 6 deletions src/future/base_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use crate::{
},
future::CancelGuard,
notification::{AsyncEvictionListener, RemovalCause},
policy::{EvictionPolicy, ExpirationPolicy},
policy::{EvictionPolicy, EvictionPolicyConfig, ExpirationPolicy},
sync_base::iter::ScanningGet,
Entry, Expiry, Policy, PredicateError,
};
Expand Down Expand Up @@ -1043,7 +1043,7 @@ pub(crate) struct Inner<K, V, S> {
read_op_ch: Receiver<ReadOp<K, V>>,
write_op_ch: Receiver<WriteOp<K, V>>,
maintenance_task_lock: RwLock<()>,
eviction_policy: EvictionPolicy,
eviction_policy: EvictionPolicyConfig,
expiration_policy: ExpirationPolicy<K, V>,
valid_after: AtomicInstant,
weigher: Option<Weigher<K, V>>,
Expand Down Expand Up @@ -1251,7 +1251,7 @@ where
read_op_ch,
write_op_ch,
maintenance_task_lock: RwLock::default(),
eviction_policy,
eviction_policy: eviction_policy.config,
expiration_policy,
valid_after: AtomicInstant::default(),
weigher,
Expand Down Expand Up @@ -1456,7 +1456,7 @@ where
.await;
}

if self.eviction_policy == EvictionPolicy::TinyLfu
if self.eviction_policy == EvictionPolicyConfig::TinyLfu
&& self.should_enable_frequency_sketch(&eviction_state.counters)
{
self.enable_frequency_sketch(&eviction_state.counters).await;
Expand Down Expand Up @@ -1754,12 +1754,12 @@ where

// Try to admit the candidate.
let admission_result = match &self.eviction_policy {
EvictionPolicy::TinyLfu => {
EvictionPolicyConfig::TinyLfu => {
let mut candidate = EntrySizeAndFrequency::new(new_weight);
candidate.add_frequency(freq, kh.hash);
Self::admit(&candidate, &self.cache, deqs, freq)
}
EvictionPolicy::Lru => AdmissionResult::Admitted {
EvictionPolicyConfig::Lru => AdmissionResult::Admitted {
victim_keys: SmallVec::default(),
},
};
Expand Down
6 changes: 6 additions & 0 deletions src/future/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,12 @@ impl<K, V, C> CacheBuilder<K, V, C> {
}
}

/// Sets the eviction (and admission) policy of the cache.
///
/// The default policy is TinyLFU. See [`EvictionPolicy`][eviction-policy] for
/// more details.
///
/// [eviction-policy]: ../policy/struct.EvictionPolicy.html
pub fn eviction_policy(self, policy: EvictionPolicy) -> Self {
Self {
eviction_policy: policy,
Expand Down
2 changes: 1 addition & 1 deletion src/future/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2335,7 +2335,7 @@ mod tests {
// Create a cache with the eviction listener.
let mut cache = Cache::builder()
.max_capacity(3)
.eviction_policy(EvictionPolicy::Lru)
.eviction_policy(EvictionPolicy::lru())
.async_eviction_listener(listener)
.build();
cache.reconfigure_for_testing().await;
Expand Down
71 changes: 69 additions & 2 deletions src/policy.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::{
fmt,
sync::Arc,
time::{Duration, Instant},
};
Expand Down Expand Up @@ -58,13 +59,79 @@ impl Policy {
}
}

/// The eviction (and admission) policy of a cache.
///
/// When the cache is full, the eviction/admission policy is used to determine which
/// items should be admitted to the cache and which cached items should be evicted.
/// The choice of a policy will directly affect the performance (hit rate) of the
/// cache.
///
/// The following policies are available:
///
/// - **TinyLFU** (default):
/// - Suitable for most workloads.
/// - TinyLFU combines the LRU eviction policy and an admission policy based on the
/// historical popularity of keys.
/// - Note that it tracks not only the keys currently in the cache, but all hit and
/// missed keys. The data structure used to _estimate_ the popularity of keys is
/// a modified Count-Min Sketch, which has a very low memory footprint (thus the
/// name "tiny").
/// - **LRU**:
/// - Suitable for some workloads with strong recency bias, such as streaming data
/// processing.
///
/// LFU stands for Least Frequently Used. LRU stands for Least Recently Used.
///
/// Use associate function [`EvictionPolicy::tiny_lfu`](#method.tiny_lfu) or
/// [`EvictionPolicy::lru`](#method.lru) to obtain an instance of `EvictionPolicy`.
#[derive(Clone, Default)]
pub struct EvictionPolicy {
pub(crate) config: EvictionPolicyConfig,
}

impl EvictionPolicy {
/// Returns the TinyLFU policy, which is suitable for most workloads.
///
/// TinyLFU is a combination of the LRU eviction policy and the admission policy
/// based on the historical popularity of keys.
///
/// Note that it tracks not only the keys currently in the cache, but all hit and
/// missed keys. The data structure used to _estimate_ the popularity of keys is
/// a modified Count-Min Sketch, which has a very low memory footprint (thus the
/// name "tiny").
pub fn tiny_lfu() -> Self {
Self {
config: EvictionPolicyConfig::TinyLfu,
}
}

/// Returns the LRU policy.
///
/// Suitable for some workloads with strong recency bias, such as streaming data
/// processing.
pub fn lru() -> Self {
Self {
config: EvictionPolicyConfig::Lru,
}
}
}

impl fmt::Debug for EvictionPolicy {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.config {
EvictionPolicyConfig::TinyLfu => write!(f, "EvictionPolicy::TinyLfu"),
EvictionPolicyConfig::Lru => write!(f, "EvictionPolicy::Lru"),
}
}
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum EvictionPolicy {
pub(crate) enum EvictionPolicyConfig {
TinyLfu,
Lru,
}

impl Default for EvictionPolicy {
impl Default for EvictionPolicyConfig {
fn default() -> Self {
Self::TinyLfu
}
Expand Down
6 changes: 6 additions & 0 deletions src/sync/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,12 @@ impl<K, V, C> CacheBuilder<K, V, C> {
}
}

/// Sets the eviction (and admission) policy of the cache.
///
/// The default policy is TinyLFU. See [`EvictionPolicy`][eviction-policy] for
/// more details.
///
/// [eviction-policy]: ../policy/struct.EvictionPolicy.html
pub fn eviction_policy(self, policy: EvictionPolicy) -> Self {
Self {
eviction_policy: policy,
Expand Down
2 changes: 1 addition & 1 deletion src/sync/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2037,7 +2037,7 @@ mod tests {
// Create a cache with the eviction listener.
let mut cache = Cache::builder()
.max_capacity(3)
.eviction_policy(EvictionPolicy::Lru)
.eviction_policy(EvictionPolicy::lru())
.eviction_listener(listener)
.build();
cache.reconfigure_for_testing();
Expand Down
12 changes: 6 additions & 6 deletions src/sync_base/base_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use crate::{
CacheRegion,
},
notification::{notifier::RemovalNotifier, EvictionListener, RemovalCause},
policy::{EvictionPolicy, ExpirationPolicy},
policy::{EvictionPolicy, EvictionPolicyConfig, ExpirationPolicy},
Entry, Expiry, Policy, PredicateError,
};

Expand Down Expand Up @@ -903,7 +903,7 @@ pub(crate) struct Inner<K, V, S> {
frequency_sketch_enabled: AtomicBool,
read_op_ch: Receiver<ReadOp<K, V>>,
write_op_ch: Receiver<WriteOp<K, V>>,
eviction_policy: EvictionPolicy,
eviction_policy: EvictionPolicyConfig,
expiration_policy: ExpirationPolicy<K, V>,
valid_after: AtomicInstant,
weigher: Option<Weigher<K, V>>,
Expand Down Expand Up @@ -1098,7 +1098,7 @@ where
frequency_sketch_enabled: AtomicBool::default(),
read_op_ch,
write_op_ch,
eviction_policy,
eviction_policy: eviction_policy.config,
expiration_policy,
valid_after: AtomicInstant::default(),
weigher,
Expand Down Expand Up @@ -1290,7 +1290,7 @@ where
self.apply_writes(&mut deqs, &mut timer_wheel, w_len, &mut eviction_state);
}

if self.eviction_policy == EvictionPolicy::TinyLfu
if self.eviction_policy == EvictionPolicyConfig::TinyLfu
&& self.should_enable_frequency_sketch(&eviction_state.counters)
{
self.enable_frequency_sketch(&eviction_state.counters);
Expand Down Expand Up @@ -1566,12 +1566,12 @@ where

// Try to admit the candidate.
let admission_result = match &self.eviction_policy {
EvictionPolicy::TinyLfu => {
EvictionPolicyConfig::TinyLfu => {
let mut candidate = EntrySizeAndFrequency::new(new_weight);
candidate.add_frequency(freq, kh.hash);
Self::admit(&candidate, &self.cache, deqs, freq)
}
EvictionPolicy::Lru => AdmissionResult::Admitted {
EvictionPolicyConfig::Lru => AdmissionResult::Admitted {
victim_keys: SmallVec::default(),
},
};
Expand Down

0 comments on commit 34cc03b

Please sign in to comment.