diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 738c17e..b264aac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,7 +63,7 @@ jobs: - name: Install cargo-hack run: cargo install cargo-hack - name: Apply clippy lints - run: cargo hack clippy --each-feature + run: cargo hack clippy --each-feature --include-features sync,async --exclude-no-default-features # Run tests on some extra platforms cross: @@ -80,7 +80,6 @@ jobs: - x86_64-pc-windows-gnu - i686-unknown-linux-gnu - powerpc64-unknown-linux-gnu - - mips64-unknown-linux-gnuabi64 - riscv64gc-unknown-linux-gnu runs-on: ubuntu-latest steps: @@ -134,7 +133,7 @@ jobs: path: ~/.cargo key: ${{ runner.os }}-coverage-dotcargo - name: Run build - run: cargo hack build --feature-powerset + run: cargo hack build --feature-powerset --include-features sync,async --exclude-no-default-features test: name: test @@ -168,63 +167,63 @@ jobs: path: ~/.cargo key: ${{ runner.os }}-coverage-dotcargo - name: Run test - run: cargo hack test --feature-powerset + run: cargo hack test --feature-powerset --include-features sync,async --exclude-no-default-features - sanitizer: - name: sanitizer - strategy: - matrix: - os: - - ubuntu-latest - - macos-latest - - windows-latest - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v3 - - name: Cache cargo build and registry - uses: actions/cache@v3 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-sanitizer-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-sanitizer- - - name: Install Rust - run: rustup update $nightly && rustup default $nightly - - name: Install rust-src - run: rustup component add rust-src - - name: Install cargo-hack - run: cargo install cargo-hack - - name: ASAN / LSAN / TSAN - run: ci/sanitizer.sh + # sanitizer: + # name: sanitizer + # strategy: + # matrix: + # os: + # - ubuntu-latest + # - macos-latest + # - windows-latest + # runs-on: ${{ matrix.os }} + # steps: + # - uses: actions/checkout@v3 + # - name: Cache cargo build and registry + # uses: actions/cache@v3 + # with: + # path: | + # ~/.cargo/registry + # ~/.cargo/git + # target + # key: ${{ runner.os }}-sanitizer-${{ hashFiles('**/Cargo.lock') }} + # restore-keys: | + # ${{ runner.os }}-sanitizer- + # - name: Install Rust + # run: rustup update $nightly && rustup default $nightly + # - name: Install rust-src + # run: rustup component add rust-src + # - name: Install cargo-hack + # run: cargo install cargo-hack + # - name: ASAN / LSAN / TSAN + # run: ci/sanitizer.sh - miri: - name: miri - strategy: - matrix: - os: - - ubuntu-latest - - macos-latest - - windows-latest - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v3 - - name: Cache cargo build and registry - uses: actions/cache@v3 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-miri-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-miri- - - name: Install cargo-hack - run: cargo install cargo-hack - - name: Miri - run: ci/miri.sh + # miri: + # name: miri + # strategy: + # matrix: + # os: + # - ubuntu-latest + # - macos-latest + # - windows-latest + # runs-on: ${{ matrix.os }} + # steps: + # - uses: actions/checkout@v3 + # - name: Cache cargo build and registry + # uses: actions/cache@v3 + # with: + # path: | + # ~/.cargo/registry + # ~/.cargo/git + # target + # key: ${{ runner.os }}-miri-${{ hashFiles('**/Cargo.lock') }} + # restore-keys: | + # ${{ runner.os }}-miri- + # - name: Install cargo-hack + # run: cargo install cargo-hack + # - name: Miri + # run: ci/miri.sh # # valgrind # valgrind: @@ -296,8 +295,8 @@ jobs: - build - cross - test - - sanitizer - - miri + # - sanitizer + # - miri - docs steps: - uses: actions/checkout@v3 diff --git a/Cargo.toml b/Cargo.toml index 2f549e7..ad77bac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,6 @@ async-channel = { version = "1.8", optional = true } async-io = { version = "1.12", optional = true } crossbeam-channel = { version = "0.5", optional = true } futures = { version = "0.3", optional = true } -log = { version = "0.4", optional = true } parking_lot = "0.12" rand = "0.8" serde = { version = "1", optional = true, features = ["derive"] } diff --git a/README-zh_hans.md b/README-zh_hans.md index f273932..82753d0 100644 --- a/README-zh_hans.md +++ b/README-zh_hans.md @@ -9,16 +9,16 @@ [English](README.md) | 简体中文 -[github][Github-url] -[Build][CI-url] +[github][Github-url] +[Build][CI-url] [codecov][codecov-url] [docs.rs][doc-url] [crates.io][crates-url] -[rustc][rustc-url] +[crates.io][crates-url] + +license -[license-apache][license-apache-url] -[license-mit][license-mit-url] diff --git a/README.md b/README.md index 1b1a310..ae367fb 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ A high performance thread-safe memory-bound Rust cache. English | [简体中文](README-zh_hans.md) -[github][Github-url] +[github][Github-url] [Build][CI-url] [codecov][codecov-url] @@ -17,8 +17,8 @@ English | [简体中文](README-zh_hans.md) [crates.io][crates-url] [crates.io][crates-url] -[license-apache][license-apache-url] -[license-mit][license-mit-url] +license + diff --git a/ci/miri.sh b/ci/miri.sh index 7ea1a2c..6e0637f 100755 --- a/ci/miri.sh +++ b/ci/miri.sh @@ -7,5 +7,5 @@ cargo miri setup export MIRIFLAGS="-Zmiri-strict-provenance -Zmiri-disable-isolation -Zmiri-symbolic-alignment-check" -cargo hack miri test --each-feature +cargo hack miri test --each-feature --include-features sync,async --exclude-no-default-features diff --git a/ci/sanitizer.sh b/ci/sanitizer.sh index a21beb3..0acecdc 100755 --- a/ci/sanitizer.sh +++ b/ci/sanitizer.sh @@ -6,12 +6,12 @@ export ASAN_OPTIONS="detect_odr_violation=0 detect_leaks=0" # Run address sanitizer with cargo-hack RUSTFLAGS="-Z sanitizer=address" \ -cargo hack test --lib --each-feature +cargo hack test --lib --each-feature --include-features sync,async --exclude-no-default-features # Run leak sanitizer with cargo-hack RUSTFLAGS="-Z sanitizer=leak" \ -cargo hack test --lib --each-feature +cargo hack test --lib --each-feature --include-features sync,async --exclude-no-default-features # Run thread sanitizer with cargo-hack RUSTFLAGS="-Z sanitizer=thread" \ -cargo hack -Zbuild-std test --lib --each-feature +cargo hack -Zbuild-std test --lib --each-feature --include-features sync,async --exclude-no-default-features diff --git a/ci/test-stable.sh b/ci/test-stable.sh deleted file mode 100755 index 2b95e2a..0000000 --- a/ci/test-stable.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -set -ex - -cmd="${1:-test}" - -# Run with all features -cargo "${cmd}" --all-features - -cargo doc --no-deps --all-features - -if [[ "${RUST_VERSION}" == "nightly"* ]]; then - # Check benchmarks - cargo check --benches - - # Check minimal versions - cargo clean - cargo update -Zminimal-versions - cargo check --all-features -fi \ No newline at end of file diff --git a/src/cache.rs b/src/cache.rs index dfd6d0d..0761dc3 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -198,6 +198,7 @@ macro_rules! impl_builder { }; } +#[cfg(feature = "sync")] macro_rules! impl_cache { ($cache: ident, $builder: ident, $item: ident) => { use crate::store::UpdateResult; diff --git a/src/cache/builder.rs b/src/cache/builder.rs index c79d218..4547d83 100644 --- a/src/cache/builder.rs +++ b/src/cache/builder.rs @@ -176,7 +176,7 @@ where /// This is a fine-tuning mechanism and you probably won't have to touch this. #[inline] pub fn set_buffer_size(mut self, sz: usize) -> Self { - self.insert_buffer_size = sz; + self.insert_buffer_size = sz; self } diff --git a/src/lib.rs b/src/lib.rs index 0292209..2c5fe57 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,175 +1,7 @@ -//!
-//!

Stretto

-//!
-//!
-//! //! Stretto is a pure Rust implementation for . //! //! A high performance thread-safe memory-bound Rust cache. //! -//! English | [简体中文](https://github.com/al8n/stretto/blob/main/README-zh_CN.md) -//! -//! [github][Github-url] -//! [Build][CI-url] -//! [codecov][codecov-url] -//! -//! [docs.rs][doc-url] -//! [crates.io][crates-url] -//! [rustc][rustc-url] -//! -//! [license-apache][license-apache-url] -//! [license-mit][license-mit-url] -//! -//!
-//! -//! ## Features -//! * **Internal Mutability** - Do not need to use `Arc>` for concurrent code, you just need `Cache<...>` or `AsyncCache<...>` -//! * **Sync and Async** - Stretto support sync and runtime agnostic async. -//! * In sync, [`Cache`] starts two extra OS level threads. One is policy thread, the other is writing thread. -//! * In async, [`AsyncCache`] starts two extra green threads. One is policy thread, the other is writing thread. -//! * **Store policy** Stretto only store the value, which means the cache does not store the key. -//! * **High Hit Ratios** - with Dgrpah's developers unique admission/eviction policy pairing, Stretto's performance is best in class. -//! * **Eviction: SampledLFU** - on par with exact LRU and better performance on Search and Database traces. -//! * **Admission: TinyLFU** - extra performance with little memory overhead (12 bits per counter). -//! * **Fast Throughput** - use a variety of techniques for managing contention and the result is excellent throughput. -//! * **Cost-Based Eviction** - any large new item deemed valuable can evict multiple smaller items (cost could be anything). -//! * **Fully Concurrent** - you can use as many threads as you want with little throughput degradation. -//! * **Metrics** - optional performance metrics for throughput, hit ratios, and other stats. -//! * **Simple API** - just figure out your ideal [`CacheBuilder`] values and you're off and running. -//! -//! ## Table of Contents -//! -//! * [Usage](#Usage) -//! * [Example](#Example) -//! * [Sync](#Sync) -//! * [Async](#Async) -//! * [Config](#Config) -//! * [num_counters](#num_counters) -//! * [max_cost](#max_cost) -//! * [key_builder](#key_builder) -//! * [buffer_size](#buffer_size) -//! * [metrics](#metrics) -//! * [ignore_internal_cost](#ignore_internal_cost) -//! * [cleanup_duration](#cleanup_duration) -//! * [update_validator](#update_validator) -//! * [callback](#callback) -//! * [coster](#coster) -//! * [hasher](#hasher) -//! -//! ## Usage -//! ### Example -//! Please see [examples](https://github.com/al8n/stretto/tree/main/examples) on github. -//! -//! ### Config -//! The [`CacheBuilder`] or [`AsyncCacheBuilder`] struct is used when creating [`Cache`]/[`AsyncCache`] instances if you want to customize the [`Cache`]/[`AsyncCache`] settings. -//! -//! #### num_counters -//! -//! `num_counters` is the number of 4-bit access counters to keep for admission and eviction. -//! Dgraph's developers have seen good performance in setting this to 10x the number of -//! items you expect to keep in the cache when full. -//! -//! For example, if you expect each item to have a cost of 1 and `max_cost` is 100, set `num_counters` to 1,000. -//! Or, if you use variable cost values but expect the cache to hold around 10,000 items when full, -//! set num_counters to 100,000. The important thing is the *number of unique items* in the full cache, -//! not necessarily the `max_cost` value. -//! -//! #### max_cost -//! -//! `max_cost` is how eviction decisions are made. For example, -//! if max_cost is 100 and a new item with a cost of 1 increases total cache cost to 101, -//! 1 item will be evicted. -//! -//! `max_cost` can also be used to denote the max size in bytes. For example, -//! if max_cost is 1,000,000 (1MB) and the cache is full with 1,000 1KB items, -//! a new item (that's accepted) would cause 5 1KB items to be evicted. -//! -//! `max_cost` could be anything as long as it matches how you're using the cost values when calling `insert`. -//! -//! #### key_builder -//! -//! [`KeyBuilder`] is the hashing algorithm used for every key. In Stretto, the Cache will never store the real key. -//! The key will be processed by [`KeyBuilder`]. Stretto has two default built-in key builder, -//! one is [`TransparentKeyBuilder`], the other is [`DefaultKeyBuilder`]. If your key implements [`TransparentKey`] trait, -//! you can use [`TransparentKeyBuilder`] which is faster than [`DefaultKeyBuilder`]. Otherwise, you should use [`DefaultKeyBuilder`] -//! You can also write your own key builder for the Cache, by implementing [`KeyBuilder`] trait. -//! -//! Note that if you want 128bit hashes you should use the full `(u64, u64)`, -//! otherwise just fill the `u64` at the `0` position, and it will behave like -//! any 64bit hash. -//! -//! #### buffer_size -//! -//! `buffer_size` is the size of the insert buffers. The Dgraph's developers find that 32 * 1024 gives a good performance. -//! -//! If for some reason you see insert performance decreasing with lots of contention (you shouldn't), -//! try increasing this value in increments of 32 * 1024. -//! This is a fine-tuning mechanism and you probably won't have to touch this. -//! -//! #### metrics -//! -//! Metrics is true when you want real-time logging of a variety of stats. -//! The reason this is a CacheBuilder flag is because there's a 10% throughput performance overhead. -//! -//! #### ignore_internal_cost -//! -//! Set to true indicates to the cache that the cost of -//! internally storing the value should be ignored. This is useful when the -//! cost passed to set is not using bytes as units. Keep in mind that setting -//! this to true will increase the memory usage. -//! -//! #### cleanup_duration -//! -//! The Cache will cleanup the expired values every 500ms by default. -//! -//! #### update_validator -//! -//! By default, the Cache will always update the value if the value already exists in the cache. -//! [`UpdateValidator`] is a trait to support customized update policy (check if the value should be updated -//! if the value already exists in the cache). -//! -//! #### callback -//! -//! [`CacheCallback`] is for customize some extra operations on values when related event happens. -//! -//! #### coster -//! -//! [`Coster`] is a trait you can pass to the [`CacheBuilder`] in order to evaluate -//! item cost at runtime, and only for the [`insert`] calls that aren't dropped (this is -//! useful if calculating item cost is particularly expensive, and you don't want to -//! waste time on items that will be dropped anyways). -//! -//! To signal to Stretto that you'd like to use this [`Coster`] trait: -//! -//! 1. Set the [`Coster`] field to your own [`Coster`] implementation. -//! 2. When calling [`insert`] for new items or item updates, use a cost of 0. -//! -//! #### hasher -//! -//! The hasher for the Cache, default is SipHasher. -//! -//! [Github-url]: https://github.com/al8n/stretto/ -//! [CI-url]: https://github.com/al8n/stretto/actions/workflows/ci.yml -//! [doc-url]: https://docs.rs/stretto -//! [crates-url]: https://crates.io/crates/stretto -//! [codecov-url]: https://app.codecov.io/gh/al8n/stretto/ -//! [license-url]: https://opensource.org/licenses/Apache-2.0 -//! [license-apache-url]: https://opensource.org/licenses/Apache-2.0 -//! [license-mit-url]: https://opensource.org/licenses/MIT -//! [rustc-image]: https://img.shields.io/badge/rustc-1.52.0--nightly%2B-orange.svg?style=for-the-badge&logo=Rust -//! [rustc-url]: https://github.com/rust-lang/rust/blob/master/RELEASES.md -//! [`KeyBuilder`]: trait.KeyBuilder.html -//! [`TransparentKey`]: trait.TransparentKey.html -//! [`TransparentKeyBuilder`]: struct.TransparentKeyBuilder.html -//! [`DefaultKeyBuilder`]: struct.DefaultKeyBuilder.html -//! [`CacheBuilder`]: struct.CacheBuilder.html -//! [`AsyncCacheBuilder`]: struct.AsyncCacheBuilder.html -//! [`insert`]: struct.Cache.html#method.insert -//! [`UpdateValidator`]: trait.UpdateValidator.html -//! [`CacheCallback`]: trait.CacheCallback.html -//! [`Coster`]: trait.Coster.html -//! [`Cache`]: struct.Cache.html -//! [`AsyncCache`]: struct.AsyncCache.html #![deny(missing_docs)] #![allow(clippy::too_many_arguments, clippy::type_complexity)] #![cfg_attr(docsrs, feature(doc_cfg))] diff --git a/src/metrics.rs b/src/metrics.rs index e441260..91b3ed8 100644 --- a/src/metrics.rs +++ b/src/metrics.rs @@ -421,7 +421,7 @@ impl MetricsInner { #[cfg(feature = "serde")] #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] -use serde::ser::{Error, SerializeStruct}; +use serde::ser::SerializeStruct; #[cfg(feature = "serde")] #[cfg_attr(docsrs, doc(cfg(feature = "serde")))] use serde::{Serialize, Serializer}; @@ -461,7 +461,6 @@ impl Serialize for MetricsInner { } } -#[cfg(not(feature = "serde"))] impl Display for MetricsInner { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let mut buf = Vec::new(); @@ -482,14 +481,6 @@ impl Display for MetricsInner { } } -#[cfg(feature = "serde")] -impl Display for MetricsInner { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let str = serde_json::to_string_pretty(self).map_err(std::fmt::Error::custom)?; - write!(f, "Metrics::Op {}", str) - } -} - fn new_histogram_bound() -> Vec { (1..=HISTOGRAM_BOUND_SIZE as u64) .map(|idx| (1 << idx) as f64) @@ -541,15 +532,6 @@ mod test { } #[test] - #[cfg(all(feature = "serde", feature = "serde_json"))] - fn test_display() { - let m = MetricsInner::new(); - let ms = serde_json::to_string_pretty(&m).unwrap(); - assert_eq!(format!("{}", m), format!("Metrics::Op {}", ms)); - } - - #[test] - #[cfg(not(all(feature = "serde", feature = "serde_json")))] fn test_display() { let m = MetricsInner::new(); let exp = "Metrics::Op { diff --git a/src/policy/test.rs b/src/policy/test.rs index b1fdf34..2206622 100644 --- a/src/policy/test.rs +++ b/src/policy/test.rs @@ -1,166 +1,172 @@ use crate::metrics::Metrics; -use crate::policy::{LFUPolicy, SampledLFU, TinyLFU}; +use crate::policy::{SampledLFU, TinyLFU}; use std::collections::hash_map::RandomState; -use std::sync::Arc; + use std::time::Duration; static WAIT: Duration = Duration::from_millis(100); -use std::thread::sleep; +#[cfg(feature = "sync")] +mod sync_test { + use super::*; + use crate::policy::LFUPolicy; + use std::sync::Arc; + use std::thread::sleep; -#[test] -fn test_policy() { - let _ = LFUPolicy::new(100, 10); -} + #[test] + fn test_policy() { + let _ = LFUPolicy::new(100, 10); + } -#[test] -fn test_policy_metrics() { - let mut p = LFUPolicy::new(100, 10).unwrap(); - p.collect_metrics(Arc::new(Metrics::new_op())); - assert!(p.metrics.is_op()); - assert!(p.inner.lock().costs.metrics.is_op()); -} + #[test] + fn test_policy_metrics() { + let mut p = LFUPolicy::new(100, 10).unwrap(); + p.collect_metrics(Arc::new(Metrics::new_op())); + assert!(p.metrics.is_op()); + assert!(p.inner.lock().costs.metrics.is_op()); + } -#[test] -fn test_policy_process_items() { - let p = LFUPolicy::new(100, 10).unwrap(); - p.items_tx.send(vec![1, 2, 2]).unwrap(); - sleep(WAIT); - let inner = p.inner.lock(); - assert_eq!(inner.admit.estimate(2), 2); - assert_eq!(inner.admit.estimate(1), 1); - drop(inner); - - p.stop_tx.send(()).unwrap(); - sleep(WAIT); - assert!(p.push(vec![3, 3, 3]).is_err()); - let inner = p.inner.lock(); - assert_eq!(inner.admit.estimate(3), 0); -} + #[test] + fn test_policy_process_items() { + let p = LFUPolicy::new(100, 10).unwrap(); + p.items_tx.send(vec![1, 2, 2]).unwrap(); + sleep(WAIT); + let inner = p.inner.lock(); + assert_eq!(inner.admit.estimate(2), 2); + assert_eq!(inner.admit.estimate(1), 1); + drop(inner); -#[test] -fn test_policy_push() { - let p = LFUPolicy::new(100, 10).unwrap(); - assert!(p.push(vec![]).unwrap()); - - let mut keep_count = 0; - (0..10).for_each(|_| { - if p.push(vec![1, 2, 3, 4, 5]).unwrap() { - keep_count += 1; - } - }); + p.stop_tx.send(()).unwrap(); + sleep(WAIT); + assert!(p.push(vec![3, 3, 3]).is_err()); + let inner = p.inner.lock(); + assert_eq!(inner.admit.estimate(3), 0); + } - assert_ne!(0, keep_count); -} + #[test] + fn test_policy_push() { + let p = LFUPolicy::new(100, 10).unwrap(); + assert!(p.push(vec![]).unwrap()); -#[test] -fn test_policy_add() { - let p = LFUPolicy::new(1000, 100).unwrap(); - let (victims, added) = p.add(1, 101); - assert!(victims.is_none()); - assert!(!added); - - let mut inner = p.inner.lock(); - inner.costs.increment(1, 1); - inner.admit.increment(1); - inner.admit.increment(2); - inner.admit.increment(3); - drop(inner); - - let (victims, added) = p.add(1, 1); - assert!(victims.is_none()); - assert!(!added); - - let (victims, added) = p.add(2, 20); - assert!(victims.is_none()); - assert!(added); - - let (victims, added) = p.add(3, 90); - assert!(victims.is_some()); - assert!(added); - - let (victims, added) = p.add(4, 20); - assert!(victims.is_some()); - assert!(!added); -} + let mut keep_count = 0; + (0..10).for_each(|_| { + if p.push(vec![1, 2, 3, 4, 5]).unwrap() { + keep_count += 1; + } + }); -#[test] -fn test_policy_has() { - let p = LFUPolicy::new(100, 10).unwrap(); - p.add(1, 1); - assert!(p.contains(&1)); - assert!(!p.contains(&2)); -} + assert_ne!(0, keep_count); + } -#[test] -fn test_policy_del() { - let p = LFUPolicy::new(100, 10).unwrap(); - p.add(1, 1); - p.remove(&1); - p.remove(&2); - assert!(!p.contains(&1)); - assert!(!p.contains(&2)); -} + #[test] + fn test_policy_add() { + let p = LFUPolicy::new(1000, 100).unwrap(); + let (victims, added) = p.add(1, 101); + assert!(victims.is_none()); + assert!(!added); -#[test] -fn test_policy_cap() { - let p = LFUPolicy::new(100, 10).unwrap(); - p.add(1, 1); - assert_eq!(p.cap(), 9); -} + let mut inner = p.inner.lock(); + inner.costs.increment(1, 1); + inner.admit.increment(1); + inner.admit.increment(2); + inner.admit.increment(3); + drop(inner); -#[test] -fn test_policy_update() { - let p = LFUPolicy::new(100, 10).unwrap(); - p.add(1, 1); - p.update(&1, 2); - let inner = p.inner.lock(); - assert_eq!(inner.costs.key_costs.get(&1).unwrap(), &2); -} + let (victims, added) = p.add(1, 1); + assert!(victims.is_none()); + assert!(!added); -#[test] -fn test_policy_cost() { - let p = LFUPolicy::new(100, 10).unwrap(); - p.add(1, 2); - assert_eq!(p.cost(&1), 2); - assert_eq!(p.cost(&2), -1); -} + let (victims, added) = p.add(2, 20); + assert!(victims.is_none()); + assert!(added); -#[test] -fn test_policy_clear() { - let p = LFUPolicy::new(100, 10).unwrap(); - p.add(1, 1); - p.add(2, 2); - p.add(3, 3); - p.clear(); - - assert_eq!(p.cap(), 10); - assert!(!p.contains(&2)); - assert!(!p.contains(&2)); - assert!(!p.contains(&3)); -} + let (victims, added) = p.add(3, 90); + assert!(victims.is_some()); + assert!(added); -#[test] -fn test_policy_close() { - let p = LFUPolicy::new(100, 10).unwrap(); - p.add(1, 1); - let _ = p.close(); - sleep(WAIT); - assert!(p.items_tx.send(vec![1]).is_err()) -} + let (victims, added) = p.add(4, 20); + assert!(victims.is_some()); + assert!(!added); + } -#[test] -fn test_policy_push_after_close() { - let p = LFUPolicy::new(100, 10).unwrap(); - let _ = p.close(); - assert!(!p.push(vec![1, 2]).unwrap()); -} + #[test] + fn test_policy_has() { + let p = LFUPolicy::new(100, 10).unwrap(); + p.add(1, 1); + assert!(p.contains(&1)); + assert!(!p.contains(&2)); + } -#[test] -fn test_policy_add_after_close() { - let p = LFUPolicy::new(100, 10).unwrap(); - let _ = p.close(); - p.add(1, 1); + #[test] + fn test_policy_del() { + let p = LFUPolicy::new(100, 10).unwrap(); + p.add(1, 1); + p.remove(&1); + p.remove(&2); + assert!(!p.contains(&1)); + assert!(!p.contains(&2)); + } + + #[test] + fn test_policy_cap() { + let p = LFUPolicy::new(100, 10).unwrap(); + p.add(1, 1); + assert_eq!(p.cap(), 9); + } + + #[test] + fn test_policy_update() { + let p = LFUPolicy::new(100, 10).unwrap(); + p.add(1, 1); + p.update(&1, 2); + let inner = p.inner.lock(); + assert_eq!(inner.costs.key_costs.get(&1).unwrap(), &2); + } + + #[test] + fn test_policy_cost() { + let p = LFUPolicy::new(100, 10).unwrap(); + p.add(1, 2); + assert_eq!(p.cost(&1), 2); + assert_eq!(p.cost(&2), -1); + } + + #[test] + fn test_policy_clear() { + let p = LFUPolicy::new(100, 10).unwrap(); + p.add(1, 1); + p.add(2, 2); + p.add(3, 3); + p.clear(); + + assert_eq!(p.cap(), 10); + assert!(!p.contains(&2)); + assert!(!p.contains(&2)); + assert!(!p.contains(&3)); + } + + #[test] + fn test_policy_close() { + let p = LFUPolicy::new(100, 10).unwrap(); + p.add(1, 1); + let _ = p.close(); + sleep(WAIT); + assert!(p.items_tx.send(vec![1]).is_err()) + } + + #[test] + fn test_policy_push_after_close() { + let p = LFUPolicy::new(100, 10).unwrap(); + let _ = p.close(); + assert!(!p.push(vec![1, 2]).unwrap()); + } + + #[test] + fn test_policy_add_after_close() { + let p = LFUPolicy::new(100, 10).unwrap(); + let _ = p.close(); + p.add(1, 1); + } } #[cfg(feature = "async")] diff --git a/src/ring.rs b/src/ring.rs index d260457..4fc2da2 100644 --- a/src/ring.rs +++ b/src/ring.rs @@ -4,14 +4,17 @@ use parking_lot::Mutex; #[cfg(feature = "async")] use crate::policy::AsyncLFUPolicy; +#[cfg(feature = "sync")] use crate::policy::LFUPolicy; +#[cfg(feature = "sync")] pub struct RingStripe { cons: Arc>, data: Mutex>, capa: usize, } +#[cfg(feature = "sync")] impl RingStripe where S: BuildHasher + Clone + 'static, diff --git a/src/store.rs b/src/store.rs index b327ebc..610ac3d 100644 --- a/src/store.rs +++ b/src/store.rs @@ -1,4 +1,3 @@ -use crate::policy::LFUPolicy; use crate::ttl::{ExpirationMap, Time}; use crate::utils::{change_lifetime_const, SharedValue, ValueRef, ValueRefMut}; use crate::{CacheError, DefaultUpdateValidator, Item as CrateItem, UpdateValidator}; @@ -238,9 +237,10 @@ impl< .map(|val| val.expiration) } + #[cfg(feature = "sync")] pub fn try_cleanup( &self, - policy: Arc>, + policy: Arc>, ) -> Result>, CacheError> { let now = Time::now(); Ok(self