pub struct DeviceLog { /* private fields */ }Expand description
The per-device append discipline: maintains the seq/prev chain and
stamps Hlc values from an HlcClock over an injected WallClock
— B3’s real hybrid clock, replacing B1’s pure-Lamport stamp source
behind the same wire shape.
DeviceLog::new defaults the wall source to logical_clock
(always 0), under which the HLC is a Lamport clock (every tick is a
counter increment) — B1 semantics as the degenerate case of one code
path. Real deployments pass system_clock (or a test-controlled
closure) via DeviceLog::with_wall_clock / DeviceLog::set_wall_clock.
Implementations§
Source§impl DeviceLog
impl DeviceLog
pub fn new(device_id: impl Into<String>) -> Self
Sourcepub fn with_wall_clock(device_id: impl Into<String>, wall: WallClock) -> Self
pub fn with_wall_clock(device_id: impl Into<String>, wall: WallClock) -> Self
A device log stamping the real HLC over the given wall source
(pass system_clock in production, a controlled closure in tests).
Sourcepub fn set_wall_clock(&mut self, wall: WallClock)
pub fn set_wall_clock(&mut self, wall: WallClock)
Swap the wall source on a live log (e.g. after a
DeviceLog::resume, which has no wall parameter). Monotonicity is
unaffected: the HlcClock never regresses below what it has
witnessed, whatever the new source reads.
Sourcepub fn resume(
device_id: impl Into<String>,
ops: &[OpRecord],
) -> Result<Self, ChainError>
pub fn resume( device_id: impl Into<String>, ops: &[OpRecord], ) -> Result<Self, ChainError>
Resume a device’s chain from previously persisted ops (e.g. after
crate::journal::OplogJournal::load): verifies the log, adopts this
device’s chain tail, and advances the clock past every op
present (local and remote), so new appends stamp above all of them.
The resumed log defaults to the logical_clock wall source — call
DeviceLog::set_wall_clock to attach the real one.
MUST: an op is journal-durable before it is transmitted. Resume
derives next_seq from the journal; if a crash lands between
“op sent to a peer/relay” and “op durably journaled”, the resumed
device re-mints that seq for a different op, and the union of the
two logs is a permanent DuplicateSeq/PrevMismatch — an
unrecoverable fork of the device’s chain. Always
OplogJournal::append (which flushes) before handing an op to any
transport (B3 must preserve this ordering).
Fenced against truncated tails (B4). If the resuming device’s
own chain doesn’t start at seq 0, the ops are a truncated tail and
resume refuses (ChainError::TruncatedChain) — the anchored
sibling checkpoint::resume_anchored is the correct path. (The case
this check can’t see — a device whose ops were ALL truncated away —
is fenced one layer down: OplogJournal::load refuses a journal
carrying a truncation marker.)
Sourcepub fn observe(&mut self, hlc: &Hlc)
pub fn observe(&mut self, hlc: &Hlc)
HLC receive rule: fold a received op’s stamp into the local clock, so a write that causally follows received ops stamps above them.
Sourcepub fn append(
&mut self,
scope: Scope,
surface: Surface,
payload: Value,
) -> OpRecord
pub fn append( &mut self, scope: Scope, surface: Surface, payload: Value, ) -> OpRecord
Append a new op: read the wall, tick the hybrid clock, stamp, link the chain.