Skip to main content

LocalDiskStore

Struct LocalDiskStore 

Source
pub struct LocalDiskStore { /* private fields */ }
Expand description

Local filesystem CAS: one file per content hash under root.

Implementations§

Trait Implementations§

Source§

impl ContentStore for LocalDiskStore

Source§

fn put(&self, key: &InputHash, bytes: &[u8]) -> Result<(), Error>

Store bytes under key, atomically.

§#2 — Model R concurrent-writer safety

Before Model R, daemons were single-writer per process and PID-scoped their CAS dir, so an in-place fs::write was adequate. Model R’s whole point is a shared --cas-dir across a fleet of daemons: N writers, multiple processes, one directory. An in-place truncate-then-write is not safe there — a concurrent reader doing contains()get() can observe a half-written file. Content-addressing guarantees the final bytes converge (same key ⇒ identical bytes); it does not make an interleaved read whole.

Fix: write to a unique sibling temp file, fsync it, then rename it onto the final path. POSIX rename(2) within one filesystem is atomic — a reader sees either the old state (absent) or the complete new file, never a torn one. If another writer wins the race the rename simply replaces an identical-content file (content-addressed), so the result is still correct and the put stays idempotent (the AC#5 cache-hit invariant). The exists() fast-path is kept so a cache hit costs one stat, not a rewrite.

(Windows non-atomic-replace is out of scope — CLAUDE.md parks Windows in v1; macOS + Linux are the supported fleet hosts.)

Source§

fn get(&self, key: &InputHash) -> Result<Option<Vec<u8>>, Error>

Fetch the bytes for key, or None if absent.
Source§

fn contains(&self, key: &InputHash) -> Result<bool, Error>

Whether key is already present — the build pipeline checks this to skip a build entirely on a cache hit.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.