pub struct TieredStorage<Hot, Cold> { /* private fields */ }Expand description
Two-tier cache storage: a fast hot tier over a durable cold tier.
TieredStorage layers two backends: a Hot tier serving the working set from fast storage
and a Cold tier holding the larger, durable set. It is itself a CacheStorage, so it
drops in wherever a single backend would go — the headline pairing is an InMemoryStorage
hot tier over a FileSystemStorage cold tier, but any two backends compose.
Clone is available when both tiers are Clone, and shares their backing storage.
§Runtime
The write path finishes asynchronously (see below), so a TieredStorage is constructed with
the Runtime it spawns that background work on — and it must be the runtime actually
driving the process, or the flush never makes progress. Construct the adapter for your
runtime directly (for example trillium_smol::SmolRuntime::default() or
trillium_tokio::TokioRuntime::default()); on a client you can instead take it from the
connector with client.connector().runtime(). The tiered_cache example wires this up end
to end.
§Read path
get consults the hot tier first and, on a hit, serves from it alone. On a hot miss it
reads the cold tier; opening a cold entry promotes it, streaming the body to the reader
and into the hot tier at once (the same teeing used on the origin→user+storage path), so
the working set migrates into fast storage as it is served. A hot tier emptied by a restart
repopulates from cold as entries are read.
The hot-first lookup assumes the hot tier evicts a whole CacheKey at once — all Vary
variants of a URL together — so a hot hit implies the full variant set for that key is
present. InMemoryStorage satisfies this. A hot tier that evicts individual variants
could leave siblings only in cold and hide them behind a hot hit; pair TieredStorage with
a whole-key-eviction hot tier.
§Write path
put writes the body into the hot tier as it streams, then finalizing the entry spawns a
background task that copies it into the cold tier — a write-back. The hot tier is populated
synchronously; cold durability follows shortly after, off the request path. A crash in that
window loses the not-yet-flushed entry, which for a cache means only an extra origin fetch.
Because cold ends up holding every stored entry, evicting from hot only drops a fast-path
copy — the entry stays served from cold and re-promotes on its next read.
§Policy refresh
A 304 revalidation refreshes the policy on whichever tier served the entry. After a hot eviction a request may fall through to a cold copy carrying the pre-refresh policy and revalidate once more; the content served is always correct.
Implementations§
Source§impl<Hot, Cold> TieredStorage<Hot, Cold>
impl<Hot, Cold> TieredStorage<Hot, Cold>
Sourcepub fn new(hot: Hot, cold: Cold, runtime: impl RuntimeTrait) -> Self
pub fn new(hot: Hot, cold: Cold, runtime: impl RuntimeTrait) -> Self
Compose hot and cold into a tiered storage, spawning background write-back onto
runtime. Lookups and promotions favor hot; every stored entry is flushed through to
cold.
Pass the runtime the surrounding server or client already runs on.
Trait Implementations§
Source§impl<Hot, Cold> CacheStorage for TieredStorage<Hot, Cold>
impl<Hot, Cold> CacheStorage for TieredStorage<Hot, Cold>
Source§type StoredEntry = TieredEntry<Hot, Cold>
type StoredEntry = TieredEntry<Hot, Cold>
get.Source§type PutHandle = TieredPutHandle<Hot, Cold>
type PutHandle = TieredPutHandle<Hot, Cold>
put.Source§async fn get(&self, key: &CacheKey) -> Vec<Self::StoredEntry>
async fn get(&self, key: &CacheKey) -> Vec<Self::StoredEntry>
key. Returns an empty vec when
the key has no entries.Source§async fn put(
&self,
key: CacheKey,
policy: CachePolicy,
) -> Result<Self::PutHandle>
async fn put( &self, key: CacheKey, policy: CachePolicy, ) -> Result<Self::PutHandle>
key with the supplied policy.
Returns a PutHandle that the caller writes body bytes into,
then closes with PutHandle::finalize. If an existing entry
has the same Vary signature, finalize replaces it; otherwise
the new entry is appended. Read moreSource§async fn invalidate(&self, key: &CacheKey)
async fn invalidate(&self, key: &CacheKey)
key.Source§impl<Hot: CacheStorage, Cold: CacheStorage> Debug for TieredStorage<Hot, Cold>
impl<Hot: CacheStorage, Cold: CacheStorage> Debug for TieredStorage<Hot, Cold>
Auto Trait Implementations§
impl<Hot, Cold> !RefUnwindSafe for TieredStorage<Hot, Cold>
impl<Hot, Cold> !UnwindSafe for TieredStorage<Hot, Cold>
impl<Hot, Cold> Freeze for TieredStorage<Hot, Cold>
impl<Hot, Cold> Send for TieredStorage<Hot, Cold>
impl<Hot, Cold> Sync for TieredStorage<Hot, Cold>
impl<Hot, Cold> Unpin for TieredStorage<Hot, Cold>
impl<Hot, Cold> UnsafeUnpin for TieredStorage<Hot, Cold>where
Hot: UnsafeUnpin,
Cold: UnsafeUnpin,
Blanket Implementations§
Source§impl<T> ArchivePointee for T
impl<T> ArchivePointee for T
Source§type ArchivedMetadata = ()
type ArchivedMetadata = ()
Source§fn pointer_metadata(
_: &<T as ArchivePointee>::ArchivedMetadata,
) -> <T as Pointee>::Metadata
fn pointer_metadata( _: &<T as ArchivePointee>::ArchivedMetadata, ) -> <T as Pointee>::Metadata
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> LayoutRaw for T
impl<T> LayoutRaw for T
Source§fn layout_raw(_: <T as Pointee>::Metadata) -> Result<Layout, LayoutError>
fn layout_raw(_: <T as Pointee>::Metadata) -> Result<Layout, LayoutError>
Source§impl<T, N1, N2> Niching<NichedOption<T, N1>> for N2
impl<T, N1, N2> Niching<NichedOption<T, N1>> for N2
Source§unsafe fn is_niched(niched: *const NichedOption<T, N1>) -> bool
unsafe fn is_niched(niched: *const NichedOption<T, N1>) -> bool
Source§fn resolve_niched(out: Place<NichedOption<T, N1>>)
fn resolve_niched(out: Place<NichedOption<T, N1>>)
out indicating that a T is niched.