Skip to main content

EventLog

Struct EventLog 

Source
pub struct EventLog<D> { /* private fields */ }
Expand description

Append-only, deduplicating event log for CRDT deltas. Equality compares log data, excluding the in-memory first-admission binding flag and the in-memory collision register.

Implementations§

Source§

impl<D> EventLog<D>

Source

pub fn new() -> Self

Create an unbound log. The first accepted record binds its carrier shape. Use for_crdt to declare a shape before any records are accepted.

Source

pub fn with_replica_count(replica_count: usize) -> Self

Create a log for a fixed replica domain, including an empty domain.

Source

pub fn for_crdt<C: Crdt<Delta = D>>(state: &C) -> Self

Bind persistence metadata to the CRDT’s actual domain before recording.

Source

pub fn replica_count(&self) -> Option<usize>

Source

pub fn append<C: Crdt<Delta = D>>( &mut self, state: &mut C, replica: u64, delta: D, ) -> Result<RecordId, AppendError>
where D: PartialEq,

Allocate a fresh ID and admit a delta without applying it to state. Returns an error if the sequence is exhausted or admission refuses the record; in either case the log and state remain unchanged.

Source

pub fn append_with<C, F>( &mut self, state: &mut C, replica: u64, delta: D, apply: F, ) -> Result<RecordId, AppendError>
where D: PartialEq, C: Crdt<Delta = D>, F: FnOnce(&mut C, &D),

Allocate a fresh ID, then use the same gate as incoming records.

Source

pub fn merge_records<C: Crdt<Delta = D>, I>( &mut self, state: &C, records: I, ) -> Vec<Admission>
where D: PartialEq, I: IntoIterator<Item = Record<D>>,

Source

pub fn admit_with<C, F>( &mut self, state: &mut C, record: Record<D>, apply: F, ) -> Admission
where D: PartialEq, C: Crdt<Delta = D>, F: FnOnce(&mut C, &D),

Validate the log shape and record against the destination carrier, then check record identity. An unbound log binds only on acceptance. Only Accepted invokes apply, with the same carrier used for validation. An invalid fresh record leaves the log, version, and carrier unchanged. Duplicate/Collision retain their identity verdicts, but also run validation. A Collision also raises the collision alarm for the ID (Self::collisions); log, version and carrier stay unchanged. The callback must be infallible and use the same delta interpretation as replay. This is an in-memory transition, not a crash-durability guarantee.

Source

pub fn version(&self) -> &VersionVector

Source

pub fn records(&self) -> &[Record<D>]

Source

pub fn insert_record<C: Crdt<Delta = D>>( &mut self, state: &C, record: Record<D>, ) -> Admission
where D: PartialEq,

Admit without applying; state must be the carrier used for later replay. A Collision raises the collision alarm, as in Self::admit_with.

Source§

impl<D: Clone> EventLog<D>

Source

pub fn since(&self, version: &VersionVector) -> Vec<Record<D>>

Return admitted records outside the peer’s positive contiguous prefixes and independently acknowledged sequence-zero records. By design, a permanent gap offers all later records from that writer on every exchange; deliver the missing record.

Source§

impl<D> EventLog<D>

Source

pub fn collisions(&self) -> Vec<RecordCollision<D>>
where D: Clone,

Every held ID whose alarm is raised, in ID order, with the payload held here and the first differing payload a peer offered or reported. Empty when no fork has been seen. Durable replicas store it separately.

Source

pub fn collision_report(&self) -> CollisionReport<D>
where D: Clone,

The report a peer needs to raise the same alarms: every entry of Self::collisions, from this log’s perspective.

Source

pub fn merge_collision_report( &mut self, report: CollisionReport<D>, ) -> Vec<(RecordId, CollisionVerdict)>
where D: PartialEq,

Merge a peer’s report. Each entry is judged against the payload held here and returns its verdict in input order. Only CollisionVerdict::Recorded changes anything, and it changes only the alarm: never the records, version or a carrier.

Source§

impl<D: Clone + WireEncode + WireSchema> EventLog<D>

Source

pub fn collision_report_bytes(&self) -> Result<Option<Vec<u8>>, WireError>

Encode Self::collision_report for the peer, or None when no alarm is raised here. Send it back after merging a peer’s batch.

Source§

impl<D: PartialEq + WireDecode + WireSchema> EventLog<D>

Source

pub fn merge_collision_report_bytes( &mut self, bytes: &[u8], limits: DecodeLimits, ) -> Result<Vec<(RecordId, CollisionVerdict)>, DecodeError>

Decode a peer’s report under limits, then merge it. A decode failure refuses the whole message and changes nothing.

Source§

impl<D: WireDecode + WireSchema + PartialEq> EventLog<D>

Source

pub fn from_wire_bytes_with_limits( bytes: &[u8], limits: DecodeLimits, ) -> Result<Self, DecodeError>

Decode an inert log with an optional top-level record budget.

Checks frame integrity and shape first, then checks the declared record count before it reads any record. Duplicate occurrences count toward it. Returns DecodeError::RecordLimitExceeded without a partial result. CRC verification still scans the entire frame; callers must separately cap input bytes and payload complexity. As with WireDecode::from_wire_bytes, this does not validate a destination CRDT for replay. The option is currently exposed only in Rust.

DecodeLimits::default admits at most its named record budget.

use safemesh_crdt::{DecodeError, DecodeLimits, EventLog, GSet, Record, RecordId};
let records = [Record { id: RecordId { replica: 0, sequence: 1 }, delta: GSet::<u64>::new() }];
let mut bytes = Vec::new();
EventLog::encode_records(None, &records, &mut bytes).unwrap();
assert_eq!(EventLog::<GSet<u64>>::from_wire_bytes_with_limits(
    &bytes, DecodeLimits { max_records: Some(0), ..DecodeLimits::default() }),
    Err(DecodeError::RecordLimitExceeded { max_records: 0 }));
let log = EventLog::<GSet<u64>>::from_wire_bytes_with_limits(
    &bytes, DecodeLimits { max_records: Some(1), max_collection_elements: Some(5_000) }).unwrap();
assert_eq!(log.records().len(), 1);
Source

pub fn from_wire_bytes_for<C: Crdt<Delta = D>>( bytes: &[u8], state: &C, ) -> Result<Self, WireError>

Decode and compare the saved shape with the destination before replay. Plain from_wire_bytes decodes a log and retains its domain; it does not load a CRDT. Use this method at every persisted-state loading boundary.

Source

pub fn from_wire_bytes_for_with_limits<C: Crdt<Delta = D>>( bytes: &[u8], state: &C, limits: DecodeLimits, ) -> Result<Self, DecodeError>

Decode with record and nested collection budgets, then validate the saved shape and records against the destination before replay.

Source

pub fn records_from_wire_bytes_for<C: Crdt<Delta = D>>( bytes: &[u8], state: &C, ) -> Result<Vec<Record<D>>, WireError>
where D: Clone,

Decode all input occurrences in order for per-record admission reporting. Validate the entire frame, collisions and destination shape before returning. The ordinary log loaders continue to deduplicate identical records.

Source

pub fn records_from_wire_bytes_for_with_limits<C: Crdt<Delta = D>>( bytes: &[u8], state: &C, limits: DecodeLimits, ) -> Result<Vec<Record<D>>, DecodeError>
where D: Clone,

Decode each occurrence with record and nested collection budgets, then validate the destination before returning the occurrences.

Source§

impl<D: WireDecode + WireEncode + WireSchema + PartialEq> EventLog<D>

Source

pub fn migrate_legacy_wire_bytes_for<C: Crdt<Delta = D>>( bytes: &[u8], state: &C, ) -> Result<Vec<u8>, WireError>

Re-encode a legacy EventLog frame (LegacyFrame) as a current shaped frame. This is explicit; no loader ever migrates automatically.

Old frames record neither schema nor arity. The caller declares both: the schema by choosing D, and the arity through state, a destination carrier with the original replica count, verified independently. Do not infer it from the largest coordinate in the log. Every record is decoded and deduplicated as the old decoder did, then validated against state. Record bytes and order are unchanged; only the frame gains its shape. Any error returns no output. A current frame is validated the same way and returned unchanged, so a repeated migration has no further effect. Nested EventLog payloads are not rewritten; a legacy nested frame fails.

A tag 0x02 frame has no checksum. Migration accepts a frame with a changed payload bit when it still decodes, and the migrated value can differ from the original. Before migration, check your own checksum of the old file or compare it with a second copy. A tag 0x03 frame has a CRC and refuses the same kind of changed payload.

use safemesh_crdt::{EventLog, GCounter, GCounterDelta, LegacyFrame, WireDecode, WireError};
let old = [0x02, 0, 0, 0, 0]; // an empty tag 0x02 log
assert_eq!(EventLog::<GCounterDelta>::from_wire_bytes(&old),
    Err(WireError::LegacyEventLogFrame { found: LegacyFrame::Tag02 }));
let state = GCounter::new(2);
let new = EventLog::migrate_legacy_wire_bytes_for(&old, &state)?;
assert_eq!(EventLog::from_wire_bytes_for(&new, &state)?.replica_count(), Some(2));
Source

pub fn migrate_legacy_wire_bytes_for_with_limits<C: Crdt<Delta = D>>( bytes: &[u8], state: &C, limits: DecodeLimits, ) -> Result<Vec<u8>, DecodeError>

Migrate a legacy frame with a budget for each input record occurrence. Duplicate records count; the limit is checked before decoding the next record. A current shaped frame uses the same limited decoder as ordinary loading. The tag 0x02 checksum warning and pre-migration checks in Self::migrate_legacy_wire_bytes_for apply here too.

Source§

impl<D: WireEncode + WireSchema> EventLog<D>

Source

pub fn encode_records( replica_count: Option<usize>, records: &[Record<D>], out: &mut Vec<u8>, ) -> Result<(), WireError>

Encode an inert batch without admitting it into a live log.

This preserves input occurrences, including duplicates and invalid records. Receivers must use records_from_wire_bytes_for or from_wire_bytes_for before applying the payload. Encoding does not validate a CRDT domain.

Trait Implementations§

Source§

impl<D: Clone> Clone for EventLog<D>

Source§

fn clone(&self) -> EventLog<D>

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<D: Debug> Debug for EventLog<D>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<D> Default for EventLog<D>

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl<D: PartialEq> PartialEq for EventLog<D>

Source§

fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl<D: WireDecode + WireSchema + PartialEq> WireDecode for EventLog<D>

Source§

fn decode_wire(cursor: &mut WireCursor<'_>) -> Result<Self, WireError>

Source§

fn decode_wire_with_collection_limits( cursor: &mut WireCursor<'_>, limits: CollectionLimits, ) -> Result<Self, WireError>

Decode a nested payload with a collection budget. Other payload types retain their ordinary decoder unless they implement this method.
Source§

fn decode_wire_with_limits( cursor: &mut WireCursor<'_>, limits: CollectionLimits, max_records: Option<usize>, ) -> Result<Self, WireError>

Decode a payload nested in an EventLog with its record budget.
Source§

fn from_wire_bytes_with_collection_limits( bytes: &[u8], limits: CollectionLimits, ) -> Result<Self, WireError>

Decode a complete wire value with an explicit collection ceiling.
Source§

fn from_wire_bytes(bytes: &[u8]) -> Result<Self, WireError>

Source§

impl<D: WireEncode + WireSchema> WireEncode for EventLog<D>

Source§

fn encode_wire(&self, out: &mut Vec<u8>) -> Result<(), WireError>

Source§

fn to_wire_bytes(&self) -> Result<Vec<u8>, WireError>

Source§

impl<D: WireSchema> WireSchema for EventLog<D>

Source§

fn wire_schema() -> Cow<'static, [u8]>

Borrow fixed identities; return owned bytes for composed schemas.
Source§

const REQUIRES_ARITY: bool = false

Source§

impl<D: Eq> Eq for EventLog<D>

Auto Trait Implementations§

§

impl<D> Freeze for EventLog<D>

§

impl<D> RefUnwindSafe for EventLog<D>
where D: RefUnwindSafe,

§

impl<D> Send for EventLog<D>
where D: Send,

§

impl<D> Sync for EventLog<D>
where D: Sync,

§

impl<D> Unpin for EventLog<D>
where D: Unpin,

§

impl<D> UnsafeUnpin for EventLog<D>

§

impl<D> UnwindSafe for EventLog<D>
where D: RefUnwindSafe + UnwindSafe,

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> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

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> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
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.