use std::any::TypeId;
use std::hash::Hash;
#[cfg(any(feature = "async", feature = "tokio"))]
use std::future::Future;
use crate::cache::{global, PerTypeCache};
use crate::hasher::hash_value;
use crate::traits::ImcCacheable;
pub fn through_imc<T, A, F>(args: A, f: F) -> T
where
T: ImcCacheable,
A: Hash + Clone + Send + 'static,
F: FnOnce() -> T,
{
let type_id = TypeId::of::<T>();
let args_hash = hash_value(&args);
{
let stores = global().stores.read().unwrap();
if let Some(cache) = stores.get(&type_id) {
if let Some(value) = cache.get::<T>(args_hash) {
crate::metrics::record_hit();
crate::log_event!(DEBUG, crate::log::API, crate::log::HIT, type_id = ?type_id);
return value;
}
}
}
crate::metrics::record_miss();
crate::log_event!(DEBUG, crate::log::API, crate::log::MISS, type_id = ?type_id);
let value = f();
let id = value.cache_id();
let id_hash = hash_value(&id);
let ttl = T::cache_ttl();
if let Some(vsize) = value.cache_value_size() {
if vsize > T::cache_max_value_size() {
crate::log_event!(WARN, crate::log::API, crate::log::SET,
"value too large ({} bytes, max {}), skipping cache", vsize, T::cache_max_value_size());
return value;
}
}
let mut stores = global().stores.write().unwrap();
let cache = stores
.entry(type_id)
.or_insert_with(|| PerTypeCache::from_trait::<T>());
cache.set::<T>(args_hash, id_hash, value, ttl);
cache
.get::<T>(args_hash)
.expect("value was just stored")
}
macro_rules! keyed_body {
($key:expr, $f:expr) => {{
let type_id = TypeId::of::<T>();
let args_hash = hash_value($key);
{
let stores = global().stores.read().unwrap();
if let Some(cache) = stores.get(&type_id) {
if let Some(value) = cache.get::<T>(args_hash) {
crate::metrics::record_hit();
crate::log_event!(DEBUG, crate::log::API, crate::log::HIT, type_id = ?type_id);
return value;
}
}
}
crate::metrics::record_miss();
crate::log_event!(DEBUG, crate::log::API, crate::log::MISS, type_id = ?type_id);
let value = ($f)();
let id = value.cache_id();
let id_hash = hash_value(&id);
let ttl = T::cache_ttl();
if let Some(vsize) = value.cache_value_size() {
if vsize > T::cache_max_value_size() {
crate::log_event!(WARN, crate::log::API, crate::log::SET,
"value too large ({} bytes, max {}), skipping cache", vsize, T::cache_max_value_size());
return value;
}
}
let mut stores = global().stores.write().unwrap();
let cache = stores
.entry(type_id)
.or_insert_with(|| PerTypeCache::from_trait::<T>());
cache.set::<T>(args_hash, id_hash, value, ttl);
#[cfg(feature = "critical")]
crate::critical::after_store::<T, T::Key>($key, args_hash);
cache
.get::<T>(args_hash)
.expect("value was just stored")
}};
}
#[cfg(feature = "critical")]
pub fn through_imc_keyed<T, F>(key: T::Key, f: F) -> T
where
T: ImcCacheable,
T::Key: crate::CriticalKey,
F: FnOnce() -> T,
{
keyed_body!(&key, f)
}
#[cfg(not(feature = "critical"))]
pub fn through_imc_keyed<T, F>(key: T::Key, f: F) -> T
where
T: ImcCacheable,
F: FnOnce() -> T,
{
keyed_body!(&key, f)
}
#[cfg(any(feature = "async", feature = "tokio"))]
pub async fn through_imc_async<T, A, F, Fut>(args: A, f: F) -> T
where
T: ImcCacheable,
A: Hash + Clone + Send + 'static,
F: FnOnce() -> Fut,
Fut: Future<Output = T>,
{
let type_id = TypeId::of::<T>();
let args_hash = hash_value(&args);
{
let stores = global().stores.read().unwrap();
if let Some(cache) = stores.get(&type_id) {
if let Some(value) = cache.get::<T>(args_hash) {
crate::metrics::record_hit();
crate::log_event!(DEBUG, crate::log::API, crate::log::HIT, type_id = ?type_id);
return value;
}
}
}
crate::metrics::record_miss();
crate::log_event!(DEBUG, crate::log::API, crate::log::MISS, type_id = ?type_id);
let value = f().await;
let id = value.cache_id();
let id_hash = hash_value(&id);
let ttl = T::cache_ttl();
if let Some(vsize) = value.cache_value_size() {
if vsize > T::cache_max_value_size() {
crate::log_event!(WARN, crate::log::API, crate::log::SET,
"value too large ({} bytes, max {}), skipping cache", vsize, T::cache_max_value_size());
return value;
}
}
let mut stores = global().stores.write().unwrap();
let cache = stores
.entry(type_id)
.or_insert_with(|| PerTypeCache::from_trait::<T>());
cache.set::<T>(args_hash, id_hash, value, ttl);
cache
.get::<T>(args_hash)
.expect("value was just stored")
}
#[cfg(any(feature = "async", feature = "tokio"))]
macro_rules! keyed_async_body {
($key:expr, $f:expr) => {{
let type_id = TypeId::of::<T>();
let args_hash = hash_value($key);
{
let stores = global().stores.read().unwrap();
if let Some(cache) = stores.get(&type_id) {
if let Some(value) = cache.get::<T>(args_hash) {
crate::metrics::record_hit();
crate::log_event!(DEBUG, crate::log::API, crate::log::HIT, type_id = ?type_id);
return value;
}
}
}
crate::metrics::record_miss();
crate::log_event!(DEBUG, crate::log::API, crate::log::MISS, type_id = ?type_id);
let value = ($f)().await;
let id = value.cache_id();
let id_hash = hash_value(&id);
let ttl = T::cache_ttl();
if let Some(vsize) = value.cache_value_size() {
if vsize > T::cache_max_value_size() {
crate::log_event!(WARN, crate::log::API, crate::log::SET,
"value too large ({} bytes, max {}), skipping cache", vsize, T::cache_max_value_size());
return value;
}
}
let mut stores = global().stores.write().unwrap();
let cache = stores
.entry(type_id)
.or_insert_with(|| PerTypeCache::from_trait::<T>());
cache.set::<T>(args_hash, id_hash, value, ttl);
#[cfg(feature = "critical")]
crate::critical::after_store::<T, T::Key>($key, args_hash);
cache
.get::<T>(args_hash)
.expect("value was just stored")
}};
}
#[cfg(all(any(feature = "async", feature = "tokio"), feature = "critical"))]
pub async fn through_imc_keyed_async<T, F, Fut>(key: T::Key, f: F) -> T
where
T: ImcCacheable,
T::Key: crate::CriticalKey,
F: FnOnce() -> Fut,
Fut: Future<Output = T>,
{
keyed_async_body!(&key, f)
}
#[cfg(all(any(feature = "async", feature = "tokio"), not(feature = "critical")))]
pub async fn through_imc_keyed_async<T, F, Fut>(key: T::Key, f: F) -> T
where
T: ImcCacheable,
F: FnOnce() -> Fut,
Fut: Future<Output = T>,
{
keyed_async_body!(&key, f)
}
pub fn imc_remove<T: ImcCacheable>(id: &T::Id) {
let type_id = TypeId::of::<T>();
let id_hash = hash_value(id);
let mut stores = global().stores.write().unwrap();
if let Some(cache) = stores.get_mut(&type_id) {
cache.remove_data(id_hash);
crate::log_event!(DEBUG, crate::log::API, crate::log::REMOVE, type_id = ?type_id, id_hash = id_hash);
}
}
pub fn imc_clear<T: ImcCacheable>() {
let type_id = TypeId::of::<T>();
let mut stores = global().stores.write().unwrap();
if let Some(cache) = stores.get_mut(&type_id) {
cache.clear();
crate::log_event!(INFO, crate::log::API, crate::log::CLEAR, type_id = ?type_id);
}
}
pub fn imc_len<T: ImcCacheable>() -> usize {
let type_id = TypeId::of::<T>();
let stores = global().stores.read().unwrap();
let len = stores.get(&type_id).map_or(0, |c| c.len());
crate::metrics::set_entries(len);
len
}
pub fn imc_invalidation_id<T: ImcCacheable>(id: &T::Id) -> String {
let id_hash = hash_value(id);
id_hash.to_string()
}