use std::fs::File;
use memmap2::MmapOptions;
use crate::buffer::scan_recovery;
use crate::file_manager::FileManager;
use crate::protocol::{LogHeader, HEADER_LEN, MAGIC_END};
use crate::recovery::{build_sync_tip_block, current_mark_info};
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum FileIoAction {
None = 0,
Success = 1,
Unnecessary = 2,
OpenFailed = 3,
ReadFailed = 4,
WriteFailed = 5,
CloseFailed = 6,
RemoveFailed = 7,
}
pub fn oneshot_flush(
file_manager: &FileManager,
mmap_capacity: usize,
max_file_size: u64,
) -> FileIoAction {
let mmap_path = file_manager.mmap_path();
if !mmap_path.exists() {
return FileIoAction::Unnecessary;
}
let f = match File::open(&mmap_path) {
Ok(f) => f,
Err(_) => return FileIoAction::OpenFailed,
};
let mmap_len = match f.metadata() {
Ok(meta) => match usize::try_from(meta.len()) {
Ok(len) => len,
Err(_) => return FileIoAction::ReadFailed,
},
Err(_) => return FileIoAction::ReadFailed,
};
if mmap_len != mmap_capacity {
return FileIoAction::ReadFailed;
}
let data = match unsafe { MmapOptions::new().len(mmap_capacity).map(&f) } {
Ok(mapped) => mapped,
Err(_) => return FileIoAction::ReadFailed,
};
let scan = scan_recovery(&data);
if scan.valid_len == 0 {
return FileIoAction::Unnecessary;
}
let sample_header = if scan.valid_len >= HEADER_LEN {
LogHeader::decode(&data[..HEADER_LEN]).ok()
} else {
None
};
if let Some(begin) = build_sync_tip_block(
sample_header,
"~~~~~ begin of mmap from other process ~~~~~\n",
) {
if append_recovered_bytes_durable(file_manager, &begin, max_file_size).is_err() {
return FileIoAction::WriteFailed;
}
}
if scan.recovered_pending_block {
let mut recovered = Vec::with_capacity(scan.valid_len.saturating_add(1));
recovered.extend_from_slice(&data[..scan.valid_len]);
recovered.push(MAGIC_END);
if append_recovered_bytes_durable(file_manager, &recovered, max_file_size).is_err() {
return FileIoAction::WriteFailed;
}
} else if append_recovered_bytes_durable(file_manager, &data[..scan.valid_len], max_file_size)
.is_err()
{
return FileIoAction::WriteFailed;
}
let end_tip = format!(
"~~~~~ end of mmap from other process ~~~~~{}\n",
current_mark_info()
);
if let Some(end) = build_sync_tip_block(sample_header, &end_tip) {
if append_recovered_bytes_durable(file_manager, &end, max_file_size).is_err() {
return FileIoAction::WriteFailed;
}
}
drop(data);
if std::fs::remove_file(&mmap_path).is_err() {
return FileIoAction::RemoveFailed;
}
FileIoAction::Success
}
fn append_recovered_bytes_durable(
file_manager: &FileManager,
bytes: &[u8],
max_file_size: u64,
) -> Result<(), crate::file_manager::FileManagerError> {
file_manager.append_log_bytes_durable(bytes, max_file_size, false)
}