use std::io::{Seek, SeekFrom, Write};
use std::sync::atomic::Ordering;
use linkrs_core::wal::types::{Lsn, WalError, WalFileHeader, WalResult, WAL_FILE_HEADER_SIZE};
use super::LocalWalWriter;
impl LocalWalWriter {
pub(crate) fn write_file_header(&mut self) -> WalResult<()> {
let current_lsn = Lsn::new(self.current_lsn.load(Ordering::SeqCst));
let header = WalFileHeader::new(self.thread_id, self.checkpoint_seq, current_lsn)
.with_checksum_enabled(self.config.checksum_enabled);
log::error!(
"WAL WRITE_FILE_HEADER: path={:?}, start_lsn={}, checkpoint_seq={}, thread_id={}, file_used={}",
self.file_path, current_lsn, self.checkpoint_seq, self.thread_id, self.file_used
);
self.persist_file_header(header, true)
}
pub(crate) fn persist_file_header(
&mut self,
header: WalFileHeader,
reset_file_used: bool,
) -> WalResult<()> {
let header_bytes = header.as_bytes();
let file = self.file.as_mut().ok_or(WalError::Closed)?;
file.seek(SeekFrom::Start(0))?;
file.write_all(&header_bytes)?;
file.sync_all()?;
self.file_header = Some(header);
self.file_start_lsn = header.start_lsn();
if reset_file_used {
self.file_used = WAL_FILE_HEADER_SIZE;
}
Ok(())
}
pub(crate) fn refresh_file_header(&mut self) -> WalResult<()> {
if self.file.is_none() {
return Ok(());
}
let Some(header) = self.file_header else {
return Ok(());
};
let updated_header = WalFileHeader {
checkpoint_seq: self.checkpoint_seq,
..header
};
self.persist_file_header(updated_header, false)
}
pub fn file_start_lsn(&self) -> Lsn {
self.file_start_lsn
}
pub fn set_recovery_baseline_lsn(&mut self, lsn: Lsn) -> WalResult<()> {
let current_lsn = self.current_lsn();
log::error!(
"WAL SET_RECOVERY_BASELINE: requested={}, current={}, file_used={}, file_start_lsn={}",
lsn,
current_lsn,
self.file_used,
self.file_start_lsn
);
if lsn <= current_lsn {
return Ok(());
}
if self.file_used > WAL_FILE_HEADER_SIZE {
return Err(WalError::InvalidOperation(format!(
"Cannot advance WAL baseline to {} while the active segment contains records",
lsn
)));
}
self.current_lsn.store(lsn.as_u64(), Ordering::SeqCst);
self.last_synced_lsn.store(lsn.as_u64(), Ordering::SeqCst);
if let Some(header) = self.file_header {
let updated_header = WalFileHeader {
start_lsn: lsn.as_u64(),
..header
};
self.persist_file_header(updated_header, false)?;
} else {
self.file_start_lsn = lsn;
}
Ok(())
}
pub fn checkpoint_seq(&self) -> u64 {
self.checkpoint_seq
}
pub fn set_checkpoint_seq(&mut self, seq: u64) -> WalResult<()> {
self.checkpoint_seq = seq;
if self.file.is_some() {
self.refresh_file_header()?;
}
Ok(())
}
pub fn file_header(&self) -> Option<&WalFileHeader> {
self.file_header.as_ref()
}
}