Expand description
§rainier-cache
A Cache port and its drivers — the cache surface an MVC framework
ships, with the 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
| Driver | Feature | Shared between instances |
|---|---|---|
MemoryCache | — | no |
RedisCache | redis-driver | yes |
RedisCache on a cluster | redis-cluster | yes, sharded |
MemcachedCache | memcached | yes |
DynamoDbCache | dynamodb | yes, 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;pub use rate_limit::CacheRateLimiter;
Modules§
- cache
- The
Cacheport and the conveniences every implementation gets. - driver
- Which cache store to build —
CacheDriver. - lock
- Atomic locks —
LockManager,LockandLockGuard. - memory
MemoryCache— a cache in this process’s memory.- prefixed
PrefixedCache— namespacing a shared cache.- rate_
limit - Rate-limit counters in the cache —
CacheRateLimiter.
Structs§
- Cache
Manager - The application’s cache, as one container-storable value.