pub trait StorageBackend: Send + Sync {
Show 16 methods
// Required methods
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,
Self: '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,
Self: '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,
Self: '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,
Self: 'async_trait;
fn nar_ref_index(&self) -> &dyn NarRefIndex;
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,
Self: '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,
Self: 'async_trait;
fn nar_residency(&self) -> NarResidency;
fn list_narinfos<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<Vec<String>, StoreError>> + Send + 'async_trait>>
where 'life0: 'async_trait,
Self: 'async_trait;
// Provided methods
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,
Self: '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,
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 { ... }
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 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 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 wipe_all<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<usize, StoreError>> + Send + 'async_trait>>
where 'life0: 'async_trait,
Self: 'async_trait { ... }
}Expand description
Abstraction over binary cache storage.
Narinfo files are keyed by the 32-character store path hash.
NAR blobs are keyed by their relative URL path (e.g. nar/<hash>.nar.xz).
§NAR verbs come in two shapes; prefer the streaming pair
get_nar / put_nar
hand whole Vec<u8> / &[u8] values across the boundary and are therefore
O(nar) resident by signature. They remain for callers that genuinely have
or want the whole thing (tests, small values, the GC).
get_nar_stream /
put_nar_stream move the same content in
NAR_CHUNK_BYTES chunks and are what the HTTP server and every tier-to-tier
transfer use. narinfo is ~728 bytes and deliberately has no streaming pair.
§Record verbs vs. composed verbs
The *_record verbs (put_narinfo_record,
delete_narinfo_record,
delete_nar_record) each touch exactly
one key and maintain nothing. They are what a backend implements.
put_narinfo and
delete are composed on top: they keep the
NarRefIndex in step and, in delete’s case, refuse to remove a NAR that
another narinfo still advertises. They are provided, so a backend cannot
forget to index — see nar_refs for what the two directions cost.
Required Methods§
Sourcefn 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,
Self: '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,
Self: 'async_trait,
Retrieve narinfo text by store path hash.
Sourcefn 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,
Self: '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,
Self: 'async_trait,
Store narinfo text keyed by store path hash — the record verb: one key, no index maintenance.
Callers want put_narinfo, which also records the
reverse edge.
Sourcefn 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,
Self: '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,
Self: 'async_trait,
Remove a narinfo record by store path hash. Idempotent; removing an
absent narinfo is Ok(()).
The record verb: it does not touch the NAR the narinfo advertises and
does not maintain the index. Callers want delete.
Sourcefn 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,
Self: '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,
Self: 'async_trait,
Remove one NAR blob by its relative path. Idempotent.
The record verb, and the one with teeth: removing a NAR that a live
narinfo still advertises is the stranding hazard this module exists to
prevent, and nothing here checks. Callers want delete;
reach for this directly only after consulting
nar_ref_index.
Sourcefn nar_ref_index(&self) -> &dyn NarRefIndex
fn nar_ref_index(&self) -> &dyn NarRefIndex
This backend’s narhash → store-hash reverse index.
Required — no default. A default would be an empty index, and an
empty index does not read as “unknown”, it reads as “nobody advertises
this NAR” — which is precisely the answer that authorizes deleting a NAR
out from under a live narinfo. Same mechanism, and the same reason, as
nar_residency: the decision cannot be omitted.
Sourcefn 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,
Self: '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,
Self: 'async_trait,
Retrieve a NAR blob by its relative path.
O(nar) resident. Prefer get_nar_stream on
any path that serves real build artifacts.
Sourcefn 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,
Self: '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,
Self: 'async_trait,
Store a NAR blob at the given relative path.
O(nar) resident. Prefer put_nar_stream on
any path that ingests real build artifacts.
Sourcefn nar_residency(&self) -> NarResidency
fn nar_residency(&self) -> NarResidency
Declare what this backend’s NAR path costs in resident memory.
Required — no default. See NarResidency for why.
Sourcefn list_narinfos<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<Vec<String>, StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
Self: '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,
Self: 'async_trait,
List all stored narinfo hashes.
Provided Methods§
Sourcefn 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,
Self: '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,
Self: 'async_trait,
Retrieve a NAR blob as a bounded-chunk stream.
The default materializes via get_nar — correct, and
O(nar) resident. A backend that declares
NarResidency::Streaming must override this.
§Errors
Propagates the backend’s read failure. Ok(None) is a clean miss.
Sourcefn 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,
Self: '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,
Self: 'async_trait,
Store a NAR blob from a re-openable bounded-chunk source.
The default drains the source into one buffer and calls
put_nar — correct, and O(nar) resident. A backend
that declares NarResidency::Streaming must override this.
§Errors
Propagates the source’s read failure or the backend’s write failure.
Sourcefn 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,
Store narinfo text and record the reverse edge it creates.
§Ordering, and why it is this way round
The edge is recorded before the narinfo record is written. A crash
between the two then leaves an edge with no narinfo — an over-report,
which costs a NAR that could have been reclaimed. The other order leaves
a narinfo with no edge, which is an under-report, and an under-report is
what lets a later delete take the NAR this narinfo advertises. Leak
over strand, every time.
A narinfo whose URL: is not an addressable relative path is refused
(see is_addressable_nar_path): it arrives over the wire, it is used
as a key and joined onto a filesystem root, and there is no sanitizing it
safely at each of those uses. Text carrying no URL: at all is stored
as-is and indexes nothing — it advertises no NAR, so there is nothing to
strand.
§Errors
Propagates the index write or the record write, and returns
StoreError::NarInfo for an unaddressable URL:.
Sourcefn 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,
The NAR path this store path’s narinfo advertises — resolved, never guessed.
None means the narinfo is absent, unparseable, or advertises a URL this
store will not address. All three mean the same thing to a caller: there
is no NAR here it is entitled to touch.
§Errors
Propagates the narinfo read failure. A read failure is not flattened
into None: “the tier is down” must never be mistaken for “this path has
no NAR” by something about to delete.
Sourcefn 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,
Delete a store path’s narinfo, and its NAR only if nothing else advertises that NAR.
§What changed, and why it is not cosmetic
This used to guess: every backend best-effort-deleted
nar/{store-hash}.{xz,zst,nar}. The NAR is keyed by narhash, not by
store hash, so the guess normally deleted three keys that were never this
path’s NAR and left the real one behind. It now resolves the key from
the narinfo’s own URL:.
Resolving alone would be a regression: two store paths with identical
contents share one narhash and therefore one URL:, so deleting either
would take the NAR the other still advertises — and a narinfo whose
advertised NAR 404s is a hard Nix failure, not a cache miss. So the NAR
goes only when nar_ref_index reports no other
referrer.
§Ordering
The narinfo record is removed first, then its edge. A crash between
the two leaves a stale edge — an over-report that costs a retained NAR.
The other order would leave a live narinfo with no edge, and the next
delete of a co-referrer would strand it.
§Errors
Propagates the narinfo read, the record delete, or the index update.
Sourcefn 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,
Rebuild every reverse edge from the narinfos this backend holds, and return the number of edges recorded.
The index is maintained forward from put_narinfo,
so a store filled before the index existed has none — and an absent
edge reads as “nobody advertises this NAR”. Running this once after an
upgrade closes that gap; it is idempotent, so running it again is free.
O(narinfos), one narinfo read each: a maintenance verb, not something on a request path.
§Errors
Propagates the listing, a narinfo read, or an index write.
Sourcefn wipe_all<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<usize, StoreError>> + Send + 'async_trait>>where
'life0: 'async_trait,
Self: '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,
Self: 'async_trait,
Clear EVERY narinfo and NAR blob from this backend. Returns the number of narinfos removed.
The default lists every narinfo and best-effort deletes it (narinfo-only
clear; NAR blobs keyed by narhash are not reached). Concrete durable
tiers override with a real truncation that reclaims NAR bytes.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".