pub struct ObjectStore { /* private fields */ }Expand description
Local content-addressed object store backed by the filesystem.
Implementations§
Source§impl ObjectStore
impl ObjectStore
Sourcepub fn open(layout: &RepoLayout) -> StoreResult<Self>
pub fn open(layout: &RepoLayout) -> StoreResult<Self>
Open the existing repository described by layout. Returns
StoreError::NotAMkitRepository if the layout’s objects/
directory does not exist. The object store is common-dir
(shared) state — see crate::layout.
Sourcepub fn init(layout: &RepoLayout) -> StoreResult<Self>
pub fn init(layout: &RepoLayout) -> StoreResult<Self>
Initialise a fresh common dir for the repository described by
layout. Returns StoreError::AlreadyInitialized if the
common dir already exists.
Sourcepub fn set_sync_policy(&mut self, policy: SyncPolicy)
pub fn set_sync_policy(&mut self, policy: SyncPolicy)
Select the SyncPolicy that Self::batch hands out. The
CLI wires the repo config key durability.objects here so
deployments on filesystems where they prefer the strict
per-object schedule can opt into it (SPEC-OBJECTS §10.1).
Sourcepub fn batch(&self) -> WriteBatch<'_>
pub fn batch(&self) -> WriteBatch<'_>
Start a batched write with the store’s configured policy
(default SyncPolicy::Batch): objects staged by the batch
become durable and visible together at WriteBatch::commit,
with O(1) full flushes per batch instead of per object.
Sourcepub fn batch_with_policy(&self, policy: SyncPolicy) -> WriteBatch<'_>
pub fn batch_with_policy(&self, policy: SyncPolicy) -> WriteBatch<'_>
Start a batched write with an explicit SyncPolicy.
Sourcepub fn is_repo_root(root: &Path) -> bool
pub fn is_repo_root(root: &Path) -> bool
Returns true when root contains a .mkit/objects directory.
Sourcepub fn objects_root(&self) -> &Path
pub fn objects_root(&self) -> &Path
Absolute path to the objects/ directory.
Sourcepub fn contains(&self, h: &Hash) -> bool
pub fn contains(&self, h: &Hash) -> bool
Returns true when the object h is present in the store. Does
not verify integrity — use Self::read for that.
Sourcepub fn write(&self, bytes: &[u8]) -> StoreResult<Hash>
pub fn write(&self, bytes: &[u8]) -> StoreResult<Hash>
Write bytes to the store, returning their BLAKE3 hash. Atomic:
writes to a sibling temp file, fsyncs, then renames into place.
Idempotent — re-writing the same bytes is a no-op (the temp file
is unlinked on the early-return path).
§Panics
Panics only if the internal hash-to-path mapping produces a path without a parent directory, which is impossible by construction.
Sourcepub fn bulk_writer(&self) -> BulkWriter<'_>
pub fn bulk_writer(&self) -> BulkWriter<'_>
Begin a bulk-write session: each new object is written
durable-before-visible (contents fsynced, then renamed), and
BulkWriter::commit batches the directory fsyncs (rename
durability) plus a content fsync of any re-used pre-existing
objects once at the end, instead of fsyncing a dir per write.
Because content is durable before the rename, the session upholds
the store’s global invariant even under concurrent readers: an
object another process can contains()-dedup against is always
backed by durable bytes.
Crash-safety contract (deliberately weaker than Self::write
only for the rename/dirent half, for callers whose whole
operation is idempotent — e.g. the deterministic git-import,
which re-runs from a retained source mirror): after a crash
BEFORE commit, a just-renamed object’s dirent may be lost (the
shard dir is not yet fsynced), so objects may be missing — never
torn, since contents were fsynced first. Existing paths are
VERIFIED (byte compare)
rather than blindly rewritten or blindly trusted: a matching
file is left in place with only its mtime refreshed (it may be
durable and referenced by native history — replacing it with an
unsynced inode would put it at risk), a torn one is healed by
rewrite, and reads always
BLAKE3-verify. Callers MUST gate bulk sessions behind their
own crash marker and re-run on detection.
Sourcepub fn read(&self, h: &Hash) -> StoreResult<Vec<u8>>
pub fn read(&self, h: &Hash) -> StoreResult<Vec<u8>>
Read raw bytes for h. Verifies that BLAKE3 of the on-disk
bytes equals h and returns StoreError::HashMismatch on
failure (the bytes are still discarded so callers cannot
accidentally use corrupt data).
Sourcepub fn read_unverified(&self, h: &Hash) -> StoreResult<Vec<u8>>
pub fn read_unverified(&self, h: &Hash) -> StoreResult<Vec<u8>>
Read raw bytes for h WITHOUT the BLAKE3 integrity check that
Self::read performs — a StoreError::HashMismatch can
therefore never come out of this path.
§Policy — display-only paths ONLY (#625)
This exists for hot read-only rendering (diff/show/commit
summaries formatting a diffstat or patch preview). It is the same
“cheap read” tradeoff Self::object_type already makes for shape
checks (see its doc and Self::verify_object_type, which is the
verifying twin used by tree-publication paths) — extended here to
the full object body.
NEVER use this for publication (commit/merge/rebase tree writes),
dedup, fetch/apply, or any path whose output is consumed as data
rather than displayed and discarded. mkit never feeds unverified
bytes into state that mkit itself writes, or into output that
mkit’s own tooling round-trips (e.g. format-patch piped into
git am). On corruption, callers of read get a loud, typed error
before anything downstream can act on bad bytes; callers of
read_unverified get whatever a decoder makes of the corrupt bytes
— a decode error, or in the worst case garbage in the rendered
output — but never durable state, because display paths write
nothing back to the store.
Prefer wrapping the source with DisplaySource at the render
call site over calling this directly; that keeps the verify/
no-verify choice at the boundary instead of threading a flag
through render code.
Sourcepub fn object_type(&self, h: &Hash) -> StoreResult<ObjectType>
pub fn object_type(&self, h: &Hash) -> StoreResult<ObjectType>
The object’s type tag, from its 6-byte prologue — without reading or hash-verifying the body. Backs cheap shape checks (e.g. “is this staged hash blob-like?”) that previously paid a full read+BLAKE3 of every staged blob per status/commit; the real read path still integrity-verifies at use time.
§Errors
StoreError::ObjectNotFound if absent; StoreError::Decode
for a short file, bad magic/version, or unknown tag.
Sourcepub fn read_object(&self, h: &Hash) -> StoreResult<Object>
pub fn read_object(&self, h: &Hash) -> StoreResult<Object>
Convenience: read raw bytes and decode into a typed Object.
A merkelized type (Object::Tree / Object::ChunkedBlob) is
already decoded once by verified_id_and_object to compute its
BMT-root id for the integrity check below — reused directly here
instead of a second deserialize of the same bytes (the two
decodes this function used to pay for every tree/chunked-blob
read, once inside Self::read’s verification and once here).
Every other type was only ever decoded once (verification there is
a plain BLAKE3(bytes), no decode), so this path is unchanged for
them.
Sourcepub fn verify_object_type(&self, h: &Hash) -> StoreResult<ObjectType>
pub fn verify_object_type(&self, h: &Hash) -> StoreResult<ObjectType>
Hash-verifying variant of object_type: reads
the whole object and confirms its content hashes to h before
returning the declared type. This is the integrity guard for
tree-publication paths (commit, merge, rebase, …) — object_type
alone reads only the 6-byte prologue, so a staged object corrupted
after add would otherwise be published into a durable tree and
only fail at later read time. Use object_type on hot read-only
paths (status/diff snapshots) where nothing durable is published.
Sourcepub fn iter_object_hashes(&self) -> StoreResult<Vec<Hash>>
pub fn iter_object_hashes(&self) -> StoreResult<Vec<Hash>>
Enumerate every object hash currently in the store by walking
objects/<2-hex>/<62-hex>. Entries whose names are not the
expected hex shape (stray files, atomic-write temp files, unknown
dirs) are skipped — they are not objects. Used by gc to find
prune candidates.
§Errors
StoreError::Io if a directory cannot be read (gc must then
fail closed rather than prune against a partial enumeration).
Sourcepub fn object_metadata(&self, h: &Hash) -> StoreResult<Metadata>
pub fn object_metadata(&self, h: &Hash) -> StoreResult<Metadata>
Filesystem metadata for object h (size + mtime), for gc’s
grace-window check.
§Errors
StoreError::ObjectNotFound if absent, else StoreError::Io.
Sourcepub fn remove_object(&self, h: &Hash) -> StoreResult<()>
pub fn remove_object(&self, h: &Hash) -> StoreResult<()>
Delete object h from the store. Idempotent: a missing object is
not an error (it may have been pruned already).
§Errors
StoreError::Io on a filesystem failure other than not-found.
Trait Implementations§
Source§impl Clone for ObjectStore
impl Clone for ObjectStore
Source§impl Debug for ObjectStore
impl Debug for ObjectStore
Source§impl DeltaBaseSource for &ObjectStore
impl DeltaBaseSource for &ObjectStore
Source§const VERIFIED: bool = true
const VERIFIED: bool = true
ObjectStore::read BLAKE3/BMT-verifies the bytes against id
and fails with HashMismatch otherwise.
Source§fn base(&mut self, id: &Hash) -> Result<Option<Vec<u8>>, PackError>
fn base(&mut self, id: &Hash) -> Result<Option<Vec<u8>>, PackError>
id, or None when this source may
not provide it. “Not permitted” and “does not exist” MUST both
be None: the decoder reports them identically, by design. Read moreSource§fn base_with_admission(
&mut self,
id: &Hash,
admit: impl FnOnce(usize) -> Result<(), PackError>,
) -> Result<Option<Vec<u8>>, PackError>
fn base_with_admission( &mut self, id: &Hash, admit: impl FnOnce(usize) -> Result<(), PackError>, ) -> Result<Option<Vec<u8>>, PackError>
base
returns; sources that know the length first should override this to
call admit before allocating, as &ObjectStore does. Before
returning Some(bytes), every override must successfully invoke
admit exactly once with bytes.len() and propagate its error. Read moreSource§impl ObjectSink for ObjectStore
impl ObjectSink for ObjectStore
Source§impl ObjectSource for ObjectStore
impl ObjectSource for ObjectStore
Source§fn read_object(&self, h: &Hash) -> StoreResult<Object>
fn read_object(&self, h: &Hash) -> StoreResult<Object>
Overrides the trait default (deserialize(self.read(h)?), which
would decode a Tree/ChunkedBlob twice — once inside read’s
id verification, once again here) to delegate to the inherent
ObjectStore::read_object, which decodes once and reuses the
result. Every caller generic over S: ObjectSource (diff::load_tree,
LoadedBlob::load, …) resolves read_object through this trait
impl rather than the inherent method even when S = ObjectStore,
so without this override those call sites — the primary
Tree/ChunkedBlob decoding paths — would keep paying for the
double decode the inherent method was written to avoid.
Source§fn read(&self, h: &Hash) -> StoreResult<Vec<u8>>
fn read(&self, h: &Hash) -> StoreResult<Vec<u8>>
h. Read moreSource§fn read_unverified(&self, h: &Hash) -> StoreResult<Vec<u8>>
fn read_unverified(&self, h: &Hash) -> StoreResult<Vec<u8>>
h without integrity verification, for display-only
rendering — see ObjectStore::read_unverified for the full
policy (#625). Defaults to the fully-verifying Self::read, so
every existing and third-party ObjectSource stays verifying
unless it explicitly opts in by overriding this method. Read moreSource§impl ObjectSource for &ObjectStore
impl ObjectSource for &ObjectStore
Source§fn fetch(&mut self, id: &Hash) -> Result<Option<Cow<'_, [u8]>>, VerifyError>
fn fetch(&mut self, id: &Hash) -> Result<Option<Cow<'_, [u8]>>, VerifyError>
id, or Ok(None) when it is absent.
Implementations MUST NOT use their own identity decision as the
verification result; the closure walker always deserializes the bytes
and re-derives the object id itself. Read moreAuto Trait Implementations§
impl !RefUnwindSafe for ObjectStore
impl !UnwindSafe for ObjectStore
impl Freeze for ObjectStore
impl Send for ObjectStore
impl Sync for ObjectStore
impl Unpin for ObjectStore
impl UnsafeUnpin for ObjectStore
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> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> FutureExt for T
impl<T> FutureExt for T
Source§fn with_context(self, otel_cx: Context) -> WithContext<Self> ⓘ
fn with_context(self, otel_cx: Context) -> WithContext<Self> ⓘ
Source§fn with_current_context(self) -> WithContext<Self> ⓘ
fn with_current_context(self) -> WithContext<Self> ⓘ
impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
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 more