#cache #async #expiration #ttl #async-context #future #async-task

retainer

支持键过期功能的Rust最小异步缓存

5个不稳定版本

0.3.0 2022年3月22日
0.2.2 2021年2月9日
0.2.1 2021年2月2日
0.2.0 2021年2月2日
0.1.0 2021年1月26日

缓存类中排名第70

Download history 293/week @ 2024-03-14 399/week @ 2024-03-21 333/week @ 2024-03-28 552/week @ 2024-04-04 397/week @ 2024-04-11 482/week @ 2024-04-18 343/week @ 2024-04-25 559/week @ 2024-05-02 557/week @ 2024-05-09 843/week @ 2024-05-16 447/week @ 2024-05-23 610/week @ 2024-05-30 1633/week @ 2024-06-06 598/week @ 2024-06-13 497/week @ 2024-06-20 497/week @ 2024-06-27

每月下载量3,411
5 crates中使用

MIT许可证

24KB
290 代码行

Retainer

Build Status Crates.io

此crates提供了一个非常小的异步缓存,允许在异步Rust上下文(Tokio、async-std、smol等)中使用,而不会完全阻塞工作线程。

它还包括根据缓存中条目的时间来使条目过期的功能;这是通过在异步运行时上启动一个监控器来定期执行清理任务来完成的。驱逐算法类似于Redis中的算法,尽管为了减少借用复杂性,键在访问时不会被删除。

此crates仍在开发中,因此请随时提出任何建议或改进,我会尽快处理它们 :).

入门

此crates可在crates.io上获得。使用它的最简单方法是向您的Cargo.toml添加一个条目,定义依赖项

[dependencies]
retainer = "0.3"

基本用法

缓存的构建非常简单,(目前)不需要任何选项。如果您需要使用键过期,您必须确保在运行时上等待一个监控器或启动一个监控器。

在将条目插入缓存时,有多种方法可以提供过期时间,通过使用实现Into<CacheExpiration>特质的几个类型。以下是一些可用的类型和一些您可能会使用的典型API示例。此代码使用Tokio运行时,但此crates应该与大多数流行的异步运行时兼容。目前有一组小型测试针对async-std、smol和Tokio运行。

use retainer::*;
use tokio::time::sleep;

use std::sync::Arc;
use std::time::{Duration, Instant};

#[tokio::main]
async fn main() {
    // construct our cache
    let cache = Arc::new(Cache::new());
    let clone = cache.clone();

    // don't forget to monitor your cache to evict entries
    let monitor = tokio::spawn(async move {
        clone.monitor(4, 0.25, Duration::from_secs(3)).await
    });

    // insert using an `Instant` type to specify expiration
    cache.insert("one", 1usize, Instant::now()).await;

    // insert using a `Duration` type to wait before expiration
    cache.insert("two", 2, Duration::from_secs(2)).await;

    // insert using a number of milliseconds
    cache.insert("three", 3, 3500).await;

    // insert using a random number of milliseconds
    cache.insert("four", 4, 3500..5000).await;

    // insert without expiration (i.e. manual removal)
    cache.insert("five", 5, CacheExpiration::none()).await;

    // wait until the monitor has run once
    sleep(Duration::from_millis(3250)).await;

    // the first two keys should have been removed
    assert!(cache.get(&"one").await.is_none());
    assert!(cache.get(&"two").await.is_none());

    // the rest should be there still for now
    assert!(cache.get(&"three").await.is_some());
    assert!(cache.get(&"four").await.is_some());
    assert!(cache.get(&"five").await.is_some());

    // wait until the monitor has run again
    sleep(Duration::from_millis(3250)).await;

    // the other two keys should have been removed
    assert!(cache.get(&"three").await.is_none());
    assert!(cache.get(&"four").await.is_none());

    // the key with no expiration should still exist
    assert!(cache.get(&"five").await.is_some());

    // but we should be able to manually remove it
    assert!(cache.remove(&"five").await.is_some());
    assert!(cache.get(&"five").await.is_none());

    // and now our cache should be empty
    assert!(cache.is_empty().await);

    // shutdown monitor
    monitor.abort();
}

如果此示例未保持最新,您可以在文档中查找实现Into<CacheExpiratio>特质的任何类型的完整列表。

缓存监控

所有密钥过期操作都是在间隔内进行的,当你使用 await 等待由 Cache::monitor 返回的未来对象时执行。这种操作的依据大致来源于Redis内部的实现,因为它既简单又有效。

当你调用 Cache::monitor 时,你需要提供3个参数

  • 样本
  • 频率
  • 阈值

以下是对驱逐流程的总结,希望它能清晰易懂

  1. 等待 频率 的下一个滴答。
  2. 从缓存中随机选择一批 样本 条目。
  3. 检查并删除批处理中找到的任何过期条目。
  4. 如果批处理中的条目超过 阈值 百分比被删除,则立即转到第2步,否则转到第1步。

这使用户能够通过调整 阈值频率 值,非常有效地控制驱逐的积极性。当然,缓存使用的内存平均会随着阈值的提高而增加,所以请务必注意这一点。

缓存日志

从v0.2开始,使用了log crate进行最小限度的日志记录。您可以将任何兼容的日志后端附加到其中,以查看缓存(特别是驱逐循环)中发生的情况,以便更好地评估您的使用情况和参数。

依赖关系

~0.6–1.1MB
~18K SLoC