pub struct GASPSync<'a> {
pub last_interaction: u64,
pub unidirectional: bool,
/* private fields */
}Expand description
GASP sync orchestrator.
Coordinates between local GASPStorage and a GASPRemote peer to
synchronize overlay UTXOs. Supports paginated sync and unidirectional mode.
The lifetime 'a allows the storage and remote to borrow from their
environment (e.g., OverlayGASPStorage borrows from the Engine’s storage).
Fields§
§last_interaction: u64Score of last successful interaction with this peer.
unidirectional: boolIf true, only pull from remote — don’t push local UTXOs.
Implementations§
Source§impl<'a> GASPSync<'a>
impl<'a> GASPSync<'a>
Sourcepub fn new(
storage: Box<dyn GASPStorage + 'a>,
remote: Box<dyn GASPRemote + 'a>,
last_interaction: u64,
log_prefix: impl Into<String>,
unidirectional: bool,
) -> Self
pub fn new( storage: Box<dyn GASPStorage + 'a>, remote: Box<dyn GASPRemote + 'a>, last_interaction: u64, log_prefix: impl Into<String>, unidirectional: bool, ) -> Self
Create a new GASP sync orchestrator.
The ancestor fetcher is OFF by default. Use with_ancestor_fetcher to
opt in to chain-backed ancestry hydration (one-time-migration only).
Sourcepub fn with_expired_outpoints(
self,
expired: Rc<RefCell<HashSet<String>>>,
) -> Self
pub fn with_expired_outpoints( self, expired: Rc<RefCell<HashSet<String>>>, ) -> Self
Share expired outpoints across the topic peers of one engine run.
Sourcepub fn with_page_slice(self, stop: Rc<dyn Fn() -> bool + 'a>) -> Self
pub fn with_page_slice(self, stop: Rc<dyn Fn() -> bool + 'a>) -> Self
Backfill slices stop after one page or at a caller-owned request bound.
pub fn pages(&self) -> u64
pub fn page_advanced(&self) -> bool
pub fn reached_tip(&self) -> bool
pub fn expired_refused(&self) -> u64
pub fn roots_proven(&self) -> u64
pub fn roots_unproven(&self) -> u64
pub fn cursor_held_by(&self) -> Option<String>
pub fn cursor_yielded(&self) -> bool
Sourcepub fn graphs_retained(&self) -> u64
pub fn graphs_retained(&self) -> u64
New durable holds the last sync saved holding at least one node
the peer served (a walk past its budget, or the graph in flight at
the per-peer deadline): work the cursor may pass because the next
sync resumes it. A hold of nothing is not counted: the peer served
no node of it (the hung peer of bsv-low #555’s H1).
Sourcepub fn did_work(&self) -> bool
pub fn did_work(&self) -> bool
Whether the last sync did work beyond finalizing a graph or moving
the cursor (engine 0.1.8): it refused an expired root not refused
before, retained a graph or resumed one with progress. Such a sync is
never a failed attempt. Only the refusal also ends the yieldless
streak (see Engine::start_gasp_sync).
Sourcepub fn graphs_resumed_with_progress(&self) -> u64
pub fn graphs_resumed_with_progress(&self) -> u64
Held graphs the last sync resumed that appended at least one node
or converged.
Sourcepub fn faulted_roots(&self) -> Vec<FaultedRoot>
pub fn faulted_roots(&self) -> Vec<FaultedRoot>
The roots the last sync dropped, each named once with its fault
class (engine 0.1.9): what cursor_held_by cannot say.
Sourcepub fn unanchorable_recorded(&self) -> u64
pub fn unanchorable_recorded(&self) -> u64
Roots the last sync recorded unanchorable: their walk faulted in
UNANCHORABLE_AFTER_FAULTED_RUNS consecutive runs, and the cursor
passes them from now on.
Sourcepub fn dead_recorded(&self) -> u64
pub fn dead_recorded(&self) -> u64
Roots with a fault record (unanchorable or not yet) that the last
sync walked again and admitted or refused on a verdict: their
record cleared.
pub fn expired_swept(&self) -> u64
pub fn unanchorable_cleared(&self) -> u64
Sourcepub fn strikes_refused(&self) -> u64
pub fn strikes_refused(&self) -> u64
Strikes the last sync could not write (engine 0.1.10): past
FAULT_RECORDS_PER_PEER_TOPIC, at the storage’s ceiling, with the
keys unread, or on a storage fault. Each is named in
Self::faulted_roots with its FaultedRoot::refused; a refused
strike of a new root leaves it a gap the cursor holds below.
Bound each graph’s walk and retain its pending work. A successfully
saved hold permits cursor progress; an unsaved graph remains a gap.
Bound the resumes of each pass by share (engine 0.1.11): ONE
deadline for every held record walked ahead of the page, started when
the held loop starts, so the page keeps the rest of the per-peer
slice (see RESUMES_SHARE_OF_PEER_BUDGET_PERCENT). A record reached
once it fell due is held back for the next sync. Without one the
resumes share one per-graph deadline (the lens fold’s M1).
pub fn with_graph_budget(self, budget: GraphBudget<'a>) -> Self
Sourcepub fn deferral_stats(&self) -> DeferralStats
pub fn deferral_stats(&self) -> DeferralStats
What the last sync did with deferred graphs (bsv-low #555).
Sourcepub fn graphs_attempted(&self) -> u64
pub fn graphs_attempted(&self) -> u64
How many UTXOs the sync set out to ingest (a new walk, a resume, or a
record held back): the peer served it work it did not hold. A sync
that attempted none was quiet (bsv-low #555, the delta fold’s D-M2:
a quiet sync never counts toward PEER_YIELDLESS_SYNCS_ALLOWED).
Sourcepub fn with_finalized_graph_hook(
self,
hook: Box<dyn FinalizedGraphHook + 'a>,
) -> Self
pub fn with_finalized_graph_hook( self, hook: Box<dyn FinalizedGraphHook + 'a>, ) -> Self
Have hook called after every completed incoming UTXO (bsv-low #552,
see FinalizedGraphHook). Without one sync is unchanged.
Sourcepub fn completed_cursor(&self) -> u64
pub fn completed_cursor(&self) -> u64
The cursor that is safe to persist if sync stopped RIGHT NOW (its
future dropped at a deadline; bsv-low #552). It is the gap guard’s
rule applied to completed scores: never above a completed output, and
strictly below the lowest score of
any UTXO of the current page not yet completed (the one in flight
included) and of any UTXO whose ingest failed, never below the cursor
the sync entered with. The remote serves score >= since, so the next
sync is served everything this one did not finish. After a completed
sync it equals last_interaction.
last_interaction itself is NOT safe after a drop: it is raised to
each UTXO’s score BEFORE that UTXO is ingested, for pagination.
Sourcepub fn graph_in_flight(&self) -> bool
pub fn graph_in_flight(&self) -> bool
Whether a graph was being fetched, walked or anchor-checked when sync
last stopped: true only after a sync future dropped mid-graph
(bsv-low #552). That graph’s nodes died with the storage adapter;
nothing of it was finalized.
Sourcepub fn discarded_graphs(&self) -> u64
pub fn discarded_graphs(&self) -> u64
How many graphs this orchestrator’s last sync discarded because
validate_graph_anchor REFUSED them (a root that does not verify, or
one the topic manager does not admit at the end of the replay; bsv-low
#551). Nothing of a discarded graph is finalized. As in the reference
a refused graph is not a failed UTXO: the cursor advances past it. A
graph whose anchor could not be CHECKED
(GASPError::AnchorUnavailable: a tracker or storage fault, a topic
manager error in the replay) is not counted here: that one fails its
UTXO and is asked for again.
Sourcepub fn anchor_walk_held(&self) -> u64
pub fn anchor_walk_held(&self) -> u64
How many graphs this orchestrator’s last sync HELD because their
anchor’s script walk could not run within the engine’s walk budget
(GASPError::AnchorWalkHeld; bsv-low #592, the E592 lens fold): not
finalized, the UTXO failed and the cursor kept below it, until the
peer serves the root proven. Not counted in discarded_graphs.
Sourcepub fn pruned_inputs(&self) -> u64
pub fn pruned_inputs(&self) -> u64
How many manager-named inputs this orchestrator pruned because the
peer answered that it does not hold them (the D8 decoy rule, see
process_incoming_node). Counted per DISTINCT outpoint per sync
call of this orchestrator, which talks to ONE peer: the same decoy
seen through two peers is counted by each. A pruned branch is NOT a
failed UTXO: the graph completes with what it has and the cursor
advances.
Sourcepub fn with_ancestor_fetcher(
self,
fetcher: Option<Rc<dyn AncestorFetcher + 'a>>,
) -> Self
pub fn with_ancestor_fetcher( self, fetcher: Option<Rc<dyn AncestorFetcher + 'a>>, ) -> Self
Opt in to chain-backed ancestor hydration (OFF by default).
When set, a peer’s inability to serve a needed ancestor during ingest triggers a fallback fetch of that ancestor’s raw tx from chain. This is a deliberate one-time-migration escape hatch — production should NOT call this. Without it, behavior is unchanged.
Sourcepub async fn sync(&mut self, limit: Option<u64>) -> Result<(), GASPError>
pub async fn sync(&mut self, limit: Option<u64>) -> Result<(), GASPError>
Run the sync protocol with the remote peer.
- Request remote’s UTXOs since last interaction (paginated)
- For each unknown UTXO, request the full graph and ingest it
- If bidirectional, push our unknown UTXOs to the remote
Sourcepub async fn defer_in_flight(&mut self)
pub async fn defer_in_flight(&mut self)
Save the walk of the graph that was in hand when a deadline dropped
sync (bsv-low #555): the per-peer deadline (D16) defers it like the
per-graph one, so the next pass resumes it instead of walking it
again. No-op without a per-graph budget.
The graph in flight is HELD at once, whatever it fetched: its root
outpoint and partial walk become a durable record (reason
peer_deadline) the next sync resumes ahead of its pages, so the
cursor passes it to the page’s last finished score. Before 0.1.8 a
graph the deadline met before its root came back was only a retry,
and the next run re-read the same page behind it (bsv-low #552:
“the graph in flight is lost whole”). A graph the deadline met after
its refusal is finished, not held. A graph that cannot be held (no
room) stays the cursor’s gap and is named in cursor_held_by.