Skip to main content

NarRefIndex

Trait NarRefIndex 

Source
pub trait NarRefIndex: Send + Sync {
    // Required methods
    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,
             Self: '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,
             Self: '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,
             Self: 'async_trait;
}
Expand description

The reverse index of a single StorageBackend.

Three verbs, all idempotent. Every implementation persists edges in the backend’s own store, so the index survives exactly as long as the data it describes.

§Which way to be wrong

Over-reporting a referrer keeps a NAR that could have been reclaimed — a leak. Under-reporting deletes a NAR another narinfo still advertises — an outage. Every implementation rounds toward over-reporting, and any place that cannot (a hot tier’s key expiring, a fan-out where one tier is down) says so at that site.

Required Methods§

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

Record “hash’s narinfo advertises nar_path”. Idempotent.

§Errors

Propagates the backend’s write failure.

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

Forget that edge. Idempotent — forgetting an absent edge is Ok(()).

§Errors

Propagates the backend’s delete 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, Self: 'async_trait,

Every store-path hash whose narinfo advertises nar_path, sorted and deduplicated.

§Errors

Propagates the backend’s read failure. An empty vector is a real answer (“nothing advertises this NAR”), never a stand-in for a failed lookup — which is why this returns Result<Vec<_>> and not Vec<_>.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§

Source§

impl NarRefIndex for LocalStorage

The reverse index as a directory tree: one empty file per edge.

record is a blind create of a path that names its own content, so it is idempotent and two writers racing on the same edge simply write the same empty file. Nothing here reads a set to write it back, which is why concurrent narinfo pushes cannot lose an edge.

Source§

impl NarRefIndex for MemNarRefIndex

Source§

impl NarRefIndex for S3Storage

The reverse index as one zero-byte object per edge, under nar-refs/.

A LIST bounded by the edge prefix is the referrer set. Recording is a blind PUT of a key that names its own content, so it is idempotent and two concurrent pushes cannot lose an edge — which a read-modify-write of a set-valued object could, and S3 gives no compare-and-swap to prevent it with.

Source§

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§

impl<C> NarRefIndex for PgStorageBackend<C>
where C: PgCacheConn + 'static,

The reverse index as one zero-value row per edge in sui_cache_nar_ref.

The key is the canonical NarRefKey, so the referrer lookup is a primary-key range scan under NarRefScan rather than a table sweep.

Source§

impl<C> NarRefIndex for RedisBackend<C>
where C: RedisConn,

The reverse index as one Redis key per edge.

Edges carry no TTL, deliberately, even when narinfo/NAR writes do. An edge that expired while the narinfo it describes is still live would be an under-report, and an under-report authorizes deleting a NAR out from under that narinfo. An edge that outlives its narinfo is an over-report, which costs a retained NAR. The whole tier is still best-effort — Redis maxmemory LRU can drop an edge regardless — which is why a TieredBackend unions this tier’s answer with the durable tiers’ rather than trusting it alone.