#![warn(missing_docs)]
mod config;
mod crc;
mod error;
mod lsn;
mod observer;
mod reader;
mod record;
mod recovery;
mod segment;
mod wal;
pub use config::WalConfig;
pub use crc::crc32c;
pub use error::{Result, WalError};
pub use lsn::Lsn;
pub use observer::{DurabilityObserver, NullObserver};
pub use reader::Reader;
pub use wal::{RecoveryReport, TailState, Wal};
#[cfg(feature = "fuzzing")]
#[doc(hidden)]
pub mod fuzzing {
use std::fs::File;
use crate::Lsn;
#[must_use]
pub fn decode_record(buf: &[u8], max_record_size: u32) -> Option<(usize, usize)> {
match crate::record::decode(buf, max_record_size) {
crate::record::Decoded::Record {
payload,
framed_len,
..
} => Some((payload.len(), framed_len)),
_ => None,
}
}
#[must_use]
pub fn recover_segment_probe(
file: &File,
base_lsn: u64,
is_active: bool,
segment_size: u64,
max_record_size: u32,
) -> bool {
scan_probe_reset();
let base = Lsn(base_lsn.max(1));
let r =
crate::recovery::recover_segment(file, base, is_active, segment_size, max_record_size);
let peak = scan_probe_peak();
assert!(
peak <= scan_bound(max_record_size),
"bounded-scan probe peak {peak} exceeded scan_bound {} (max_record_size {max_record_size})",
scan_bound(max_record_size),
);
r.is_ok()
}
#[must_use]
pub fn scan_bound(max_record_size: u32) -> u64 {
crate::recovery::scan_bound(max_record_size)
}
pub fn scan_probe_reset() {
crate::recovery::scan_probe::reset();
}
#[must_use]
pub fn scan_probe_peak() -> u64 {
crate::recovery::scan_probe::peak()
}
#[must_use]
pub fn segment_header_bytes(base_lsn: u64) -> Vec<u8> {
crate::segment::encode_header(Lsn(base_lsn), 0).to_vec()
}
pub fn encode_record_into(buf: &mut Vec<u8>, lsn: u64, payload: &[u8]) -> usize {
crate::record::encode_into(buf, Lsn(lsn), payload)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SegClass {
Clean { max_lsn: u64 },
Truncated { offset: u64, max_lsn: u64 },
TornMidLog { offset: u64 },
Corruption { offset: u64 },
OtherErr,
}
#[must_use]
pub fn recover_segment_classify(
file: &File,
base_lsn: u64,
is_active: bool,
segment_size: u64,
max_record_size: u32,
) -> SegClass {
use crate::error::WalError;
use crate::wal::TailState;
let base = Lsn(base_lsn.max(1));
match crate::recovery::recover_segment(file, base, is_active, segment_size, max_record_size)
{
Ok(rec) => match rec.tail_state {
TailState::Clean => SegClass::Clean {
max_lsn: rec.max_lsn.0,
},
TailState::TruncatedAt { offset, .. } => SegClass::Truncated {
offset,
max_lsn: rec.max_lsn.0,
},
},
Err(WalError::TornMidLog { offset, .. }) => SegClass::TornMidLog { offset },
Err(WalError::Corruption { offset, .. }) => SegClass::Corruption { offset },
Err(_) => SegClass::OtherErr,
}
}
}