pub struct Wal<F: FileBackend = FileHandle> { /* private fields */ }Expand description
The write-ahead log.
Owns the on-disk WAL file, the current generation salt, and the LSN
counter. The pager talks to a Wal via Wal::begin_txn, staging
per-page writes in a WalTxn and then calling WalTxn::commit
to make them durable.
Generic over F: FileBackend (Rule 9: static dispatch on the hot
path). Production code uses Wal<FileHandle>; the fault-injection
harness substitutes Wal<FaultyFileHandle> to drive recovery
against torn writes, dropped fsyncs, and bit flips.
Phase 4 (issue #9): when the parent pager opens an
encryption-capable file with the right key, the WAL also
encrypts each frame body with XChaCha20-Poly1305. The frame
layout gains a 40-byte suffix (nonce || tag), the on-disk
per-frame stride becomes 4200 bytes, and the frame’s existing
CRC32C is computed over (header_sans_crc + PLAINTEXT body) —
the CRC catches in-memory bit-flips on the post-decryption
representation rather than running on attacker-controlled
ciphertext.
Implementations§
Source§impl Wal<FileHandle>
impl Wal<FileHandle>
Sourcepub fn create_fresh(path: &Path, config: WalConfig) -> Result<Self>
pub fn create_fresh(path: &Path, config: WalConfig) -> Result<Self>
Create or truncate the WAL sidecar at path to a fresh,
empty WAL backed by a FileHandle. Convenience for
production callers; see Wal::create_fresh_with when the
caller already holds a backend instance (e.g. a fault-injection
harness).
§Errors
Returns Error::Io on syscall failure.
Sourcepub fn open_for_recovery(
path: &Path,
expected_salt: u32,
size_limit: u64,
) -> Result<Recovered>
pub fn open_for_recovery( path: &Path, expected_salt: u32, size_limit: u64, ) -> Result<Recovered>
Walk the on-disk WAL at path and produce a Recovered
snapshot, opening the WAL with a production FileHandle.
See Wal::open_for_recovery_with for the documented
algorithm; see Wal::create_fresh for the file-handle
rationale.
§Errors
Source§impl<F: FileBackend> Wal<F>
impl<F: FileBackend> Wal<F>
Sourcepub fn create_fresh_with(
file: F,
path: PathBuf,
config: WalConfig,
) -> Result<Self>
pub fn create_fresh_with( file: F, path: PathBuf, config: WalConfig, ) -> Result<Self>
Sourcepub fn from_recovered_meta(
file: F,
path: PathBuf,
salt: u32,
next_lsn: Lsn,
end_offset: u64,
committed_frames: u64,
config: WalConfig,
) -> Self
pub fn from_recovered_meta( file: F, path: PathBuf, salt: u32, next_lsn: Lsn, end_offset: u64, committed_frames: u64, config: WalConfig, ) -> Self
Adopt an already-walked WAL handle. Used by Pager::open
after Wal::open_for_recovery has returned a Recovered.
salt, next_lsn, committed_frames, and end_offset are
taken from recovered; the caller separately merges
recovered.view into the pager’s in-memory state.
Sourcepub fn open_for_recovery_with(
file: &F,
expected_salt: u32,
size_limit: u64,
) -> Result<Recovered>
pub fn open_for_recovery_with( file: &F, expected_salt: u32, size_limit: u64, ) -> Result<Recovered>
Walk an already-open WAL file and produce a Recovered
snapshot.
Algorithm (matches docs/format.md § Recovery semantics):
- If
pathdoes not exist, or is shorter than a WAL header, return an emptyRecoveredcarryingexpected_salt. - Read the WAL header. If magic / format-major / page-size
disagree with the build, fail with
Error::InvalidFormat. - If the header’s salt does not equal
expected_salt, the WAL is from a previous generation; return an emptyRecovered. - Pass 1: scan every aligned frame in the WAL and record the byte offset of the last frame whose salt matches and whose CRC validates AND whose commit-marker bit is set. Frames whose CRC fails (or whose salt does not match) in pass 1 are silently skipped — they might be torn-tail noise that precedes a later valid commit marker.
- Pass 2: walk frames from offset
WAL_HEADER_SIZEup to (but not past) the last-commit-end offset from pass 1. Any frame in this range whose salt matches MUST have a valid CRC; otherwise returnError::WalCorruption— the bad frame sits between two intact commit markers and recovery cannot determine if data was lost. - Salt-mismatched frames inside pass 2’s range are skipped (they are stale-generation noise, not corruption). Frames past the last commit marker are torn tail and are silently discarded.
§Errors
Error::Ioon syscall failure.Error::InvalidFormatwhen the WAL header is malformed in a way that indicates a config mismatch rather than torn tail.Error::WalCorruptionwhen a CRC-invalid frame sits before the last committed frame in the current generation.Error::InvalidArgumentifsize_limitwould be exceeded during the walk (a runaway WAL caps recovery).
Sourcepub fn open_for_recovery_with_key(
file: &F,
expected_salt: u32,
size_limit: u64,
key: Option<[u8; 32]>,
) -> Result<Recovered>
pub fn open_for_recovery_with_key( file: &F, expected_salt: u32, size_limit: u64, key: Option<[u8; 32]>, ) -> Result<Recovered>
Phase 4 (issue #9): same as
Self::open_for_recovery_with but takes an optional
per-file page-encryption key. On encrypted WALs each frame
body is decrypted with the supplied key BEFORE the frame’s
CRC32C is validated; the recovery walker therefore needs the
key at construction. The pager calls this entry point.
§Errors
As Self::open_for_recovery_with, plus
Error::EncryptionKeyInvalid when a salt-matching frame
in the WAL fails Poly1305 verification — the smoking-gun
wrong-key signal.
Sourcepub fn path(&self) -> &Path
pub fn path(&self) -> &Path
Path the WAL was opened at. Used by the pager to remove the sidecar on clean shutdown.
Sourcepub fn committed_frames(&self) -> u64
pub fn committed_frames(&self) -> u64
Frames currently on disk (committed; torn-tail not counted).
Sourcepub fn checkpoint_threshold(&self) -> u64
pub fn checkpoint_threshold(&self) -> u64
Configured auto-checkpoint threshold.