pub struct TieredBackend { /* private fields */ }Expand description
Three-tier read-through / write-through cache resolver.
Holds Arc<dyn StorageBackend> per tier, so any backend composes — the real
deployment injects RedisBackend (L1), PgStorageBackend (L2), S3Storage
(L3); tests inject in-memory mocks + a LocalStorage.
Implementations§
Source§impl TieredBackend
impl TieredBackend
Sourcepub fn new(
l1: Arc<dyn StorageBackend>,
l2: Arc<dyn StorageBackend>,
l3: Arc<dyn StorageBackend>,
) -> TieredBackend
pub fn new( l1: Arc<dyn StorageBackend>, l2: Arc<dyn StorageBackend>, l3: Arc<dyn StorageBackend>, ) -> TieredBackend
Compose three tiers with the default WritePolicy::WriteThrough.
Sourcepub fn with_write_policy(
l1: Arc<dyn StorageBackend>,
l2: Arc<dyn StorageBackend>,
l3: Arc<dyn StorageBackend>,
write_policy: WritePolicy,
) -> TieredBackend
pub fn with_write_policy( l1: Arc<dyn StorageBackend>, l2: Arc<dyn StorageBackend>, l3: Arc<dyn StorageBackend>, write_policy: WritePolicy, ) -> TieredBackend
Compose three tiers with an explicit WritePolicy.
Sourcepub fn write_policy(&self) -> WritePolicy
pub fn write_policy(&self) -> WritePolicy
The active write policy.
Trait Implementations§
Source§impl NarRefIndex for TieredBackend
The composite reverse index: each tier keeps its own edges, and the answer is
their union.
impl NarRefIndex for TieredBackend
The composite reverse index: each tier keeps its own edges, and the answer is their union.
§Why the union, and why it includes L1
list_narinfos reads only the authoritative
tiers because a hot tier’s partial view would under-report a listing. Here
the asymmetry runs the other way: an extra referrer keeps a NAR that could
have been reclaimed (a leak), a missing one deletes a NAR another narinfo
still advertises (an outage). So every tier that answers is believed —
including a stale L1 edge that outlived its narinfo.
A tier whose read fails is not silently treated as empty: the failure is logged and propagated, because “this tier is down” must never resolve to “nobody advertises this NAR” for something about to delete.
Source§fn record<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
nar_path: &'life1 str,
hash: &'life2 str,
) -> Pin<Box<dyn Future<Output = Result<(), StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
TieredBackend: 'async_trait,
fn record<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
nar_path: &'life1 str,
hash: &'life2 str,
) -> Pin<Box<dyn Future<Output = Result<(), StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
TieredBackend: 'async_trait,
Record on both durable tiers (gated by
durable_write_outcome) and
best-effort on L1 — the same shape as a narinfo write, so an edge lands
wherever its narinfo does.
Source§fn forget<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
nar_path: &'life1 str,
hash: &'life2 str,
) -> Pin<Box<dyn Future<Output = Result<(), StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
TieredBackend: 'async_trait,
fn forget<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
nar_path: &'life1 str,
hash: &'life2 str,
) -> Pin<Box<dyn Future<Output = Result<(), StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
TieredBackend: 'async_trait,
Forget on every tier, best-effort.
A tier that keeps an edge it should have dropped over-reports, which retains a NAR — the safe direction, and the reason this does not abort the fan-out on the first failure.
Source§fn referrers<'life0, 'life1, 'async_trait>(
&'life0 self,
nar_path: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<Vec<String>, StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
TieredBackend: 'async_trait,
fn referrers<'life0, 'life1, 'async_trait>(
&'life0 self,
nar_path: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<Vec<String>, StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
TieredBackend: 'async_trait,
nar_path, sorted and
deduplicated. Read moreSource§impl StorageBackend for TieredBackend
impl StorageBackend for TieredBackend
Source§fn nar_residency(&self) -> NarResidency
fn nar_residency(&self) -> NarResidency
The composite’s residency is its weakest tier’s — a streaming
resolver in front of a whole-value tier still materializes NARs.
Reporting Streaming here because the resolver itself streams would be
exactly the rounding-up this type exists to prevent.
Source§fn get_nar_stream<'life0, 'life1, 'async_trait>(
&'life0 self,
path: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<Option<Pin<Box<dyn Stream<Item = Result<Bytes, StoreError>> + Send>>>, StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
TieredBackend: 'async_trait,
fn get_nar_stream<'life0, 'life1, 'async_trait>(
&'life0 self,
path: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<Option<Pin<Box<dyn Stream<Item = Result<Bytes, StoreError>> + Send>>>, StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
TieredBackend: 'async_trait,
Fall through the tiers and promote, without ever holding the NAR.
Identical resolution order and identical promotion targets to
get_narinfo; the only difference is that a promotion re-reads the tier
that answered (see [TierNarSource]) instead of copying a buffer the
caller is holding. The stream handed back is opened against the tier that
actually had the content, so the caller’s bytes never depend on whether a
warm succeeded.
Source§fn delete_narinfo_record<'life0, 'life1, 'async_trait>(
&'life0 self,
hash: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<(), StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
TieredBackend: 'async_trait,
fn delete_narinfo_record<'life0, 'life1, 'async_trait>(
&'life0 self,
hash: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<(), StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
TieredBackend: 'async_trait,
Fan the narinfo removal out to every tier, then report whether every tier actually removed it.
§Why this is not best-effort, unlike the old delete
Reads fall through, so a narinfo that ANY tier still holds is still
served. If this swallowed a per-tier failure and returned Ok(()), the
composed delete would go on to drop the edge
and remove the NAR — leaving a narinfo that is still served, advertising
a NAR that is gone. That is the strand, arrived at from the other side.
Every tier is still attempted (one dead tier does not stop the others),
but a failure surfaces, so delete stops before touching the NAR. The
cost is that a GC pass against a degraded tier aborts instead of
half-completing — the right trade: a retained NAR is a leak, a stranded
narinfo is an outage.
Source§fn delete_nar_record<'life0, 'life1, 'async_trait>(
&'life0 self,
nar_path: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<(), StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
TieredBackend: 'async_trait,
fn delete_nar_record<'life0, 'life1, 'async_trait>(
&'life0 self,
nar_path: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<(), StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
TieredBackend: 'async_trait,
Fan the NAR removal out to every tier and report any failure.
A surviving copy on one tier is a leak, not an outage — but it is still not a completed delete, and a caller that believes the bytes are gone (a byte-accounting sweep) would drift.
Source§fn put_nar_stream<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
path: &'life1 str,
src: &'life2 dyn NarSource,
) -> Pin<Box<dyn Future<Output = Result<(), StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
TieredBackend: 'async_trait,
fn put_nar_stream<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
path: &'life1 str,
src: &'life2 dyn NarSource,
) -> Pin<Box<dyn Future<Output = Result<(), StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
TieredBackend: 'async_trait,
Fan a NAR out to the tiers in the same order as before, from a re-openable source.
Line for line the previous put_nar, with data: &[u8] replaced by
src: &dyn NarSource and each tier opening its own bounded stream. That
equivalence is the reason NarSource is re-openable rather than a
one-shot Stream: a one-shot stream can be consumed once, so it would
force either buffering the NAR to fan it out (the bug) or interleaving
chunks across tiers (a different order). The load-bearing properties are
unchanged:
- L2 and L3 are both attempted, then gated by
durable_write_outcome; - the L1 warm happens strictly after that gate under
WriteThrough(strictly before it underWriteBack), and its result is discarded, so a refused L1 warm can never fail a build.
Source§fn wipe_all<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<usize, StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
TieredBackend: 'async_trait,
fn wipe_all<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<usize, StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
TieredBackend: 'async_trait,
Fan the wipe out to EVERY tier (L1 hot + L2/L3 durable), so a cache-wipe
clears all three at once. Best-effort per tier (mirrors delete): one
tier’s failure is logged, never aborting the others — the whole point is
to return the store to cold. Reports the largest per-tier narinfo count
removed (the authoritative tiers’ full set).
Source§fn get_narinfo<'life0, 'life1, 'async_trait>(
&'life0 self,
hash: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<Option<String>, StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
TieredBackend: 'async_trait,
fn get_narinfo<'life0, 'life1, 'async_trait>(
&'life0 self,
hash: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<Option<String>, StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
TieredBackend: 'async_trait,
Source§fn get_nar<'life0, 'life1, 'async_trait>(
&'life0 self,
path: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<Option<Vec<u8>>, StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
TieredBackend: 'async_trait,
fn get_nar<'life0, 'life1, 'async_trait>(
&'life0 self,
path: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<Option<Vec<u8>>, StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
TieredBackend: 'async_trait,
Source§fn put_narinfo_record<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
hash: &'life1 str,
content: &'life2 str,
) -> Pin<Box<dyn Future<Output = Result<(), StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
TieredBackend: 'async_trait,
fn put_narinfo_record<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
hash: &'life1 str,
content: &'life2 str,
) -> Pin<Box<dyn Future<Output = Result<(), StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
TieredBackend: 'async_trait,
Source§fn nar_ref_index(&self) -> &dyn NarRefIndex
fn nar_ref_index(&self) -> &dyn NarRefIndex
Source§fn put_nar<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
path: &'life1 str,
data: &'life2 [u8],
) -> Pin<Box<dyn Future<Output = Result<(), StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
TieredBackend: 'async_trait,
fn put_nar<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
path: &'life1 str,
data: &'life2 [u8],
) -> Pin<Box<dyn Future<Output = Result<(), StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
TieredBackend: 'async_trait,
Source§fn list_narinfos<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<Vec<String>, StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
TieredBackend: 'async_trait,
fn list_narinfos<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<Vec<String>, StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
TieredBackend: 'async_trait,
Source§fn put_narinfo<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
hash: &'life1 str,
content: &'life2 str,
) -> Pin<Box<dyn Future<Output = Result<(), StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Self: 'async_trait,
fn put_narinfo<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
hash: &'life1 str,
content: &'life2 str,
) -> Pin<Box<dyn Future<Output = Result<(), StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Self: 'async_trait,
Source§fn advertised_nar<'life0, 'life1, 'async_trait>(
&'life0 self,
hash: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<Option<String>, StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait,
fn advertised_nar<'life0, 'life1, 'async_trait>(
&'life0 self,
hash: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<Option<String>, StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait,
Source§fn delete<'life0, 'life1, 'async_trait>(
&'life0 self,
hash: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<(), StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait,
fn delete<'life0, 'life1, 'async_trait>(
&'life0 self,
hash: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<(), StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait,
Source§fn reindex_nar_refs<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<usize, StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
Self: 'async_trait,
fn reindex_nar_refs<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<usize, StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
Self: 'async_trait,
Auto Trait Implementations§
impl !RefUnwindSafe for TieredBackend
impl !UnwindSafe for TieredBackend
impl Freeze for TieredBackend
impl Send for TieredBackend
impl Sync for TieredBackend
impl Unpin for TieredBackend
impl UnsafeUnpin for TieredBackend
Blanket Implementations§
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
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> Paint for Twhere
T: ?Sized,
impl<T> Paint for Twhere
T: ?Sized,
Source§fn fg(&self, value: Color) -> Painted<&T>
fn fg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the foreground set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like red() and
green(), which have the same functionality but are
pithier.
§Example
Set foreground color to white using fg():
use yansi::{Paint, Color};
painted.fg(Color::White);Set foreground color to white using white().
use yansi::Paint;
painted.white();Source§fn bright_black(&self) -> Painted<&T>
fn bright_black(&self) -> Painted<&T>
Source§fn bright_red(&self) -> Painted<&T>
fn bright_red(&self) -> Painted<&T>
Source§fn bright_green(&self) -> Painted<&T>
fn bright_green(&self) -> Painted<&T>
Source§fn bright_yellow(&self) -> Painted<&T>
fn bright_yellow(&self) -> Painted<&T>
Source§fn bright_blue(&self) -> Painted<&T>
fn bright_blue(&self) -> Painted<&T>
Source§fn bright_magenta(&self) -> Painted<&T>
fn bright_magenta(&self) -> Painted<&T>
Source§fn bright_cyan(&self) -> Painted<&T>
fn bright_cyan(&self) -> Painted<&T>
Source§fn bright_white(&self) -> Painted<&T>
fn bright_white(&self) -> Painted<&T>
Source§fn bg(&self, value: Color) -> Painted<&T>
fn bg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the background set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like on_red() and
on_green(), which have the same functionality but
are pithier.
§Example
Set background color to red using fg():
use yansi::{Paint, Color};
painted.bg(Color::Red);Set background color to red using on_red().
use yansi::Paint;
painted.on_red();Source§fn on_primary(&self) -> Painted<&T>
fn on_primary(&self) -> Painted<&T>
Source§fn on_magenta(&self) -> Painted<&T>
fn on_magenta(&self) -> Painted<&T>
Source§fn on_bright_black(&self) -> Painted<&T>
fn on_bright_black(&self) -> Painted<&T>
Source§fn on_bright_red(&self) -> Painted<&T>
fn on_bright_red(&self) -> Painted<&T>
Source§fn on_bright_green(&self) -> Painted<&T>
fn on_bright_green(&self) -> Painted<&T>
Source§fn on_bright_yellow(&self) -> Painted<&T>
fn on_bright_yellow(&self) -> Painted<&T>
Source§fn on_bright_blue(&self) -> Painted<&T>
fn on_bright_blue(&self) -> Painted<&T>
Source§fn on_bright_magenta(&self) -> Painted<&T>
fn on_bright_magenta(&self) -> Painted<&T>
Source§fn on_bright_cyan(&self) -> Painted<&T>
fn on_bright_cyan(&self) -> Painted<&T>
Source§fn on_bright_white(&self) -> Painted<&T>
fn on_bright_white(&self) -> Painted<&T>
Source§fn attr(&self, value: Attribute) -> Painted<&T>
fn attr(&self, value: Attribute) -> Painted<&T>
Enables the styling Attribute value.
This method should be used rarely. Instead, prefer to use
attribute-specific builder methods like bold() and
underline(), which have the same functionality
but are pithier.
§Example
Make text bold using attr():
use yansi::{Paint, Attribute};
painted.attr(Attribute::Bold);Make text bold using using bold().
use yansi::Paint;
painted.bold();Source§fn rapid_blink(&self) -> Painted<&T>
fn rapid_blink(&self) -> Painted<&T>
Source§fn quirk(&self, value: Quirk) -> Painted<&T>
fn quirk(&self, value: Quirk) -> Painted<&T>
Enables the yansi Quirk value.
This method should be used rarely. Instead, prefer to use quirk-specific
builder methods like mask() and
wrap(), which have the same functionality but are
pithier.
§Example
Enable wrapping using .quirk():
use yansi::{Paint, Quirk};
painted.quirk(Quirk::Wrap);Enable wrapping using wrap().
use yansi::Paint;
painted.wrap();Source§fn clear(&self) -> Painted<&T>
👎Deprecated since 1.0.1: renamed to resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.
fn clear(&self) -> Painted<&T>
renamed to resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.
Source§fn whenever(&self, value: Condition) -> Painted<&T>
fn whenever(&self, value: Condition) -> Painted<&T>
Conditionally enable styling based on whether the Condition value
applies. Replaces any previous condition.
See the crate level docs for more details.
§Example
Enable styling painted only when both stdout and stderr are TTYs:
use yansi::{Paint, Condition};
painted.red().on_yellow().whenever(Condition::STDOUTERR_ARE_TTY);