pub struct Wal<O: DurabilityObserver = NullObserver> { /* private fields */ }Expand description
Single-writer, append-only write-ahead log handle.
Send but not Sync (§6.2): the write methods take &mut self, and the
PhantomData<Cell<()>> marker makes sharing the handle across threads a
compile error, so concurrent writers cannot exist. Generic over a
DurabilityObserver; the default NullObserver compiles away.
Implementations§
Source§impl Wal<NullObserver>
impl Wal<NullObserver>
Sourcepub fn open(
dir: &Path,
config: WalConfig,
) -> Result<(Wal<NullObserver>, RecoveryReport)>
pub fn open( dir: &Path, config: WalConfig, ) -> Result<(Wal<NullObserver>, RecoveryReport)>
Open or create a WAL in dir with the default no-op observer.
Source§impl<O: DurabilityObserver> Wal<O>
impl<O: DurabilityObserver> Wal<O>
Sourcepub fn open_with(
dir: &Path,
config: WalConfig,
observer: O,
) -> Result<(Wal<O>, RecoveryReport)>
pub fn open_with( dir: &Path, config: WalConfig, observer: O, ) -> Result<(Wal<O>, RecoveryReport)>
Open or create a WAL in dir, running recovery, with an explicit
observer (§6). Acquires an exclusive advisory lock on the directory;
fails with Locked if already held.
Runs full recovery (§8): it cold-starts an empty directory (creating
…0001.wal) or discovers every *.wal segment, sorts them by base_lsn,
validates each header, discards an incomplete-header highest-base file left
by a crash mid-create (§8.4), recovers each segment’s record run (§8.2 —
torn-tail truncation + durable zeroing on the active segment, fatal mid-log
corruption), and verifies cross-segment LSN continuity (§8.1).
Sourcepub fn append(&mut self, payload: &[u8]) -> Result<Lsn>
pub fn append(&mut self, payload: &[u8]) -> Result<Lsn>
Sequence + buffer a record (§7.1). Pure memory: no syscall, no allocation
once the staging buffer is warm. The record is not durable until a
later commit returns covering it.
Sourcepub fn commit(&mut self) -> Result<Lsn>
pub fn commit(&mut self) -> Result<Lsn>
Make all buffered records durable (§7.2), splitting across segments on
whole-record boundaries when the batch does not fit the active segment
(§7.3). Each segment touched gets its own write + fdatasync
(F_FULLFSYNC on macOS) and advances durable_lsn to that segment’s last
LSN; the observer fires after each advance (§15.3).
commit is not atomic (§4.1): on a multi-segment split a crash or an
I/O failure between two segments’ syncs leaves the first segment durable
and the rest lost — a valid dense suffix (D9), never an internal gap. On
any write/fdatasync/roll failure the handle is poisoned (§12);
durable_lsn keeps whatever earlier segments achieved (monotonic, never
regresses).
Sourcepub fn durable_lsn(&self) -> Lsn
pub fn durable_lsn(&self) -> Lsn
Highest durable LSN (§6).
Sourcepub fn reader_from(&self, from: Lsn) -> Result<Reader<'_>>
pub fn reader_from(&self, from: Lsn) -> Result<Reader<'_>>
A streaming replay Reader starting at from (§6).
from == Lsn(0) means “from the beginning”. A from below the oldest
available LSN is a fatal gap (§15.4) — the needed records were
checkpointed away; never a silent skip. (Dormant in M2, where
oldest_lsn == 1.)
Sourcepub fn checkpoint(&mut self, up_to: Lsn) -> Result<()>
pub fn checkpoint(&mut self, up_to: Lsn) -> Result<()>
Delete whole sealed segments fully superseded by up_to, reclaiming space
(§9). A segment covering [b, b') (b' = the next segment’s base) is
deletable iff b' − 1 ≤ up_to; the active segment is never deleted.
Deletion is oldest-first, then a single directory fsync makes the
unlinks durable. Because only a prefix is ever removed, survivors remain a
contiguous suffix at every crash point (D8/D9): a crash before the
dir-fsync recovers via the §4 D2 “missing prefix accepted silently” path to
the same suffix. Checkpoint only unlinks whole files — it never rewrites or
compacts — and advances oldest_lsn (P).
Caller rule (§9): pass only up_to ≤ your latest durable *snapshot* LSN, never durable_lsn — recovery is “latest durable snapshot + replay of
the log after it”, so deleting the log past your snapshot silently caps
recovery. The WAL trusts the caller and cannot verify this (the inverse of
D8). Any unlink/fsync failure poisons the handle (§12).