85 个版本 (52 个重大更改)
0.53.1 | 2024年7月23日 |
---|---|
0.52.0 | 2024年7月3日 |
0.49.2 | 2024年2月24日 |
0.46.1 | 2023年11月6日 |
0.1.0 | 2017年3月28日 |
#1 in 缓存
473,362 每月下载量
用于 331 个 Crates (150 个直接使用)
225KB
4.5K SLoC
cached
缓存结构和简化函数记忆化
cached
提供了几个缓存结构的实现以及定义记忆化函数的便捷宏。
使用宏定义的缓存函数,如#[cached]
、#[once]
、#[io_cached]
和cached!
,是线程安全的。它们的后端函数缓存被封装在互斥锁或读写锁中,或者在#[io_cached]
的情况下外部同步。默认情况下,函数缓存在整个函数执行过程中不会被锁定,因此对于相同参数的长运行函数的初始(在空缓存的情况下)并发调用将每个完全执行并覆盖缓存值。这反映了Python的functools.lru_cache
的行为。为了同步未缓存参数的执行和缓存,请指定#[cached(sync_writes = true)]
/ #[once(sync_writes = true)]
(#[io_cached]
不支持)。
- 请参阅
cached::stores
文档了解可用的缓存存储。 - 请参阅
proc_macro
以获取更多过程宏示例。 - 请参阅
macros
以获取更多声明性宏示例。
功能
default
:包含proc_macro
和ahash
功能proc_macro
:包含过程宏ahash
:启用可选的默认散列算法ahash
散列器async
:包含对异步函数和异步缓存存储的支持async_tokio_rt_multi_thread
:启用tokio
的rt-multi-thread
可选功能。redis_store
:包含Redis缓存存储redis_async_std
:包含使用async-std
和async-std
的异步Redis支持,以及redis_async_std
的TLS支持,隐含redis_store
和async
redis_tokio
:包含使用tokio
和tokio
的异步Redis支持,以及redis_tokio
的TLS支持,隐含redis_store
和async
redis_connection_manager
:启用redis
的可选connection-manager
功能。任何创建的异步Redis缓存都将使用连接管理器而不是MultiplexedConnection
redis_ahash
:启用redis
的可选ahash
功能disk_store
:包含磁盘缓存存储wasm
:启用WASM支持。请注意,此功能与tokio
的多线程运行时(async_tokio_rt_multi_thread
)以及所有Redis功能(redis_store
、redis_async_std
、redis_tokio
、redis_ahash
)不兼容。
过程宏(#[cached]
、#[once]
、#[io_cached]
)提供了更多功能,包括异步支持。请参阅proc_macro
和macros
模块中的更多示例,以及examples
目录中的可运行代码片段。
任何实现cached::Cached
/cached::CachedAsync
的自定义缓存都可以用于替换内置的#[cached]
/#[once]
/cached!
宏。任何实现cached::IOCached
/cached::IOCachedAsync
的自定义缓存都可以用于#[io_cached]
宏。
基本用法如下:
use cached::proc_macro::cached;
/// Defines a function named `fib` that uses a cache implicitly named `FIB`.
/// By default, the cache will be the function's name in all caps.
/// The following line is equivalent to #[cached(name = "FIB", unbound)]
#[cached]
fn fib(n: u64) -> u64 {
if n == 0 || n == 1 { return n }
fib(n-1) + fib(n-2)
}
use std::thread::sleep;
use std::time::Duration;
use cached::proc_macro::cached;
use cached::SizedCache;
/// Use an explicit cache-type with a custom creation block and custom cache-key generating block
#[cached(
ty = "SizedCache<String, usize>",
create = "{ SizedCache::with_size(100) }",
convert = r#"{ format!("{}{}", a, b) }"#
)]
fn keyed(a: &str, b: &str) -> usize {
let size = a.len() + b.len();
sleep(Duration::new(size as u64, 0));
size
}
use cached::proc_macro::once;
/// Only cache the initial function call.
/// Function will be re-executed after the cache
/// expires (according to `time` seconds).
/// When no (or expired) cache, concurrent calls
/// will synchronize (`sync_writes`) so the function
/// is only executed once.
#[once(time=10, option = true, sync_writes = true)]
fn keyed(a: String) -> Option<usize> {
if a == "a" {
Some(a.len())
} else {
None
}
}
use cached::proc_macro::cached;
/// Cannot use sync_writes and result_fallback together
#[cached(
result = true,
time = 1,
sync_writes = true,
result_fallback = true
)]
fn doesnt_compile() -> Result<String, ()> {
Ok("a".to_string())
}
use cached::proc_macro::io_cached;
use cached::AsyncRedisCache;
use thiserror::Error;
#[derive(Error, Debug, PartialEq, Clone)]
enum ExampleError {
#[error("error with redis cache `{0}`")]
RedisError(String),
}
/// Cache the results of an async function in redis. Cache
/// keys will be prefixed with `cache_redis_prefix`.
/// A `map_error` closure must be specified to convert any
/// redis cache errors into the same type of error returned
/// by your function. All `io_cached` functions must return `Result`s.
#[io_cached(
map_error = r##"|e| ExampleError::RedisError(format!("{:?}", e))"##,
ty = "AsyncRedisCache<u64, String>",
create = r##" {
AsyncRedisCache::new("cached_redis_prefix", 1)
.set_refresh(true)
.build()
.await
.expect("error building example redis cache")
} "##
)]
async fn async_cached_sleep_secs(secs: u64) -> Result<String, ExampleError> {
std::thread::sleep(std::time::Duration::from_secs(secs));
Ok(secs.to_string())
}
use cached::proc_macro::io_cached;
use cached::DiskCache;
use thiserror::Error;
#[derive(Error, Debug, PartialEq, Clone)]
enum ExampleError {
#[error("error with disk cache `{0}`")]
DiskError(String),
}
/// Cache the results of a function on disk.
/// Cache files will be stored under the system cache dir
/// unless otherwise specified with `disk_dir` or the `create` argument.
/// A `map_error` closure must be specified to convert any
/// disk cache errors into the same type of error returned
/// by your function. All `io_cached` functions must return `Result`s.
#[io_cached(
map_error = r##"|e| ExampleError::DiskError(format!("{:?}", e))"##,
disk = true
)]
fn cached_sleep_secs(secs: u64) -> Result<String, ExampleError> {
std::thread::sleep(std::time::Duration::from_secs(secs));
Ok(secs.to_string())
}
通过宏定义的函数将使用函数的参数作为键,在过程宏上指定的convert
表达式,或者在cached_key!
声明宏上指定的Key
块来缓存其结果。
当调用宏定义的函数时,首先检查函数的缓存以查找已计算(且仍然有效)的值,然后再评估函数体。
由于需要在全局缓存中存储参数和返回值的要求
- 函数返回类型
- 对于所有存储类型,除了Redis,必须拥有并实现
Clone
- 对于Redis存储类型,必须拥有并实现
serde::Serialize + serde::DeserializeOwned
- 对于所有存储类型,除了Redis,必须拥有并实现
- 函数参数
- 对于所有存储类型,除了Redis,必须是拥有并实现
Hash + Eq + Clone
的类型,或者使用cached_key!
宏与一个指定键构建的Key
块,或者在一个过程宏上指定一个convert
表达式来指定如何构建一个Hash + Eq + Clone
类型的键。 - 对于Redis存储类型,必须要么是所有者并实现
Display
,要么使用cached_key!
和Key
或过程宏以及convert
表达式来指定如何构造Display
类型的关键字。
- 对于所有存储类型,除了Redis,必须是拥有并实现
- 在插入和检索过程中,参数和返回值将被
cloned
。除了Redis,其中参数被格式化为Strings
,值被反/序列化。 - 不应使用宏定义的函数来生成副作用结果!
- 由于宏扩展为
once_cell
初始化和一个或多个函数定义,因此宏定义的函数不能直接位于impl
块下。 - 宏定义的函数不能接受
Self
类型作为参数。
许可证:MIT
依赖项
~1-15MB
~189K SLoC