#![forbid(unsafe_code)]
#![warn(missing_docs)]
pub mod arc;
pub mod bounded;
pub mod builder;
pub mod lfu;
pub mod lru;
pub mod sharded;
pub mod sketch;
pub mod stats;
pub mod sync;
pub mod tinylfu;
pub mod write_adapter;
#[cfg(feature = "blob")]
pub mod blob_cache;
#[cfg(feature = "columnar")]
pub mod columnar_cache;
#[cfg(feature = "sql")]
pub mod sql_cache;
pub use arc::ArcCache;
pub use bounded::BoundedCache;
pub use builder::{CacheBuilder, CachePolicy};
pub use lfu::LfuCache;
pub use lru::LruCache;
pub use sharded::ShardedCache;
pub use stats::{CacheStats, StatsCache};
pub use sync::SyncCache;
pub use tinylfu::WTinyLfuCache;
pub use write_adapter::{CacheableKvStore, WriteBackCache, WriteThroughCache};
#[cfg(feature = "blob")]
pub use blob_cache::BlobCache;
#[cfg(feature = "columnar")]
pub use columnar_cache::ColumnarRowGroupCache;
#[cfg(feature = "sql")]
pub use sql_cache::{CachedQueryRunner, SqlPlanCache, SqlQueryCache};
pub struct CacheEntry<V> {
pub value: V,
pub expires_at: Option<std::time::Instant>,
}
impl<V> CacheEntry<V> {
#[must_use]
pub fn new(value: V) -> Self {
CacheEntry {
value,
expires_at: None,
}
}
#[must_use]
pub fn with_ttl(value: V, ttl: std::time::Duration) -> Self {
CacheEntry {
value,
expires_at: Some(std::time::Instant::now() + ttl),
}
}
#[must_use]
pub fn is_expired(&self) -> bool {
match self.expires_at {
Some(deadline) => std::time::Instant::now() >= deadline,
None => false,
}
}
}
pub trait Cache<K, V> {
fn get(&mut self, key: &K) -> Option<&V>;
fn put(&mut self, key: K, value: V) -> Option<V>;
fn put_with_ttl(&mut self, key: K, value: V, ttl: std::time::Duration) -> Option<V> {
let _ = ttl;
self.put(key, value)
}
fn len(&self) -> usize;
fn cap(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
fn remove(&mut self, key: &K) -> Option<V>;
fn clear(&mut self);
fn peek(&self, key: &K) -> Option<&V>;
fn contains_key(&self, key: &K) -> bool;
fn resize(&mut self, new_cap: usize);
fn get_or_insert(&mut self, key: K, default: impl FnOnce() -> V) -> &V
where
K: Clone,
{
if !self.contains_key(&key) {
let v = default();
self.put(key.clone(), v);
}
self.peek(&key).expect("key was just inserted")
}
fn values(&self) -> Vec<&V> {
Vec::new()
}
fn warm(&mut self, iter: impl IntoIterator<Item = (K, V)>) {
for (k, v) in iter {
self.put(k, v);
}
}
}
pub async fn get_or_insert_async<K, V, C, F, Fut>(
cache: &std::sync::Mutex<C>,
key: K,
loader: F,
) -> V
where
K: Eq + std::hash::Hash + Clone,
V: Clone,
C: Cache<K, V>,
F: FnOnce() -> Fut,
Fut: std::future::Future<Output = V>,
{
{
let mut guard = cache.lock().expect("cache mutex poisoned");
if let Some(v) = guard.get(&key) {
return v.clone();
}
}
let value = loader().await;
{
let mut guard = cache.lock().expect("cache mutex poisoned");
if let Some(existing) = guard.peek(&key) {
return existing.clone();
}
guard.put(key, value.clone());
}
value
}
#[cfg(feature = "async-helpers")]
pub async fn get_or_insert_async_tokio<K, V, C, F, Fut>(
cache: &tokio::sync::Mutex<C>,
key: K,
loader: F,
) -> V
where
K: Eq + std::hash::Hash + Clone,
V: Clone,
C: Cache<K, V>,
F: FnOnce() -> Fut,
Fut: std::future::Future<Output = V>,
{
{
let mut guard = cache.lock().await;
if let Some(v) = guard.get(&key) {
return v.clone();
}
}
let value = loader().await;
{
let mut guard = cache.lock().await;
if let Some(existing) = guard.peek(&key) {
return existing.clone();
}
guard.put(key, value.clone());
}
value
}