pub struct RemoteRefBatch<'a> { /* private fields */ }Expand description
Batched writer for remote-tracking refs (#645): amortises the
parent-directory fsync across every ref written into it, instead of
paying one per ref like write_remote_ref in a loop.
push/fetch publish one tracking ref per branch
(refs/remotes/<remote>/<branch>) in a loop; each individual
write_remote_ref call goes through write_atomic’s
temp+fsync+rename+dirsync pattern, so N branches cost N serial
directory fsyncs even though every write lands under the same
refs/remotes/<remote>/ tree. RemoteRefBatch instead:
Self::writes each ref durable-content-before-visible (fsyncs the wire bytes, then renames — same invariantcrate::atomic::write_content_syncedgives object writes: a reader can never observe a torn file), immediately, one call at a time, deferring only the directory fsync that makes the RENAME itself crash-durable;Self::commitfsyncs every distinct directory touched, once each, deduplicated — O(distinct directories) instead of O(refs). For the common case (one remote, flat branch names) that is exactly one fsync for the whole batch.
This is deliberately scoped to refs/remotes/* ONLY — see
atomic.rs’s module docs for why branch heads (refs/heads/*,
write_ref/update_ref) keep the unbatched per-write contract:
tracking refs are a locally-cached, recomputable-from-a-re-fetch
view of another repository, not a pointer anything else orders
against.
§Partial-failure semantics
Best-effort, fail-fast — the same contract
crate::batch::WriteBatch::commit documents for the object store’s
batched writes. Self::write renames each ref as soon as it
validates and its content is durable, so a ref that made it through
write is visible to readers immediately, exactly as it would have
been under the old per-ref loop. If a later write in the same
batch fails (bad name or I/O error), earlier successful writes are
not rolled back — content-addressed dedup isn’t in play here,
but the same reasoning applies: remote-tracking refs are
recomputable, so a partially-applied batch is exactly as safe to
retry as the old loop was after failing at the same point. Callers
that want the successful prefix to be durable even when the batch as
a whole errors out should call Self::commit regardless of
whether the write loop returned early (see
remote_dispatch::push_all_with / fetch_objects_inner for the
pattern).
Implementations§
Source§impl<'a> RemoteRefBatch<'a>
impl<'a> RemoteRefBatch<'a>
Sourcepub fn new(layout: &'a RepoLayout, remote: &str) -> RefResult<Self>
pub fn new(layout: &'a RepoLayout, remote: &str) -> RefResult<Self>
Sourcepub fn write(&mut self, branch: &str, h: &Hash) -> RefResult<()>
pub fn write(&mut self, branch: &str, h: &Hash) -> RefResult<()>
Write one remote-tracking ref: validates branch, fsyncs its
wire-encoded content, then renames it into place — visible
immediately, same as write_remote_ref. Only the directory
fsync (rename durability) is deferred to Self::commit.
§Errors
RefError::InvalidRefNameifbranchfailsvalidate_ref_name— no I/O is attempted for an invalid name.RefError::Iofor filesystem failures.
§Panics
Never in practice: ref_path always produces a path with a
parent (it is self.layout.common_dir() joined with at least the
remote’s subdirectory and the branch name).
Sourcepub fn commit(self) -> RefResult<()>
pub fn commit(self) -> RefResult<()>
Fsync every directory touched by a completed Self::write,
once each. Idempotent to call on a batch with nothing staged (a
no-op). MUST be called after the last write a caller intends to
make durable — refs written but never committed are visible but
not crash-durable (the rename may not have reached stable
storage), the same window crate::store::BulkWriter::commit
documents for bulk object writes.
§Errors
RefError::Io on the first directory fsync failure. Directories
are synced in sorted order for determinism; a failure partway
through leaves the remaining directories un-synced (best-effort,
matching Self::write’s partial-failure contract).
Trait Implementations§
Auto Trait Implementations§
impl<'a> Freeze for RemoteRefBatch<'a>
impl<'a> RefUnwindSafe for RemoteRefBatch<'a>
impl<'a> Send for RemoteRefBatch<'a>
impl<'a> Sync for RemoteRefBatch<'a>
impl<'a> Unpin for RemoteRefBatch<'a>
impl<'a> UnsafeUnpin for RemoteRefBatch<'a>
impl<'a> UnwindSafe for RemoteRefBatch<'a>
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