Skip to main content

StorageBackend

Trait StorageBackend 

Source
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§

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, Self: 'async_trait,

Retrieve narinfo text by store path hash.

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, 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.

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, 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.

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, 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.

Source

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.

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, 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.

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, 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.

Source

fn nar_residency(&self) -> NarResidency

Declare what this backend’s NAR path costs in resident memory.

Required — no default. See NarResidency for why.

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, Self: 'async_trait,

List all stored narinfo hashes.

Provided Methods§

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, 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.

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, 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.

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,

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:.

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,

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.

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,

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.

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,

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.

Source

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".

Implementors§