Skip to main content

EventLog

Struct EventLog 

Source
pub struct EventLog<E>
where E: Context + BufferPooler,
{ /* private fields */ }
Expand description

An append-only, ordered log of conversation Events.

Generic over a commonware_storage::Context so the same code runs on the tokio backend in production and the deterministic backend in tests. See the crate-level docs for the ordering contract and runtime-coexistence notes.

Implementations§

Source§

impl<E> EventLog<E>
where E: Context + BufferPooler,

Source

pub async fn open( context: E, config: EventLogConfig, ) -> Result<Self, EventLogError>

Open (creating if absent, recovering if present) the event log for a conversation on the given runtime context.

On open the journal replays only its final section to recover the exact append size, and self-heals any data/offset divergence left by a crash.

§Errors

Returns EventLogError::Journal if the underlying storage fails to initialize or recover the journal.

Source

pub async fn destroy(self) -> Result<(), EventLogError>

Destroy the log: consume the handle and REMOVE the partition’s underlying blobs (data + offsets) from storage. The erasure primitive (#216): after this, a fresh EventLog::open of the same partition starts empty.

Deliberately consuming — a destroyed log has no valid further operation, and the caller must drop every other handle first (the control plane’s host serializes this through its single command loop and evicts its cache entry before destroying).

§Errors

Returns EventLogError::Journal if the underlying blob removal fails.

Source

pub async fn append(&self, event: &Event) -> Result<u64, EventLogError>

Append a single event, returning the position the journal assigned it.

Positions are strictly increasing from 0 and define replay order. The caller must append in conversation order (turn, then seq within a turn) for replay to reflect that order.

Takes &self: EventLog’s own lock (see the struct-level doc) recovers a shared reference over the journal’s &mut self ops.

Appends are buffered for durability; call EventLog::commit (or EventLog::sync) to guarantee they survive a crash.

§Errors

Returns EventLogError::Journal if the item cannot be encoded or the underlying storage write fails.

Source

pub async fn len(&self) -> u64

Number of events appended to the log (the position the next append will receive). Not reduced by pruning.

Source

pub async fn is_empty(&self) -> bool

Whether the log has no appended events.

Source

pub async fn replay_with_positions( &self, ) -> Result<Vec<(u64, Event)>, EventLogError>

Replay every event in append order, each paired with its position.

The returned Vec is ordered by position ascending (0, 1, 2, …), which is conversation order. Each tuple is (position, event).

This collects the full log into memory; it is intended for rebuilding in-memory conversation state on resume. For very large logs a streaming variant could be added later (the journal exposes a Stream), but the foundational API materializes for simplicity.

§Errors

Returns EventLogError::Journal if the journal cannot start the replay stream or if decoding any stored event fails.

Source

pub async fn replay_with_positions_bounded( &self, max_bytes: u64, ) -> Result<BoundedReplay, EventLogError>

Replay every event in append order, each paired with its position — same as EventLog::replay_with_positions — but STOP pulling from the underlying replay stream the instant the cumulative payload bytes read so far exceed max_bytes.

This is issue #1541’s early-abort primitive: EventLog::replay_with_positions always drains the whole stream into one Vec before any caller can check its size, so a budget checked only after that call returns has already paid the full allocation cost it meant to avoid. This method instead checks the running byte total INSIDE the same loop that pulls from the stream, so the returned Vec never grows past max_bytes plus one event’s own payload size (the one event whose read tips the budget over is kept, then the loop breaks — the rest of the partition, however large, is never fetched from the journal).

§Errors

Returns EventLogError::Journal if the journal cannot start the replay stream or if decoding any stored item fails before the budget trips.

Source

pub async fn replay_from_with_positions( &self, start: u64, ) -> Result<Vec<(u64, Event)>, EventLogError>

Replay events in append order starting at position start, each paired with its position.

The journal is position-indexed, so resuming at an offset is cheap — the reader seeks to start rather than scanning from zero. This is what lets a caller replay only the tail since a durable checkpoint instead of re-reading the whole partition every time. start is clamped up to the pruning boundary, and a start at or past the end yields an empty Vec. Returned tuples are (position, event) for positions in [max(start, bounds.start), len), ascending.

§Errors

Returns EventLogError::Journal if the journal cannot start the replay stream or if decoding any stored event fails.

Source

pub async fn replay_from_with_positions_bounded( &self, start: u64, max_bytes: u64, ) -> Result<BoundedReplay, EventLogError>

Replay events in append order starting at position start, each paired with its position — same resume semantics as EventLog::replay_from_with_positions — but STOP pulling from the underlying replay stream the instant the cumulative payload bytes read so far exceed max_bytes, the same early-abort mechanic EventLog::replay_with_positions_bounded applies to a replay from the very start.

This is the combined primitive neither of the other two replay methods can express alone: EventLog::replay_with_positions_bounded bounds bytes but always starts at position 0, and EventLog::replay_from_with_positions resumes at start but has no byte cap, so a partition whose TAIL (the part after a caller-held watermark) is itself large could still be materialized in full before any caller ever gets a chance to reject it. This method closes that gap: a caller resuming from its own cached watermark (polyc_query’s per-partition decode cache is the first consumer) gets the same mid-stream budget enforcement a fresh replay already had, without paying to re-read anything before start.

start is clamped up to the partition’s own pruning boundary exactly as EventLog::replay_from_with_positions does, and a start at or past the partition’s end yields an empty, budget_exceeded: false BoundedReplay rather than an error.

§Errors

Returns EventLogError::Journal if the journal cannot start the replay stream or if decoding any stored item fails before the budget trips.

Source

pub async fn replay_range_with_positions_bounded( &self, start: u64, end: u64, max_bytes: u64, ) -> Result<BoundedReplay, EventLogError>

Replay events in [start, end) — end EXCLUSIVE — each paired with its position, under the same byte cap EventLog::replay_from_with_positions_bounded applies.

The only replay primitive here that accepts an UPPER bound. Every other one drains to the journal’s tail: replay_with_positions and replay_from_with_positions have no cap at all, and the two _bounded siblings cap BYTES, which stops a large read but cannot express “these events and no others”. A caller that knows the exact span it wants — one turn’s events, say, located by a prior index — otherwise has to replay from start to the tail and discard the remainder, so the cost of fetching a hit near the beginning of a long conversation scales with the conversation rather than with the hit.

Both ends are clamped to the partition’s own bounds: start up to the pruning boundary (as EventLog::replay_from_with_positions does), end down to the journal’s tail, so an end past the tail reads to the tail rather than erroring. An empty or inverted range — start at or past the clamped end — yields an empty, budget_exceeded: false BoundedReplay, never an error.

The byte cap keeps the same meaning it has on the sibling methods: the event that trips the budget is INCLUDED, and budget_exceeded is set so the caller can tell a truncated read from a complete one. A range that ends before the cap trips returns budget_exceeded: false even if bytes_read is large, because the range, not the budget, is what stopped it.

§Errors

Returns EventLogError::Journal if the journal cannot start the replay stream or if decoding any stored item fails before either bound stops it.

Source

pub async fn replay(&self) -> Result<Vec<Event>, EventLogError>

Replay every event in append order, discarding positions.

Convenience over EventLog::replay_with_positions for callers that only need the ordered events.

§Errors

Returns EventLogError::Journal on the same conditions as EventLog::replay_with_positions.

Source

pub async fn replay_from(&self, start: u64) -> Result<Vec<Event>, EventLogError>

Replay events from position start in append order, discarding positions. Convenience over EventLog::replay_from_with_positions.

§Errors

Returns EventLogError::Journal on the same conditions as EventLog::replay_from_with_positions.

Source

pub async fn replay_quarantining( &self, ) -> Result<(Vec<(u64, Event)>, Vec<QuarantinedItem>), EventLogError>

Replay every event in append order, skipping any position whose item cannot be decoded rather than aborting the whole replay.

EventLog::replay_with_positions stops at the first bad item (the backup/DR gap #799 tracks: one corrupted event permanently locks a conversation out of replay). This reads each position independently through the journal’s position index — a corrupted item’s neighbors don’t depend on decoding it — so it recovers everything readable and reports the rest as QuarantinedItems. This is the primitive a conversation repair operation uses to drop only the unreadable event(s) and let the rest of the log replay again; it is otherwise intended for that recovery path, not routine replay (one read per position, versus one streamed pass).

§Errors

Returns EventLogError::Journal if the journal cannot report its own bounds. A per-item decode failure is reported in the returned quarantine list, never as an Err.

Source

pub async fn commit(&self) -> Result<(), EventLogError>

Durably persist all buffered appends, guaranteeing they survive a crash.

Committed appends survive a crash, but the next EventLog::open may perform recovery work rebuilding the position index from data before replay is available — EventLog::sync additionally makes the next open recovery-free.

§Errors

Returns EventLogError::Journal if the underlying flush fails.

Source

pub async fn sync(&self) -> Result<(), EventLogError>

Stronger durability than EventLog::commit: persist and guarantee no recovery work is needed on next open.

§Errors

Returns EventLogError::Journal if the underlying sync fails.

Auto Trait Implementations§

§

impl<E> !Freeze for EventLog<E>

§

impl<E> !RefUnwindSafe for EventLog<E>

§

impl<E> !UnwindSafe for EventLog<E>

§

impl<E> Send for EventLog<E>

§

impl<E> Sync for EventLog<E>

§

impl<E> Unpin for EventLog<E>
where E: Unpin, <E as Storage>::Blob: Unpin,

§

impl<E> UnsafeUnpin for EventLog<E>
where E: UnsafeUnpin, <E as Storage>::Blob: UnsafeUnpin,

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<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

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

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> FutureExt for T

Source§

fn with_context(self, otel_cx: Context) -> WithContext<Self>

Attaches the provided Context to this type, returning a WithContext wrapper. Read more
Source§

fn with_current_context(self) -> WithContext<Self>

Attaches the current Context to this type, returning a WithContext wrapper. Read more
Source§

impl<T> Instrument for T

Source§

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

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

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

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

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

Initializes a with the given initializer. Read more
Source§

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

Dereferences the given pointer. Read more
Source§

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

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

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

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

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + 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
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + 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> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

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.
Source§

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

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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