pub struct CommitHistory<X: Executor = TokioExecutor> { /* private fields */ }Expand description
Append-only Merkle history of commit hashes for one branch.
Two construction modes:
CommitHistory::open— purely in-memory. Useful for tests and for callers that don’t want any disk side-effects. Lost on drop.CommitHistory::open_at— on-disk journaled MMR under<mkit_dir>/history/<branch>/. Survives process exit.
Each call to a sync method (append, root, prove) on the
journaled flavour drives an async operation on the underlying
commonware-storage::journaled::Mmr via the caller-supplied
Executor.
Implementations§
Source§impl<X: Executor + 'static> CommitHistory<X>
impl<X: Executor + 'static> CommitHistory<X>
Sourcepub fn open_at(
executor: Arc<X>,
layout: &RepoLayout,
branch: &str,
) -> Result<Self, HistoryError>
pub fn open_at( executor: Arc<X>, layout: &RepoLayout, branch: &str, ) -> Result<Self, HistoryError>
Open a persisted history under <common dir>/history/<branch>/.
The on-disk layout is commonware-storage’s native journaled MMR
shape — see HISTORY_DIR and SPEC-HISTORY-PROOF §4. If the
underlying journal does not yet exist, it is created empty
(subsequent CommitHistory::append calls populate it).
Crash recovery is delegated to commonware: its Journaled::init
detects a half-written trailing leaf, rewinds the journal to
the last valid size, and re-derives the in-memory state. See
SPEC-HISTORY-PROOF §4.4 for the contract surfaced to mkit
callers.
§Errors
HistoryError::InvalidBranch—branchfailedvalidate_ref_name.HistoryError::RuntimeBootstrap— could not start the commonware tokio Runner used to construct the underlying storage Context.HistoryError::Corrupted— commonware refused to recover the journal (a deeper-than-trailing-leaf corruption).HistoryError::Io— failed to create the history dir.
Sourcepub fn common_dir(&self) -> Option<&Path>
pub fn common_dir(&self) -> Option<&Path>
Borrow the common dir this history was opened against. None
for the mem-only flavour. Used by
crate::refs::update_ref_with_history to take a RepoLock
around the ref-write + MMR-append critical section.
Sourcepub fn branch(&self) -> Option<&str>
pub fn branch(&self) -> Option<&str>
Borrow the branch name this history was opened against. None
for the mem-only flavour.
Sourcepub fn reopen(&mut self) -> Result<(), HistoryError>
pub fn reopen(&mut self) -> Result<(), HistoryError>
Re-derive this handle’s in-memory state from the current on-disk journal. A no-op for the mem-only flavour.
CommitHistory::open_at may run before the caller has taken
any cross-process lock (opening the journal is cheap and the
lock is only needed around the ref-write + append critical
section — see crate::refs::update_ref_with_history). If
another process appended to the same on-disk journal in the
window between that open_at and this handle’s own locked
critical section, this handle’s leaf count / root would be
stale, and appending against stale state risks writing a leaf
at a position the on-disk journal has already used. Callers
that hold a cross-process lock around their critical section
MUST call this once after acquiring it and before appending, so
the append is always against what is truly on disk.
§Errors
Same as CommitHistory::open_at: HistoryError::Corrupted
if commonware cannot recover the journal,
HistoryError::RuntimeBootstrap if the runtime bootstrap
fails, HistoryError::Io for filesystem failures.
§Bootstrap cost (issue #640)
This does NOT spawn a second commonware Context bootstrap.
CommitHistory::open_at’s bootstrap thread already built a
full tokio runtime, metrics task, and buffer pools for this
handle; re-running that per call would double the cost of
every history-tracked ref write for no benefit, since the
bootstrapped Context is reusable — only the MMR’s in-memory
view of the on-disk journal needs re-deriving. reopen takes a
labelled child of the handle’s own already-live Context and
re-runs JournaledMmr::init against it, which is what actually
picks up a concurrent writer’s append.
Sourcepub fn append(&mut self, commit_hash: &Hash) -> Result<Position, HistoryError>
pub fn append(&mut self, commit_hash: &Hash) -> Result<Position, HistoryError>
Append a commit hash. Returns its leaf Position.
Positions are dense: the n-th append returns Position(n).
For the journaled flavour, the underlying MMR is sync’d to
disk before returning — survives a SIGKILL immediately after.
A thin wrapper over Self::append_no_sync + Self::sync —
see those for the split. Callers appending many leaves in one
critical section (e.g. rebuild_from_chain’s backfill) should
call the two halves directly instead, batching the fsync.
Sourcepub fn root(&self) -> Hash
pub fn root(&self) -> Hash
Current MMR root digest. 32-byte BLAKE3 (mkit Hash shape).
Defined for an empty history — commonware returns a deterministic empty-MMR root (see SPEC-HISTORY-PROOF §2.3).
§Panics
Never panics in practice. Internally calls commonware’s
root(&hasher, 0), which only returns an error for a non-zero
inactive-peak count; mkit always requests 0 inactive peaks
(its proofs are self-contained over the full leaf set), so the
Result is always Ok.
Sourcepub fn prove(&self, position: Position) -> Result<InclusionProof, HistoryError>
pub fn prove(&self, position: Position) -> Result<InclusionProof, HistoryError>
Build an inclusion proof for the commit at position.
Sourcepub fn destroy(self) -> Result<(), HistoryError>
pub fn destroy(self) -> Result<(), HistoryError>
Permanently delete this history’s on-disk journal + metadata partitions (issue #648).
Consumes self: there is no valid handle to keep using once the
backing storage is gone. A no-op for the mem-only flavour (there
is nothing on disk to remove).
This is the primitive crate::refs::delete_ref_with_history
uses to fence a branch’s journal on branch -d / branch -m:
without it, re-creating a branch with a previously-used name
would reopen the dead incarnation’s non-empty journal (same
sanitized partition name) and resume appending on top of its old
leaves — see SPEC-HISTORY-PROOF and issue #648 for the full
write-up of the resulting stale-inclusion-proof bug.
§Errors
HistoryError::Mmr if commonware’s underlying journal/metadata
destroy() fails (e.g. an I/O error removing the partition
files).
Trait Implementations§
Source§impl<X: Executor> Debug for CommitHistory<X>
impl<X: Executor> Debug for CommitHistory<X>
Source§impl Default for CommitHistory<TokioExecutor>
impl Default for CommitHistory<TokioExecutor>
Auto Trait Implementations§
impl<X = TokioExecutor> !RefUnwindSafe for CommitHistory<X>
impl<X = TokioExecutor> !UnwindSafe for CommitHistory<X>
impl<X> Freeze for CommitHistory<X>
impl<X> Send for CommitHistory<X>
impl<X> Sync for CommitHistory<X>
impl<X> Unpin for CommitHistory<X>
impl<X> UnsafeUnpin for CommitHistory<X>
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> 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