Git Product home page Git Product logo

cached's Introduction

cached

Build Status crates.io docs

Caching structures and simplified function memoization

cached provides implementations of several caching structures as well as a handy macros for defining memoized functions.

Memoized functions defined using #[cached]/#[once]/#[io_cached]/cached! macros are thread-safe with the backing function-cache wrapped in a mutex/rwlock, or externally synchronized in the case of #[io_cached]. By default, the function-cache is not locked for the duration of the function's execution, so initial (on an empty cache) concurrent calls of long-running functions with the same arguments will each execute fully and each overwrite the memoized value as they complete. This mirrors the behavior of Python's functools.lru_cache. To synchronize the execution and caching of un-cached arguments, specify #[cached(sync_writes = true)] / #[once(sync_writes = true)] (not supported by #[io_cached].

Features

  • default: Include proc_macro and async features
  • proc_macro: Include proc macros
  • async: Include support for async functions and async cache stores
  • redis_store: Include Redis cache store
  • redis_async_std: Include async Redis support using async-std and async-std tls support, implies redis_store and async
  • redis_tokio: Include async Redis support using tokio and tokio tls support, implies redis_store and async
  • wasm: Enable WASM support. Note that this feature is incompatible with all Redis features (redis_store, redis_async_std, redis_tokio)

The procedural macros (#[cached], #[once], #[io_cached]) offer more features, including async support. See the proc_macro and macros modules for more samples, and the examples directory for runnable snippets.`

Any custom cache that implements cached::Cached/cached::CachedAsync can be used with the #[cached]/#[once]/cached! macros in place of the built-ins. Any custom cache that implements cached::IOCached/cached::IOCachedAsync can be used with the #[io_cached] macro.


The basic usage looks like:

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(
    type = "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::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))"##,
    type = "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())
}

Functions defined via macros will have their results cached using the function's arguments as a key, a convert expression specified on a procedural macros, or a Key block specified on a cached_key! declarative macro.

When a macro-defined function is called, the function's cache is first checked for an already computed (and still valid) value before evaluating the function body.

Due to the requirements of storing arguments and return values in a global cache:

  • Function return types:
    • For all store types, except Redis, must be owned and implement Clone
    • For the Redis store type, must be owned and implement serde::Serialize + serde::DeserializeOwned
  • Function arguments:
    • For all store types, except Redis, must either be owned and implement Hash + Eq + Clone, the cached_key! macro is used with a Key block specifying key construction, or a convert expression is specified on a procedural macro to specify how to construct a key of a Hash + Eq + Clone type.
    • For the Redis store type, must either be owned and implement Display, or the cached_key! & Key or procedural macro & convert expression used to specify how to construct a key of a Display type.
  • Arguments and return values will be cloned in the process of insertion and retrieval. Except for Redis where arguments are formatted into Strings and values are de/serialized.
  • Macro-defined functions should not be used to produce side-effectual results!
  • Macro-defined functions cannot live directly under impl blocks since macros expand to a once_cell/lazy_static initialization and one or more function definitions.
  • Macro-defined functions cannot accept Self types as a parameter.

License: MIT

cached's People

Contributors

absoludity avatar altair-bueno avatar bowenwang1996 avatar clia avatar csos95 avatar cuviper avatar djmcgill avatar drwilco avatar jaemk avatar jakobn-ai avatar jkoelker avatar jqnatividad avatar kianmeng avatar kloenk avatar makorne avatar manishearth avatar mfornet avatar mikhailok avatar omid avatar paolobarbolini avatar paulvt avatar petersf-libre avatar rahix avatar rukai avatar shoichi1023 avatar stargateur avatar thorhs avatar troublescooter avatar yagince avatar yusdacra avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.