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
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() }
|
|
}
|
|
}
|
|
*/
|