expire_cache 0.1.17

High-performance generational cache for Rust / 高性能分代缓存
Documentation

English | 中文


expire_cache: High-performance generational cache

expire_cache implements an efficient expiration cache using generational collection strategy. Instead of tracking individual item expiration times, it maintains two data buckets (generations), significantly reducing memory overhead and CPU usage for expiration checks.

Features

  • High Performance: O(1) amortized expiration overhead per item
  • Concurrent Access: Built on DashMap for thread-safe operations
  • Async Support: Native async initialization with get_or_init_async
  • Flexible Storage: Support for both key-value maps and sets
  • Simple API: Clean interface with get, insert, and initialization methods

Installation

[dependencies]
expire_cache = { version = "0.1.16", features = ["dashmap", "get_or_init_async"] }

Available features:

  • dashmap: Enable DashMap support
  • dashset: Enable DashSet support
  • get_or_init: Enable synchronous initialization
  • get_or_init_async: Enable asynchronous initialization

Quick Start

Basic Usage

use expire_cache::Expire;
use dashmap::DashMap;
use std::time::Duration;

#[tokio::main]
async fn main() {
  let cache: Expire<DashMap<&str, &str>> = Expire::new(60);
  
  cache.insert("key", "value");
  
  if let Some(val) = cache.get("key") {
    println!("Found: {}", *val);
  }
  
  // Wait for expiration
  tokio::time::sleep(Duration::from_secs(120)).await;
  assert!(cache.get("key").is_none());
}

Async Initialization

use expire_cache::Expire;
use dashmap::DashMap;

#[tokio::main]
async fn main() -> Result<(), std::io::Error> {
  let cache: Expire<DashMap<String, String>> = Expire::new(60);
  
  let value = cache
    .get_or_init_async("user_123", |key| async move {
      Ok(format!("data_for_{key}"))
    })
    .await?;
  println!("Loaded: {}", *value);
  
  Ok(())
}

Sync Initialization

use expire_cache::Expire;
use dashmap::DashMap;

fn main() -> Result<(), std::io::Error> {
  let cache: Expire<DashMap<String, String>> = Expire::new(60);
  
  let value = cache.get_or_init("user_123", |key| {
    Ok(format!("data_for_{key}"))
  })?;
  println!("Loaded: {}", *value);
  
  Ok(())
}

Set Usage

use expire_cache::Expire;
use dashmap::DashSet;

#[tokio::main]
async fn main() {
  let cache: Expire<DashSet<&str>> = Expire::new(60);
  
  cache.insert("active_session", ());
  
  if cache.get("active_session").is_some() {
    println!("Session exists");
  }
}

API Reference

Expire<T: Map>

  • new(expire: u64) -> Self: Create cache with expiration period in seconds
  • get(&self, key) -> Option<RefVal>: Retrieve value from cache
  • insert(&self, key, val): Insert value into cache
  • get_or_init(&self, key, func) -> Result<RefVal, E>: Sync initialization
  • get_or_init_async(&self, key, func) -> Result<RefVal, E>: Async initialization

Design

Generational Collection

The cache uses a double-buffer approach with two generations:

  1. Insertion: New entries always go to the active generation
  2. Lookup: Check active generation first, then passive generation
  3. Expiration: Background task periodically clears passive generation and swaps roles
  4. Lifecycle: Items live between expire and 2 * expire seconds

This approach trades absolute precision for significant throughput improvements and reduced memory fragmentation.

License

MulanPSL-2.0


About

This project is an open-source component of js0.site ⋅ Refactoring the Internet Plan.

We are redefining the development paradigm of the Internet in a componentized way. Welcome to follow us:


expire_cache: 高性能分代缓存

expire_cache 实现了基于分代收集策略的高效过期缓存。它不追踪单个条目的过期时间,而是维护两个数据桶(代),显著降低过期检查的内存开销和 CPU 使用率。

特性

  • 高性能:每条目摊销 O(1) 过期开销
  • 并发访问:基于 DashMap 的线程安全操作
  • 异步支持:原生异步初始化 get_or_init_async
  • 灵活存储:支持键值映射和集合
  • 简洁 API:清晰的 getinsert 和初始化接口

安装

[dependencies]
expire_cache = { version = "0.1.16", features = ["dashmap", "get_or_init_async"] }

可用特性:

  • dashmap:启用 DashMap 支持
  • dashset:启用 DashSet 支持
  • get_or_init:启用同步初始化
  • get_or_init_async:启用异步初始化

快速开始

基本用法

use expire_cache::Expire;
use dashmap::DashMap;
use std::time::Duration;

#[tokio::main]
async fn main() {
  let cache: Expire<DashMap<&str, &str>> = Expire::new(60);
  
  cache.insert("key", "value");
  
  if let Some(val) = cache.get("key") {
    println!("Found: {}", *val);
  }
  
  // 等待过期
  tokio::time::sleep(Duration::from_secs(120)).await;
  assert!(cache.get("key").is_none());
}

异步初始化

use expire_cache::Expire;
use dashmap::DashMap;

#[tokio::main]
async fn main() -> Result<(), std::io::Error> {
  let cache: Expire<DashMap<String, String>> = Expire::new(60);
  
  let value = cache
    .get_or_init_async("user_123", |key| async move {
      Ok(format!("data_for_{key}"))
    })
    .await?;
  println!("Loaded: {}", *value);
  
  Ok(())
}

同步初始化

use expire_cache::Expire;
use dashmap::DashMap;

fn main() -> Result<(), std::io::Error> {
  let cache: Expire<DashMap<String, String>> = Expire::new(60);
  
  let value = cache.get_or_init("user_123", |key| {
    Ok(format!("data_for_{key}"))
  })?;
  println!("Loaded: {}", *value);
  
  Ok(())
}

集合用法

use expire_cache::Expire;
use dashmap::DashSet;

#[tokio::main]
async fn main() {
  let cache: Expire<DashSet<&str>> = Expire::new(60);
  
  cache.insert("active_session", ());
  
  if cache.get("active_session").is_some() {
    println!("Session exists");
  }
}

API 参考

Expire<T: Map>

  • new(expire: u64) -> Self:创建带过期周期的缓存(秒)
  • get(&self, key) -> Option<RefVal>:从缓存检索值
  • insert(&self, key, val):向缓存插入值
  • get_or_init(&self, key, func) -> Result<RefVal, E>:同步初始化
  • get_or_init_async(&self, key, func) -> Result<RefVal, E>:异步初始化

设计

分代收集策略

缓存使用双缓冲方法,包含两个代:

  1. 插入:新条目总是进入活跃代
  2. 查找:先检查活跃代,再检查被动代
  3. 过期:后台任务定期清空被动代并交换角色
  4. 生命周期:条目存活时间在 expire2 * expire 秒之间

这种方法牺牲了绝对精度,换取了显著的吞吐量提升和内存碎片减少。

许可证

MulanPSL-2.0


关于

本项目为 js0.site ⋅ 重构互联网计划 的开源组件。

我们正在以组件化的方式重新定义互联网的开发范式,欢迎关注: