pub struct RefManager { /* private fields */ }Expand description
Manager for references (threads, markers, HEAD).
Implementations§
Source§impl RefManager
impl RefManager
pub fn inspect_ref_summary_index(&self) -> Result<RefSummaryIndexInspection>
pub fn rebuild_ref_summary_index(&self) -> Result<RefSummaryIndexInspection>
Source§impl RefManager
impl RefManager
pub fn new(heddle_dir: impl AsRef<Path>) -> Self
pub fn with_local_head(self, path: PathBuf) -> Self
Sourcepub fn with_reconciler(self, reconciler: Arc<dyn RefReconciler>) -> Self
pub fn with_reconciler(self, reconciler: Arc<dyn RefReconciler>) -> Self
Inject the oplog-backed reconciler (heddle#330 §2.2). Once set, every
logical read funnels through RefManager::reconciled_load and
reconciles against the committed oplog tail. Mirrors the
with_local_head builder shape.
The class watermarks are seeded to the current generation: this
handle trusts the already-published canonical cache as of open and
reconciles only commits made after it — the load-bearing long-held
handle cell (the daemon’s Arc<Repository>, cid 3328112197) an
open-time-only pass cannot reach. (Catching a pre-open crash lag is the
job of the optional Repository::open eager pass, deferred here as the
spike’s stated optimization, not the guarantee.) Seeding to the current
generation also keeps reconciliation from re-deriving long-since-deleted
refs from old records in the un-migrated tree, where deletes do not all
record yet.
Sourcepub fn with_committer(self, committer: Arc<dyn RefCommitter>) -> Self
pub fn with_committer(self, committer: Arc<dyn RefCommitter>) -> Self
Inject the oplog-backed committer (heddle#330 §2.2 write chokepoint).
Once set, commit_and_publish appends the
caller’s ref-carrying records before publishing the ref batch.
Sourcepub fn commit_and_publish(
&self,
records: &[OpRecord],
ref_updates: &[RefUpdate],
scope: Option<&str>,
) -> Result<()>
pub fn commit_and_publish( &self, records: &[OpRecord], ref_updates: &[RefUpdate], scope: Option<&str>, ) -> Result<()>
The atomic-write entry of THE write chokepoint (heddle#330 §2.2): commit
the caller-supplied ref-carrying record batch (phase 4) before
publishing the atomic ref batch (phase 5), record-before-publish, the
whole batch published as one unit. The bare publish (temp→rename, via
update_refs_with_lock) is reachable through this seam, never with a
ref published ahead of its record. With no committer it degrades to a
plain publish (bootstrap).
Invariant (cid 3329490978 / 3329490984): the oplog record and the ref
publish commit together under the refs lock; a record exists iff its
publish succeeded, and concurrent publishes to the same ref serialize
record-and-publish as a unit. Routed through
write_chokepoint, which takes the refs lock
FIRST and materializes the committed-but-unpublished tail of every class
BEFORE the body runs; the ref expectations are then validated (phase 3)
against that reconciled state, BEFORE the record is appended (phase 4),
and the publish (phase 5) follows under the same lock — so a failed
expectation never leaks a record, and two concurrent callers can never
append in one order and publish in another. (For PgRefBackend the single
pool.begin()…commit() gives the same atomicity natively.)
Sourcepub fn materialize_snapshot_thread_after_commit(
&self,
thread: &ThreadName,
state: StateId,
tip: u64,
) -> Result<()>
pub fn materialize_snapshot_thread_after_commit( &self, thread: &ThreadName, state: StateId, tip: u64, ) -> Result<()>
Atomically publish a just-committed attached snapshot as a reconstructible materialized view. The caller already owns the authoritative state and oplog tip, so replaying the same oplog tail through a fresh reconciler here only adds file/index I/O to every capture. The persisted watermarks deliberately remain at their prior durable floor for fresh-process recovery.
Sourcepub fn materialize_snapshot_head_after_commit(
&self,
state: StateId,
tip: u64,
) -> Result<()>
pub fn materialize_snapshot_head_after_commit( &self, state: StateId, tip: u64, ) -> Result<()>
Detached-HEAD counterpart to
materialize_snapshot_thread_after_commit.
Sourcepub fn init_reconcile_watermark(&self) -> Result<()>
pub fn init_reconcile_watermark(&self) -> Result<()>
Seed the per-read watermarks from the persisted last-clean point (heddle#354 r5, cid 3329631074), so a fresh handle recovers a prior process’s committed-but-unpublished crash tail.
A RefManager seeds its in-memory watermarks at the current generation
(with_reconciler) — so the per-read gate, on a
fresh process, would never fold a record committed before this handle
opened, and a cross-process crash (phase-4 committed, phase-5 publish
never ran) would be silently lost. The fix is NOT an eager open-time fold
(that would re-derive long-since-deleted refs from ancient records, since
the un-migrated delete paths do not all record yet): it is a persisted
watermark. Reads advance and persist it past every materialized record,
so on open the seed sits at the last point canonical was known-consistent;
the per-read reconcile then folds only (seed, tip] — the genuine crash
tail — and never the ancient records below the seed.
When no watermark has been persisted yet (a fresh repo, or a repo from before this version), seed conservatively at the current generation and write the file, so the next process has a real last-clean point.
The two classes seed from SEPARATE files: the local watermark from the per-worktree file, the shared watermark from the shared-dir file (cid 3329711893). A sibling worktree that already advanced the shared watermark publishes it to the shared file, so this checkout seeds at that shared last-clean point and never re-folds a shared create the sibling already processed.
pub fn init(&self) -> Result<()>
pub fn cleanup_stale_temps(&self)
pub fn read_head(&self) -> Result<Head>
pub fn write_head(&self, head: &Head) -> Result<()>
pub fn write_head_cas( &self, expected: RefExpectation<Head>, head: &Head, ) -> Result<()>
pub fn get_thread(&self, name: &ThreadName) -> Result<Option<StateId>>
pub fn set_thread(&self, name: &ThreadName, state: &StateId) -> Result<()>
pub fn set_thread_cas( &self, name: &ThreadName, expected: RefExpectation<StateId>, state: &StateId, ) -> Result<()>
pub fn delete_thread(&self, name: &ThreadName) -> Result<Option<StateId>>
pub fn delete_thread_cas( &self, name: &ThreadName, expected: RefExpectation<StateId>, ) -> Result<()>
pub fn list_threads(&self) -> Result<Vec<ThreadName>>
Sourcepub fn list_threads_with_states(&self) -> Result<Vec<(ThreadName, StateId)>>
pub fn list_threads_with_states(&self) -> Result<Vec<(ThreadName, StateId)>>
List thread names and targets through one reconciled list read. The summary index is updated by reconciliation before it is read here, so callers avoid one logical point read per listed thread.
pub fn get_marker(&self, name: &MarkerName) -> Result<Option<StateId>>
pub fn create_marker(&self, name: &MarkerName, state: &StateId) -> Result<()>
pub fn set_marker_cas( &self, name: &MarkerName, expected: RefExpectation<StateId>, state: &StateId, ) -> Result<()>
pub fn delete_marker(&self, name: &MarkerName) -> Result<Option<StateId>>
pub fn delete_marker_cas( &self, name: &MarkerName, expected: RefExpectation<StateId>, ) -> Result<()>
pub fn list_markers(&self) -> Result<Vec<MarkerName>>
Sourcepub fn set_undo_recovery(&self, state: &StateId) -> Result<()>
pub fn set_undo_recovery(&self, state: &StateId) -> Result<()>
Record the heddle-internal pre-undo recovery pointer (ORIG_HEAD-style:
a single rolling ref each undo overwrites). Stored OUTSIDE the
user-writable marker namespace so marker create/delete — and their
undo inverses — can never collide with it. See
UNDO_RECOVERY_HANDLE for the resolution handle.
Sourcepub fn clear_undo_recovery(&self) -> Result<()>
pub fn clear_undo_recovery(&self) -> Result<()>
Remove the heddle-internal pre-undo recovery pointer, returning the repo
to the “no undo has run” state. Routes through the same
write_chokepoint as the setter so it cannot
bypass reconciliation, and is a no-op when no pointer exists. Used as the
inverse of set_undo_recovery when the atomic
undo transaction rewinds and the pointer had no prior value to restore
(the first-ever undo): the pointer is written with no oplog record of its
own, so deleting the canonical file is a complete clear.
Sourcepub fn get_undo_recovery(&self) -> Result<Option<StateId>>
pub fn get_undo_recovery(&self) -> Result<Option<StateId>>
Read the heddle-internal pre-undo recovery pointer, if one has been
recorded. Returns None when no undo has run in this repo.
pub fn get_remote_thread( &self, remote: &str, thread: &ThreadName, ) -> Result<Option<StateId>>
pub fn set_remote_thread( &self, remote: &str, thread: &ThreadName, state: &StateId, ) -> Result<()>
pub fn delete_remote_thread( &self, remote: &str, thread: &ThreadName, ) -> Result<Option<StateId>>
pub fn list_remotes(&self) -> Result<Vec<String>>
pub fn list_remote_threads(&self, remote: &str) -> Result<Vec<ThreadName>>
pub fn update_refs(&self, updates: &[RefUpdate]) -> Result<()>
pub fn resolve(&self, refspec: &str) -> Result<Option<StateId>>
pub fn pack_refs(&self) -> Result<()>
Source§impl RefManager
impl RefManager
Sourcepub fn set_synthetic_frontier(
&self,
name: &SyntheticFrontierName,
state: &StateId,
) -> Result<()>
pub fn set_synthetic_frontier( &self, name: &SyntheticFrontierName, state: &StateId, ) -> Result<()>
Persist a synthetic frontier root. Does not construct a [ThreadName].
Sourcepub fn get_synthetic_frontier(
&self,
name: &SyntheticFrontierName,
) -> Result<Option<StateId>>
pub fn get_synthetic_frontier( &self, name: &SyntheticFrontierName, ) -> Result<Option<StateId>>
Fetch a synthetic frontier root by its type-distinct name.
Sourcepub fn list_synthetic_frontiers(
&self,
) -> Result<Vec<(SyntheticFrontierName, StateId)>>
pub fn list_synthetic_frontiers( &self, ) -> Result<Vec<(SyntheticFrontierName, StateId)>>
List every stored synthetic frontier root.
Trait Implementations§
Source§impl CoreRefBackend for RefManager
impl CoreRefBackend for RefManager
type Error = HeddleError
fn read_head(&self) -> Result<Head>
fn write_head(&self, head: &Head) -> Result<()>
fn write_head_cas( &self, expected: RefExpectation<Head>, head: &Head, ) -> Result<()>
async fn get_thread(&self, name: &ThreadName) -> Result<Option<StateId>>
fn set_thread(&self, name: &ThreadName, state: &StateId) -> Result<()>
fn set_thread_cas( &self, name: &ThreadName, expected: RefExpectation<StateId>, state: &StateId, ) -> Result<()>
fn delete_thread(&self, name: &ThreadName) -> Result<Option<StateId>>
fn delete_thread_cas( &self, name: &ThreadName, expected: RefExpectation<StateId>, ) -> Result<()>
fn list_threads(&self) -> Result<Vec<ThreadName>>
async fn get_marker(&self, name: &MarkerName) -> Result<Option<StateId>>
async fn create_marker(&self, name: &MarkerName, state: &StateId) -> Result<()>
fn set_marker_cas( &self, name: &MarkerName, expected: RefExpectation<StateId>, state: &StateId, ) -> Result<()>
fn delete_marker(&self, name: &MarkerName) -> Result<Option<StateId>>
fn delete_marker_cas( &self, name: &MarkerName, expected: RefExpectation<StateId>, ) -> Result<()>
fn list_markers(&self) -> Result<Vec<MarkerName>>
fn update_refs(&self, updates: &[RefUpdate]) -> Result<()>
async fn resolve(&self, refspec: &str) -> Result<Option<StateId>>
Source§impl RefBackend for RefManager
impl RefBackend for RefManager
Source§fn can_commit_records(&self) -> bool
fn can_commit_records(&self) -> bool
commit_and_publish seam. Read morefn get_remote_thread( &self, remote: &str, thread: &ThreadName, ) -> Result<Option<StateId>>
fn set_remote_thread( &self, remote: &str, thread: &ThreadName, state: &StateId, ) -> Result<()>
fn delete_remote_thread( &self, remote: &str, thread: &ThreadName, ) -> Result<Option<StateId>>
fn list_remotes(&self) -> Result<Vec<String>>
fn list_remote_threads(&self, remote: &str) -> Result<Vec<ThreadName>>
Source§fn commit_and_publish(
&self,
records: &[OpRecord],
ref_updates: &[RefUpdate],
scope: Option<&str>,
) -> Result<()>
fn commit_and_publish( &self, records: &[OpRecord], ref_updates: &[RefUpdate], scope: Option<&str>, ) -> Result<()>
OpRecords) before publishing
the atomic ref batch (phase 5), record-before-publish. The seam is
per backend: the file backend (RefManager) earns atomicity by
oplog-append-then-publish + per-read reconciliation; the Postgres
backend co-commits the record row and ref/head rows in one SQL
transaction (native ACID). Read more