This is a Rust implementation for HashiCorp's golang-lru. This crate contains three LRU based cache, LRUCache, TwoQueueCache and AdaptiveCache.

Overview

Caches

github Build codecov

docs.rs crates.io license-apache license-mit

This is a Rust implementation for HashiCorp's golang-lru.

This crate contains three LRU based cache, LRUCache, TwoQueueCache and AdaptiveCache.

See Introduction, Trade-Off and Usages for more details.

English | 简体中文

Introduction

The MSRV for this crate is 1.55.0.

LRU based caches are O(1) for read, write and delete.

  • LRUCache or RawLRU is a fixed size LRU cache.

  • AdaptiveCache is a fixed size Adaptive Replacement Cache (ARC). ARC is an enhancement over the standard LRU cache in that tracks both frequency and recency of use. This avoids a burst in access to new entries from evicting the frequently used older entries.

  • TwoQueueCache is a fixed size 2Q cache. 2Q is an enhancement over the standard LRU cache in that it tracks both frequently and recently used entries separately.

Trade-Off

In theory, AdaptiveCache and TwoQueueCache add some additional tracking overhead to a LRUCache cache, computationally it is roughly 2x the cost, and the extra memory overhead is linear with the size of the cache. AdaptiveCache and TwoQueueCache have similar computationally cost, which has been patented by IBM, but the TwoQueueCache (2Q) need to set reasonable parameters.

However, the implementation for the RawLRU uses Box and a raw pointer for each entry to break the limitation of the Rust language (It does not use Rc, because Rc is slower). Thus, in practice, TwoQueueCache is 2.5x computationally slower than LRUCache and AdaptiveCache is 3x computationally slower than LRUCache, because TwoQueueCache and AdaptiveCache has to do more box and re-box than LRUCache, even though I try my best to avoid boxing and re-boxing and use mem::swap to avoid memory allocating and deallocating.

Hence, it is better to understand what is the situation is (your project wants a cache with a higher hit ratio or faster computationally performance), and then choose the reasonable Cache in your project.

(For more performance details, you can clone the project and run cargo bench. The source code for benchmark is in the benches, I am also looking forward to anyone's help for writing more reasonable test cases for benchmark).

Usages

RawLRU and LRUCache

RawLRU is the basic data structure over the crate, it has a generic type E: OnEvictCallback, which support users to write and apply their own callback policy.

LRUCache is a type alias for RawLRU , so it does not support custom the on_evict callback.

More methods and examples, please see documents.

Default No-op Callback

Use RawLRU with default noop callback.

use hashicorp_lru::{RawLRU, PutResult};
  
fn main() {
    let mut cache = RawLRU::new(2).unwrap();
    // fill the cache
    assert_eq!(cache.put(1, 1), PutResult::Put);
    assert_eq!(cache.put(2, 2), PutResult::Put);
      
    // put 3, should evict the entry (1, 1)
    assert_eq!(cache.put(3, 3), PutResult::Evicted {key: 1,value: 1});
      
    // put 4, should evict the entry (2, 2)
    assert_eq!(cache.put(4, 4), PutResult::Evicted {key: 2,value: 2});
      
    // get 3, should update the recent-ness
    assert_eq!(cache.get(&3), Some(&3));
      
    // put 5, should evict the entry (4, 4)
    assert_eq!(cache.put(5, 5), PutResult::Evicted {key: 4,value: 4});
}

Custom Callback

Use RawLRU with a custom callback.

use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
use hashicorp_lru::{OnEvictCallback, RawLRU, PutResult};

// EvictedCounter is a callback which is used to record the number of evicted entries.
struct EvictedCounter {
    ctr: Arc<AtomicU64>,
}

impl EvictedCounter {
    pub fn new(ctr: Arc
   ) -> 
   Self {
        
   Self {
            ctr,
        }
    }
}


   impl 
   OnEvictCallback 
   for 
   EvictedCounter {
    
   fn 
   on_evict
   
    (
    &
    self, _: 
    &K, _: 
    &V) {
        
    self.ctr.
    fetch_add(
    1, Ordering
    ::SeqCst);
    }
}
   

    fn 
    main() {
    
    let counter 
    = Arc
    ::
    new(AtomicU64
    ::
    new(
    0));
       
    
    let 
    mut cache: RawLRU
    <
    u64, 
    u64, EvictedCounter
    > 
    = RawLRU
    ::
    with_on_evict_cb(
    2, EvictedCounter
    ::
    new(counter.
    clone())).
    unwrap();
       
    
    // fill the cache
    
    assert_eq!(cache.
    put(
    1, 
    1), PutResult
    ::Put);
    
    assert_eq!(cache.
    put(
    2, 
    2), PutResult
    ::Put);
       
    
    // put 3, should evict the entry (1, 1)
    
    assert_eq!(cache.
    put(
    3, 
    3), PutResult
    ::Evicted {key: 
    1,value: 
    1});
       
    
    // put 4, should evict the entry (2, 2)
    
    assert_eq!(cache.
    put(
    4, 
    4), PutResult
    ::Evicted {key: 
    2,value: 
    2});
       
    
    // get 3, should update the recent-ness
    
    assert_eq!(cache.
    get(
    &
    3), 
    Some(
    &
    3));
       
    
    // put 5, should evict the entry (4, 4)
    
    assert_eq!(cache.
    put(
    5, 
    5), PutResult
    ::Evicted {key: 
    4,value: 
    4});
       
    
    assert_eq!(counter.
    load(Ordering
    ::SeqCst), 
    3); 
}
   
  

AdaptiveCache (Adaptive Replacement Cache)

More methods and examples, please see documents.

use hashicorp_lru::AdaptiveCache;
 
fn main() {
    let mut cache = AdaptiveCache::new(4).unwrap();
     
    // fill recent
    (0..4).for_each(|i| cache.put(i, i));
     
    // move to frequent
    cache.get(&0);
    cache.get(&1);
    assert_eq!(cache.frequent_len(), 2);
     
    // evict from recent
    cache.put(4, 4);
    assert_eq!(cache.recent_evict_len(), 1);
     
    // current state
    // recent:          (MRU) [4, 3] (LRU)
    // frequent:        (MRU) [1, 0] (LRU)
    // recent evict:    (MRU) [2] (LRU)
    // frequent evict:  (MRU) [] (LRU)
     
    // Add 2, should cause hit on recent_evict
    cache.put(2, 2);
    assert_eq!(cache.recent_evict_len(), 1);
    assert_eq!(cache.partition(), 1);
    assert_eq!(cache.frequent_len(), 3);
     
    // Current state
    // recent LRU:      (MRU) [4] (LRU)
    // frequent LRU:    (MRU) [2, 1, 0] (LRU)
    // recent evict:    (MRU) [3] (LRU)
    // frequent evict:  (MRU) [] (LRU)
     
    // Add 4, should migrate to frequent
    cache.put(4, 4);
    assert_eq!(cache.recent_len(), 0);
    assert_eq!(cache.frequent_len(), 4);
     
    // Current state
    // recent LRU:      (MRU) [] (LRU)
    // frequent LRU:    (MRU) [4, 2, 1, 0] (LRU)
    // recent evict:    (MRU) [3] (LRU)
    // frequent evict:  (MRU) [] (LRU)
     
    // Add 5, should evict to b2
    cache.put(5, 5);
    assert_eq!(cache.recent_len(), 1);
    assert_eq!(cache.frequent_len(), 3);
    assert_eq!(cache.frequent_evict_len(), 1);
     
    // Current state
    // recent:          (MRU) [5] (LRU)
    // frequent:        (MRU) [4, 2, 1] (LRU)
    // recent evict:    (MRU) [3] (LRU)
    // frequent evict:  (MRU) [0] (LRU)
     
    // Add 0, should decrease p
    cache.put(0, 0);
    assert_eq!(cache.recent_len(), 0);
    assert_eq!(cache.frequent_len(), 4);
    assert_eq!(cache.recent_evict_len(), 2);
    assert_eq!(cache.frequent_evict_len(), 0);
    assert_eq!(cache.partition(), 0);
     
    // Current state
    // recent:         (MRU) [] (LRU)
    // frequent:       (MRU) [0, 4, 2, 1] (LRU)
    // recent evict:   (MRU) [5, 3] (LRU)
    // frequent evict: (MRU) [0] (LRU)
}

TwoQueueCache

More methods and examples, please see documents.

use hashicorp_lru::{TwoQueueCache, PutResult};

fn main() {
    let mut cache = TwoQueueCache::new(4).unwrap();
     
    // Add 1,2,3,4,
    (1..=4).for_each(|i| { assert_eq!(cache.put(i, i), PutResult::Put);});
     
    // Add 5 -> Evict 1 to ghost LRU
    assert_eq!(cache.put(5, 5), PutResult::Put);
     
    // Pull in the recently evicted
    assert_eq!(cache.put(1, 1), PutResult::Update(1));
     
    // Add 6, should cause another recent evict
    assert_eq!(cache.put(6, 6), PutResult::<i32, i32>::Put);
     
    // Add 7, should evict an entry from ghost LRU.
    assert_eq!(cache.put(7, 7), PutResult::Evicted { key: 2, value: 2 });
     
    // Add 2, should evict an entry from ghost LRU
    assert_eq!(cache.put(2, 11), PutResult::Evicted { key: 3, value: 3 });
     
    // Add 4, should put the entry from ghost LRU to freq LRU
    assert_eq!(cache.put(4, 11), PutResult::Update(4));
     
    // move all entry in recent to freq.
    assert_eq!(cache.put(2, 22), PutResult::Update(11));
    assert_eq!(cache.put(7, 77), PutResult::<i32, i32>::Update(7));
     
    // Add 6, should put the entry from ghost LRU to freq LRU, and evicted one
    // entry
    assert_eq!(cache.put(6, 66), PutResult::EvictedAndUpdate { evicted: (5, 5), update: 6});
    assert_eq!(cache.recent_len(), 0);
    assert_eq!(cache.ghost_len(), 1);
    assert_eq!(cache.frequent_len(), 4);
}

Acknowledgments

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
You might also like...
A read-only, memory-mapped cache.

mmap-cache A low-level API for a memory-mapped cache of a read-only key-value store. Design The [Cache] index is an [fst::Map], which maps from arbitr

Turn your discord cache back to viewable images.

discache 🦀 Every time you view an Image in Discord, it gets saved in your cache folder as an unviewable file. Discache allows you to convert those fi

Non-volatile, distributed file cache backed by content-addressed storage

blobnet A low-latency file server that responds to requests for chunks of file data. This acts as a non-volatile, over-the-network content cache. Inte

Key-value cache RESP server with support for key expirations ⌛

BADER-DB (بادِر) Key-value cache RESP server with support for key expirations ⌛ Supported Features • Getting Started • Basic Usage • Cache Eviction •

Rust binary memcached implementation

bmemcached-rs Rust binary memcached implementation (ON GOING) Usage extern crate bmemcached; use std::sync::Arc; use std::thread; use bmemcached::Me

This is a Rust implementation for popular caches (support no_std).

Caches This is a Rust implementation for popular caches (support no_std). See Introduction, Installation and Usages for more details. English | 简体中文 I

Hitbox is an asynchronous caching framework supporting multiple backends and suitable for distributed and for single-machine applications.

Hitbox is an asynchronous caching framework supporting multiple backends and suitable for distributed and for single-machine applications.

memcache client for rust
memcache client for rust

rust-memcache rust-memcache is a memcached client written in pure rust. Install The crate is called memcache and you can depend on it via cargo: [depe

A generational arena based LRU Cache implementation in 100% safe rust.

generational-lru Crate providing a 100% safe, generational arena based LRU cache implementation. use generational_lru::lrucache::{LRUCache, CacheError

Easy c̵̰͠r̵̛̠ö̴̪s̶̩̒s̵̭̀-t̶̲͝h̶̯̚r̵̺͐e̷̖̽ḁ̴̍d̶̖̔ ȓ̵͙ė̶͎ḟ̴͙e̸̖͛r̶̖͗ë̶̱́ṉ̵̒ĉ̷̥e̷͚̍ s̷̹͌h̷̲̉a̵̭͋r̷̫̊ḭ̵̊n̷̬͂g̵̦̃ f̶̻̊ơ̵̜ṟ̸̈́ R̵̞̋ù̵̺s̷̖̅ţ̸͗!̸̼͋

Rust S̵̓i̸̓n̵̉ I̴n̴f̶e̸r̵n̷a̴l mutability! Howdy, friendly Rust developer! Ever had a value get m̵̯̅ð̶͊v̴̮̾ê̴̼͘d away right under your nose just when

A set of safe Least Recently Used (LRU) map/cache types for Rust

LruMap A set of safe Least-Recently-Used (LRU) cache types aimed at providing flexible map-like structures that automatically evict the least recently

Yet another sort crate, porting Golang sort package to Rust.

IndexSort IndexSort Yet another sort crate (in place), porting Golang's standard sort package to Rust. Installation [dependencies] indexsort = "0.1.0"

Three-body Problem with rust
Three-body Problem with rust

Briefing In physics and classical mechanics, the three-body problem is the problem of taking the initial positions and velocities (or momenta) of thre

a Vietnamese gambling game using three dice

Near Cetificate Devoloper - Demo Vietnamese gambling game: Gourd-Fish-Shrimp-Crab On NEAR How to play Instead of showing one to six pips, the sides of

✨ rudimentary simulation of the three-body problem
✨ rudimentary simulation of the three-body problem

✨ three_body a very rudimentary simulation of the three-body problem. i was curious how far we could get with just euler's method and a small time ste

Gostd is the golang standard library implementation in rust-lang.

Gostd Gostd is the golang standard library implementation in rust-lang.

Golang like WaitGroup implementation for sync/async Rust.

wg Golang like WaitGroup implementation for sync/async Rust.

A fast and flexible LRU map.

A fast and flexible LRU map This repository contains a fast and flexible LRU map. Blazingly fast. Up to twice as fast as the lru crate, and with less

Tiny cross-platform webview library for C/C++/Golang. Uses WebKit (Gtk/Cocoa) and Edge (Windows)

webview A tiny cross-platform webview library for C/C++/Golang to build modern cross-platform GUIs. Also, there are Rust bindings, Python bindings, Ni

Comments
  • fix: Make the core feature work

    fix: Make the core feature work

    Updating the support for the core feature to compile and be compatible on the API.

    Also deletes some unused fields and methods the compiler was complaining about and cargo-fmt made some changes to the edited files.

    We don't particularly need the core feature, however it is the only way to use the crate without depending on default features of the chrono crate. And the chrono crate has a dependency on a vulnerable version of the time crate. Switching to the core feature for this crate allows us to avoid having the vulnerability in our dependencies.

    opened by flub 2
  • Hidden KeyRef type complicates key lookup

    Hidden KeyRef type complicates key lookup

    Because keys are stored inside a KeyRef type, .get() can be complicated or inefficient. For instance, if I have a cache which stores the key as a Vec<u8>, I'd expect to be able to lookup a value a in the cache using a &[u8], but that's not possible:

    error[E0277]: the trait bound `KeyRef<Vec<u8>>: Borrow<[u8]>` is not satisfied
      --> native/lib.rs:98:17
       |
    98 |     match cache.get(key.as_slice()) {
       |                 ^^^ the trait `Borrow<[u8]>` is not implemented for `KeyRef<Vec<u8>>`
       |
       = help: the following implementations were found:
                 <KeyRef<K> as Borrow<K>>
    
    opened by JayKickliter 1
Releases(caches-0.2.0)
Owner
Al Liu
Rustacean/Gopher, distributed system engineer. In the previous two years, writing Go, but now, writing Rust most of the time.
Al Liu
A set of safe Least Recently Used (LRU) map/cache types for Rust

LruMap A set of safe Least-Recently-Used (LRU) cache types aimed at providing flexible map-like structures that automatically evict the least recently

Khonsu Labs 4 Sep 24, 2022
Stretto is a Rust implementation for ristretto. A high performance memory-bound Rust cache.

Stretto is a Rust implementation for ristretto. A high performance memory-bound Rust cache.

Al Liu 310 Dec 29, 2022
Key-Value based in-memory cache library which supports Custom Expiration Policies

Endorphin Key-Value based in-memory cache library which supports Custom Expiration Policies with standard HashMap, HashSet interface. use endorphin::H

Jun Ryung Ju 15 Oct 1, 2022
A native stateless cache implementation.

fBNC fBNC, Blockchain Native Cache. A native stateless storage library for block chain. Its value is to improve the stability and security of online s

Findora Foundation 1 Jan 12, 2022
Rust cache structures and easy function memoization

cached Caching structures and simplified function memoization cached provides implementations of several caching structures as well as a handy macro f

James Kominick 996 Jan 7, 2023
Rust type wrapper to cache hash of potentially large structures.

CachedHash For a type T, CachedHash<T> wraps T and implements Hash in a way that caches T's hash value. This is useful when T is expensive to hash (fo

pali 3 Dec 8, 2022
A general-purpose distributed memory cache system compatible with Memcached

memcrsd memcached server implementation in Rust memcrsd is a key value store implementation in Rust. It is compatible with binary protocol of memcache

null 274 Dec 14, 2022
A lightweight key-value cache system developed for experimental purposes

A lightweight key-value cache system developed for experimental purposes. It can also include distributed systems setup if I can.

Burak Selim Senyurt 8 Jul 23, 2022
ConstDB - an in-memory cache store which aims at master-master replications

A redis-like cache store that implements CRDTs and active-active replications.

null 27 Aug 15, 2022
Read-optimized cache of Cardano on-chain entities

Read-optimized cache of Cardano on-chain entities Intro Scrolls is a tool for building and maintaining read-optimized collections of Cardano's on-chai

TxPipe 58 Dec 2, 2022