Development Documentation (main branch) - For stable release docs, see docs.rs/eidetica
Skip to main content

Cluster

Struct Cluster 

Source
pub struct Cluster { /* private fields */ }
Expand description

A set of in-process eidetica peers wired for multi-peer sync. Each peer is a full Instance with its own backend. The cluster owns the wiring; the test owns the databases, the auth, and the order of operations.

Implementations§

Source§

impl Cluster

Source

pub fn builder() -> ClusterBuilder

Start building a cluster. Defaults: 2 peers, per-peer FixedClock, HttpLoopback transport.

Source

pub fn len(&self) -> usize

Number of peers in the cluster.

Source

pub fn is_empty(&self) -> bool

Whether the cluster has no peers.

Source

pub fn peer(&self, i: usize) -> &Peer

Shared access to a peer (instance, user, key, address).

Source

pub fn peer_mut(&mut self, i: usize) -> &mut Peer

Mutable access to a peer, for create_database / open_database on its User.

Source

pub async fn bootstrap( &mut self, from: usize, to: usize, tree: &ID, permission: Permission, ) -> Result<()>

Have peer to bootstrap tree from peer from: request the tree with to’s own key (asking for permission), flush, and track it locally (sync disabled — the test turns on serve/auto_sync if it wants more). The joining-peer dance as one call.

permission is the access level to requests; the harness does not pick it — auth posture stays the test’s. from must already be serving tree (see Peer::serve) with a policy that admits this request.

Source

pub async fn exchange(&self, from: usize, to: usize, tree: &ID) -> Result<()>

Drive a sync exchange for tree, initiated by peer from against peer to. eidetica’s sync_with_peer exchanges in both directions, so after this both peers hold each other’s entries for the tree. Both sync queues are flushed; flush errors propagate (this is a bug-finding tool — it does not swallow them).

Source

pub async fn auto_sync(&mut self, a: usize, b: usize, tree: &ID) -> Result<()>

Turn on background, automatic sync of tree between peers a and b, in both directions. After this a commit on either peer is queued for the other automatically (sync-on-commit) — no per-write exchange call. Use flush to push the queue immediately, or let the background interval carry it.

Both peers must already hold tree (e.g. one Peer::served it and the other bootstrapped it). This wires the peer relationship both ways (register peer + dial-back address + per-tree sync target) and re-tracks the tree as on_commit on each side.

Source

pub async fn auto_sync_all(&mut self, tree: &ID) -> Result<()>

auto_sync every peer pair in the cluster — a full mesh, so a commit on any peer fans out to all the others. Both peers of every pair must already hold tree. Does not flush; call flush_all to drain the setup pushes.

Source

pub async fn flush(&self, peer: usize) -> Result<()>

Push peer peer’s pending auto-sync queue to its targets now, instead of waiting for the background interval. The deterministic barrier for auto-sync tests: commit, flush, assert.

Source

pub async fn flush_all(&self) -> Result<()>

Drain the whole cluster: repeatedly flush every peer until all pending auto-sync work has propagated everywhere, then settle.

A single pass over the peers is not enough in general. flush visits each peer once, in index order, so a pass advances an in-flight entry at most one hop along its sync path (and only in the index direction — an entry that must travel “backwards”, from a higher-indexed peer to a lower one, waits for the next pass). One pass suffices only when every peer pushes directly to every other (a full mesh); a sparser topology — a relay chain — needs up to one pass per hop. len() + 1 passes covers the worst case, since no propagation path through len() peers is longer than len() - 1 hops. This is the whole-cluster barrier: after it, every deliverable entry has reached every peer.

Source

pub async fn snapshot(&self, peer: usize, tree: &ID) -> Result<Snapshot>

The Snapshot peer peer currently holds for tree — the canonical (sorted, deduplicated) tip set identifying its state. Snapshot::EMPTY if the peer has never seen the tree.

Source

pub async fn converged(&self, peers: &[usize], tree: &ID) -> Result<bool>

True if the named peers all agree on tree’s Snapshot — the convergence invariant. The caller names which peers should have converged; a peer that never received the tree has an empty snapshot and will not match. Comparison is Snapshot set-equality, so tip order never matters.

Source

pub async fn converged_all(&self, tree: &ID) -> Result<bool>

Whether every peer agrees on tree’s tip set — the common convergence check. Shorthand for converged over all peers; the explicit &[peers] form stays for partition tests that expect only a subset to agree.

Source

pub async fn converge(&self, tree: &ID) -> Result<bool>

Drive bidirectional exchange across every peer pair, round after round, until the whole cluster holds an identical tip set for tree — then return true. Bounded to peers rounds (a complete graph converges in one, the budget is slack for safety); returns the final convergence status if the budget is spent without settling.

Quiescent only: there must be no concurrent writes while this runs (it has no way to observe them). Every peer must already hold and serve tree so it can answer an exchange — bootstrap then Peer::serve on each joiner. The fixpoint barrier the N-peer / partition-heal tests assert against.

Source

pub async fn entries(&self, peer: usize, tree: &ID) -> Result<Vec<Entry>>

Every entry peer peer holds for tree, in id order. The full DAG of the tree — settings, auth, and every store — not just the tips.

Source

pub async fn entry_ids(&self, peer: usize, tree: &ID) -> Result<Vec<ID>>

The id of every entry peer peer holds for tree, sorted.

Source

pub async fn assert_no_lost_entries( &self, peers: &[usize], tree: &ID, ) -> Result<()>

Assert no peer in peers is missing an entry another holds for tree — the merge converged onto the union of histories, never silently dropping one peer’s signed entry. Stronger than converged, which only compares tips.

Limitation: if every peer dropped the same entry the union is also short it, so this can’t see that loss — use assert_all_present with an externally-known id set for the absolute form.

Source

pub async fn assert_all_present( &self, peers: &[usize], tree: &ID, expected: &[ID], ) -> Result<()>

Assert every id in expected is present on every peer in peers. The absolute form of assert_no_lost_entries: the test names entries it knows were committed (e.g. ids captured from its own writes) and demands they survive the merge everywhere.

Source

pub async fn assert_all_signed(&self, peer: usize, tree: &ID) -> Result<()>

Assert every entry peer peer holds for tree carries a well-formed signature. A synced CRDT under global auth must never store an unsigned or malformed-signature entry; this catches one that slipped through.

Source

pub async fn assert_all_verified(&self, peer: usize, tree: &ID) -> Result<()>

Assert no entry peer peer holds for tree is in the Failed verification state — every entry, including those received over sync, verified against the tree’s auth. Stronger than tip equality: a peer can converge on the right tips while having stored a received entry that does not verify.

This is only a meaningful convergence invariant once sync runs a per-entry verification pass that promotes received entries after their signing context arrives. On a build where sync ingestion records a placeholder status instead of a real signature check (see the TODO on VerificationStatus and docs/src/design/verification.md), the stored status does not reflect verification and this assertion should not be used — a bootstrapped peer legitimately holds entries marked Failed that no pass has yet promoted. Provided for the harness’s forward path: exercise it once verification-on-ingest is in place.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
§

impl<T> Pointable for T

§

const ALIGN: usize

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
§

impl<T> PolicyExt for T
where T: ?Sized,

§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] only if self and other return Action::Follow. Read more
§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<A, B, T> HttpServerConnExec<A, B> for T
where B: Body,

§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,