stemedb/crates/stemedb-wal/src/error.rs
jordan 3320c24afa feat: WAL hardening (Phase 5B) - CRC32C, crash recovery, group commit, log rotation
Add CRC32C checksums to WAL record format (v2), implement crash recovery
with automatic truncation of corrupt records, add feature-gated group commit
buffer for batched fsync under concurrent load, and implement log rotation
via segment files with global offset addressing.

Key changes:
- Record format v2: [len:u32][crc32c:u32][blake3:32][payload:N]
- recover_file() scans and truncates corrupt tail records
- GroupCommitBuffer batches fsync via MPSC channel (tokio feature gate)
- SegmentManager with binary search resolution and cursor-based cleanup
- Journal::read() auto-refreshes segments on miss for writer/reader split
- Split recovery.rs and key_codec.rs into directory modules for 500-line max

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 12:36:35 -07:00

78 lines
2.2 KiB
Rust

use std::io;
use std::path::PathBuf;
use thiserror::Error;
/// Result type for WAL operations.
pub type Result<T> = std::result::Result<T, QuarantineError>;
/// Errors that can occur during WAL operations.
#[derive(Error, Debug)]
pub enum QuarantineError {
/// IO error at a specific path.
#[error("IO error at {path:?}: {source}")]
Io {
/// The path where the error occurred.
path: PathBuf,
/// The underlying IO error.
source: io::Error,
},
/// Failed to fsync a file.
#[error("Failed to fsync {path:?}: {source}")]
FsyncFailed {
/// The path of the file.
path: PathBuf,
/// The underlying IO error.
source: io::Error,
},
/// File is locked by another process.
#[error("File is locked: {path:?}")]
FileLocked {
/// The path of the locked file.
path: PathBuf,
},
/// CRC32C checksum mismatch (fast integrity check, detects torn writes).
#[error(
"CRC32C mismatch at offset {offset}: expected {expected:#010x}, actual {actual:#010x}"
)]
Crc32cMismatch {
/// File offset where the corrupt record starts.
offset: u64,
/// Expected CRC32C value from the record header.
expected: u32,
/// Actual CRC32C computed from the data.
actual: u32,
},
/// Record length field is invalid (zero or exceeds MAX_RECORD_SIZE).
#[error("Invalid record length at offset {offset}: {length} bytes")]
InvalidRecordLength {
/// File offset where the record starts.
offset: u64,
/// The invalid length value read.
length: u32,
},
/// Generic record corruption with a descriptive reason.
#[error("Corrupt record at offset {offset}: {reason}")]
CorruptRecord {
/// File offset where corruption was detected.
offset: u64,
/// Human-readable description of the corruption.
reason: String,
},
/// Generic IO error.
#[error(transparent)]
IoGeneric(#[from] io::Error),
}
impl QuarantineError {
/// Helper to create an `Io` error variant.
pub fn io(path: impl Into<PathBuf>, source: io::Error) -> Self {
Self::Io { path: path.into(), source }
}
}