#cache #lru-cache #thread-safe #async #async-io #tokio #loader

cache_loader_async

基于tokio的线程安全加载缓存,具有异步加载函数

10个版本

0.2.1 2022年9月9日
0.2.0 2022年1月23日
0.1.2 2021年10月7日
0.1.1 2021年8月31日
0.0.5 2021年5月10日

#80缓存

Download history 40/week @ 2024-03-11 46/week @ 2024-03-18 29/week @ 2024-03-25 71/week @ 2024-04-01 141/week @ 2024-04-08 87/week @ 2024-04-15 51/week @ 2024-04-22 152/week @ 2024-04-29 53/week @ 2024-05-06 106/week @ 2024-05-13 115/week @ 2024-05-20 14/week @ 2024-05-27 28/week @ 2024-06-03 78/week @ 2024-06-10 29/week @ 2024-06-17 34/week @ 2024-06-24

169 每月下载量
6 包中使用(直接使用3个)

Apache-2.0

73KB
1.5K SLoC

cache-loader-async

Tests

crates.io

此包的目的是提供一种线程安全且易于访问任何数据结构的简便方法,这些数据结构可能最多只存储在数据库中一次,并将其保存在缓存中以便后续请求。

此库基于tokio-rsfutures

使用方法

使用此库非常简单

#[tokio::main]
async fn main() {
    let static_db: HashMap<String, u32> =
        vec![("foo".into(), 32), ("bar".into(), 64)]
            .into_iter()
            .collect();
    
    let cache = LoadingCache::new(move |key: String| {
        let db_clone = static_db.clone();
        async move {
            db_clone.get(&key).cloned().ok_or("error-message")
        }
    });

    let result = cache.get("foo".to_owned()).await.unwrap().0;

    assert_eq!(result, 32);
}

LoadingCache将首先尝试在一个内部的HashMap中查找结果,如果没有找到且没有正在进行的加载,它将触发加载请求并将其他获取请求排队,直到加载请求完成。

功能与缓存后端

cache-loader-async库目前支持两个额外的内置后端:LRU & TTL。LRU根据缓存最大大小逐出键,而TTL在键的TTL过期后自动逐出键。

LRU后端

您可以通过启用lru-cache功能来使用来自lru-rs crate的简单预构建LRU缓存。

要使用LRU缓存后端创建LoadingCache,请使用LoadingCache上的with_backing方法。

async fn main() {
    let size: usize = 10;
    let cache = LoadingCache::with_backing(LruCacheBacking::new(size), move |key: String| {
        async move {
            Ok(key.to_lowercase())
        }
    });
}

TTL后端

您可以通过启用ttl-cache功能来使用简单的预构建TTL缓存。这不需要任何额外的依赖项。

要使用TTL缓存后端创建LoadingCache,请使用LoadingCache上的with_backing方法。

async fn main() {
    let duration: Duration = Duration::from_secs(30);
    let cache = LoadingCache::with_backing(TtlCacheBacking::new(duration), move |key: String| {
        async move {
            Ok(key.to_lowercase())
        }
    });
}

如果您使用with_meta_loader方法,还可以为每个键提供自定义的TTL。以下示例将使用10秒的TTL覆盖全局30秒TTL。是的,覆盖每个键都没有意义,因此您应该在那里有条件。

async fn main() {
    let duration: Duration = Duration::from_secs(30);
    let cache = LoadingCache::with_meta_loader(TtlCacheBacking::new(duration), move |key: String| {
        async move {
            Ok(key.to_lowercase())
                .with_meta(Some(TtlMeta::from(Duration::from_secs(10))))
        }
    });
}

此外,TTL后端允许您自定义底层后端。默认情况下,它使用HashMapBacking

async fn main() {
    let duration: Duration = Duration::from_secs(30);
    let cache = LoadingCache::with_meta_loader(TtlCacheBacking::with_backing(LruCacheBacking::new(10), duration), move |key: String| {
        async move {
            Ok(key.to_lowercase())
                .with_meta(Some(TtlMeta::from(Duration::from_secs(10))))
        }
    });
}

自定义后端

要实现自己的缓存后端,只需从 backing 模块中实现公共的 CacheBacking 特性。

pub trait CacheBacking<K, V>
    where K: Eq + Hash + Sized + Clone + Send,
          V: Sized + Clone + Send {
    type Meta: Clone + Send;

    fn get_mut(&mut self, key: &K) -> Result<Option<&mut V>, BackingError>;
    fn get(&mut self, key: &K) -> Result<Option<&V>, BackingError>;
    fn set(&mut self, key: K, value: V, meta: Option<Self::Meta>) -> Result<Option<V>, BackingError>;
    fn remove(&mut self, key: &K) -> Result<Option<V>, BackingError>;
    fn contains_key(&mut self, key: &K) -> Result<bool, BackingError>;
    fn remove_if(&mut self, predicate: Box<dyn Fn((&K, &V)) -> bool + Send + Sync + 'static>) -> Result<Vec<(K, V)>, BackingError>;
    fn clear(&mut self) -> Result<(), BackingError>;
}

依赖项

~3-5MB
~85K SLoC