You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

163 lines
4.1 KiB

//! Service cacheing
use super::*;
use std::{
hash::Hash,
marker::{Send, Sync},
fmt,
};
use std::sync::{
Weak,
RwLock,
atomic::AtomicUsize,
};
use std::cell::UnsafeCell;
use std::path::PathBuf;
use std::num::NonZeroUsize;
use std::collections::HashMap;
use chrono::DateTime;
use uuid::Uuid;
use crossbeam_utils::atomic::AtomicCell;
use cryptohelpers::sha256;
pub type Timezone = chrono::Utc;
/// A type that can be used for the cache key
pub trait Key: Sized +
Send +
Sync +
Hash +
PartialEq +
Eq +
fmt::Display +
Serialize +
for<'a> Deserialize<'a> +
'static
{}
impl<T> Key for T
where T:
Send +
Sync +
Hash +
PartialEq +
Eq +
fmt::Display +
Serialize +
for<'a> Deserialize<'a> +
'static
{}
mod mem;
use mem::Memory;
basic_enum!(pub PurgeOrder; "How should a cache determine what to purge": Oldest => "Purge the oldest entries first", LeastUsed => "Purge the least accessed entries first", OldestUsed => "Purge the oldest accessed entries first");
default!(PurgeOrder: Self::LeastUsed);
/// Config for a cache
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Config
{
// General
// Disk cache
/// The directory to save to data to.
/// If `None`, se memory cache only.
pub disk_location: Option<PathBuf>,
// Memory cache
/// Max size of an entry to cache in memory
pub mem_max_ent_size: Option<NonZeroUsize>,
/// Max entries in memory cache
pub mem_max_ent: Option<NonZeroUsize>,
/// Max total bytes to keep in memcache before purging older entries.
pub mem_max_total_size: Option<NonZeroUsize>,
/// When purging entries from memcache, how to select ones to purge
pub mem_purge_order: PurgeOrder,
}
/// An entry in a `ByteCache`.
#[derive(Debug)]
pub struct CacheEntry //<K: Key> // `K` is the key in `entries`.
{
id: Uuid,
tm_accessed: RwLock<DateTime<Timezone>>, // Can be mutated when read, so must be mutable by the reader, hence the `RwLock`.
tm_created: DateTime<Timezone>,
accesses: AtomicUsize, // Can be mutated when read, hence the atomic.
/// Hash of the memcache
///
/// Used to ensure integrity of written disk data on an explicit check
/// (implicitly, integrity is checked by comparing the length of the disk stream with the length of the memory stream, since they are write-only in partial entries.)
hash: sha256::Sha256Hash,
memory: Option<Memory>,
// Pathname is computed from `id`.
}
/// A byte-stream cache of data. Caches both to memory and also writes entries to disk.
#[derive(Debug)]
pub struct ByteCache<K: Key>
{
/// How the cache should operate.
/// This is `Arc`d so Partial entries can access it.
// Config is big, box it.
cfg: Arc<Config>,
/// Frozen entries.
entries: RwLock<HashMap<K, CacheEntry>>,
///// Non-complete entries are completed with async semantics.
///// Moving them to `entries` is completed with sync semantics.
// TODO: Is this right at all? eh...
// FUCK This shit, don't store it here, do it somewhere fucking else FUCK.
// working: tokio::sync::RwLock<Vec<Weak<AtomicCell<PartialCacheEntryInner<K>>>>>,
}
impl<K: Key> ByteCache<K>
{
/// Create a new empty entry for this cache
pub async fn create_partial(&self, key: K) -> Result<PartialCacheEntry<K>, error::PartialInitError>
{
let mut this = PartialCacheEntry::new_uninit(self, key);
this.init().await.map_err(error::PartialInitError)?;
Ok(this)
}
}
mod partial;
pub use partial::*;
pub mod error;
/*
XXX: Move this to submodule, fuck the Cell BULLSHIT FUCK
#[derive(Debug)]
struct PartialCacheEntryInner<K: Key>
{
key: K,
disk: tokio::fs::File,
memory: ::bytes::BytesMut,
}
//#[derive(Debug)]
pub struct PartialCacheEntry<K: Key>(Arc<AtomicCell<PartialCacheEntryInner<K>>>);
impl<K: Key> PartialCacheEntry<K>
{
fn as_inner_mut(&mut self) -> &mut PartialCacheEntryInner<K>
{
//XXX: Is this safe???? Do we need an extra unsafecell? eh... fuck this
unsafe { &mut *(self.0.as_ptr() as *mut _)}
}
fn as_inner(&self) -> &PartialCacheEntryInner<K>
{
unsafe { &*self.0.as_ptr() }
}
}
*/