Skip to main content

Crate rainier_cache

Crate rainier_cache 

Source
Expand description

§rainier-cache

A Cache port and its drivers — Laravel’s Cache, with the same shape and the same drivers you would expect.

use rainier_cache::{Cache, CacheExt, MemoryCache};
use std::time::Duration;

let cache = MemoryCache::new();

cache.put_string("greeting", "hello", Some(Duration::from_secs(60))).await?;
assert_eq!(cache.get_string("greeting").await?.as_deref(), Some("hello"));

// A counter, incremented atomically.
assert_eq!(cache.increment("hits", 1).await?, 1);

§Drivers

DriverFeatureShared between instances
MemoryCache—no
RedisCacheredis-driveryes
RedisCache on a clusterredis-clusteryes, sharded
MemcachedCachememcachedyes
DynamoDbCachedynamodbyes, and no server to run

The transports live in rainier-drivers rather than here, because Redis is wanted by the queue too and an application should configure it once.

Needs the matching feature, so this one is not compiled as a doctest:

ⓘ
// One server…
let cache = RedisCache::connect(RedisConnector::open("redis://127.0.0.1/")?).await?;

// …or a sharded cluster, with no change to anything downstream.
let cache = RedisCache::connect(
    RedisConnector::open_cluster(["redis://10.0.0.1:6379", "redis://10.0.0.2:6379"])?,
).await?;

§A miss is not a failure

get returns Ok(None) for an absent key and Err only when the cache could not be reached. Conflating the two is how a cache outage becomes an application outage — and a cache is the one dependency an application should be able to lose.

Driver errors are ServiceUnavailable, so they land in the right bucket on a dashboard rather than looking like bugs.

§Namespace a shared cache

Two applications caching user:1 on one Redis database read each other’s values, and the symptom is a user seeing another application’s data. PrefixedCache fixes it, and makes “flush mine” refuse rather than empty the server:

let shared: Arc<dyn Cache> = Arc::new(MemoryCache::new());
let mine = PrefixedCache::new(shared, "billing");

§What not to cache in memory

A MemoryCache is per-process, so anything cached for correctness rather than for speed is wrong in it: a rate-limit counter becomes N × limit across N instances, and a lock is not a lock.

Cache::add and Cache::forget_if are atomic on every driver, so a Lock over a shared one is a real lock. Over a MemoryCache it is a real lock within this process and nothing at all between two — LockManager::is_shared is the check worth making at boot.

Re-exports§

pub use cache::Cache;
pub use cache::CacheExt;
pub use driver::CacheDriver;
pub use lock::Lock;
pub use lock::LockGuard;
pub use lock::LockManager;
pub use memory::MemoryCache;
pub use prefixed::PrefixedCache;

Modules§

cache
The Cache port and the conveniences every implementation gets.
driver
Which cache store to build — CacheDriver.
lock
Atomic locks — LockManager, Lock and LockGuard.
memory
MemoryCache — a cache in this process’s memory.
prefixed
PrefixedCache — namespacing a shared cache.

Structs§

CacheManager
The application’s cache, as one container-storable value.