use std::collections::{HashMap, HashSet};
use std::fs::File as SysFile;
use std::io::{Cursor, Read, Write};
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use iter::LevelManifestIterator;
pub(crate) use level::{Level, Levels};
use rand::Rng;
use crate::error::Error;
use crate::sstable::table::Table;
use crate::vfs::File;
use crate::wal::list_segment_ids;
use crate::{Options, Result};
pub(crate) fn validate_wal_log_number(wal_path: &Path, manifest_log_number: u64) -> Result<()> {
if let Ok(segment_ids) = list_segment_ids(wal_path, Some("wal")) {
if !segment_ids.is_empty() {
let last_wal = *segment_ids.last().unwrap();
if manifest_log_number > last_wal + 1 {
return Err(Error::ManifestCorruption(format!(
"log_number {} exceeds WAL segments (max={}). \
Possible manifest corruption or incomplete backup restoration.",
manifest_log_number, last_wal
)));
}
}
}
Ok(())
}
pub const MANIFEST_FORMAT_VERSION_V1: u16 = 1;
#[derive(Debug, Clone)]
pub(crate) struct SnapshotInfo {
pub seq_num: u64,
pub created_at: u128,
}
impl SnapshotInfo {
pub(crate) fn encode(&self) -> Result<Vec<u8>> {
let mut buf = Vec::new();
buf.write_u64::<BigEndian>(self.seq_num)?;
buf.write_u128::<BigEndian>(self.created_at)?;
Ok(buf)
}
pub(crate) fn decode(mut buf: &[u8]) -> Result<Self> {
let seq_num = buf.read_u64::<BigEndian>()?;
let created_at = buf.read_u128::<BigEndian>()?;
Ok(Self {
seq_num,
created_at,
})
}
}
#[derive(Clone, Default)]
pub(crate) struct ManifestChangeSet {
pub manifest_format_version: Option<u16>,
pub deleted_tables: HashSet<(u8, u64)>,
pub new_tables: Vec<(u8, Arc<Table>)>,
pub new_snapshots: Vec<SnapshotInfo>,
pub deleted_snapshots: HashSet<u64>,
pub log_number: Option<u64>,
}
pub(crate) struct ChangeSetRollback {
pub deleted_tables: Vec<(u8, Arc<Table>)>,
pub added_table_ids: Vec<(u8, u64)>,
pub deleted_snapshots: Vec<SnapshotInfo>,
pub added_snapshot_seqs: Vec<u64>,
pub prev_version: Option<u16>,
pub prev_log_number: Option<u64>,
pub prev_last_sequence: u64,
}
mod iter;
mod level;
pub type HiddenSet = HashSet<u64>;
pub(crate) struct LevelManifest {
pub path: PathBuf,
pub levels: Levels,
pub(crate) hidden_set: HiddenSet,
pub(crate) next_table_id: Arc<AtomicU64>,
pub manifest_format_version: u16,
pub snapshots: Vec<SnapshotInfo>,
pub(crate) log_number: u64,
pub(crate) last_sequence: u64,
}
impl LevelManifest {
pub(crate) fn new(opts: Arc<Options>) -> Result<Self> {
assert!(opts.level_count > 0, "level_count should be >= 1");
let manifest_file_path = opts.manifest_file_path(0);
if manifest_file_path.exists() {
return Self::load_from_file(&manifest_file_path, opts);
}
let levels = Self::initialize_levels(opts.level_count);
let next_table_id = Arc::new(AtomicU64::new(1));
let manifest = Self {
path: manifest_file_path,
levels,
hidden_set: HashSet::with_capacity(10),
next_table_id,
manifest_format_version: MANIFEST_FORMAT_VERSION_V1,
snapshots: Vec::new(),
log_number: 0,
last_sequence: 0,
};
write_manifest_to_disk(&manifest)?;
Ok(manifest)
}
pub(crate) fn get_log_number(&self) -> u64 {
self.log_number
}
pub(crate) fn get_last_sequence(&self) -> u64 {
self.last_sequence
}
fn initialize_levels(level_count: u8) -> Levels {
let levels = (0..level_count).map(|_| Arc::new(Level::default())).collect::<Vec<_>>();
Levels(levels)
}
pub(crate) fn load_from_file<P: AsRef<Path>>(
manifest_path: P,
opts: Arc<Options>,
) -> Result<Self> {
log::info!("Loading manifest from {:?}", manifest_path.as_ref());
let data = std::fs::read(&manifest_path)?;
let mut level_manifest = Cursor::new(data);
let version = level_manifest.read_u16::<BigEndian>()?;
if version != MANIFEST_FORMAT_VERSION_V1 {
return Err(Error::LoadManifestFail(format!(
"Unsupported manifest format version: {}",
version
)));
}
let next_table_id = level_manifest.read_u64::<BigEndian>()?;
let log_number = level_manifest.read_u64::<BigEndian>()?;
let last_sequence = level_manifest.read_u64::<BigEndian>()?;
log::debug!(
"Manifest header: version={}, next_table_id={}, log_number={}, last_sequence={}",
version,
next_table_id,
log_number,
last_sequence
);
validate_wal_log_number(&opts.wal_dir(), log_number)?;
let level_data = Levels::decode(&mut level_manifest)?;
let snapshot_count = level_manifest.read_u32::<BigEndian>()?;
let mut snapshots = Vec::new();
for _ in 0..snapshot_count {
let snapshot_len = level_manifest.read_u32::<BigEndian>()? as usize;
let mut snapshot_bytes = vec![0u8; snapshot_len];
level_manifest.read_exact(&mut snapshot_bytes)?;
let snapshot = SnapshotInfo::decode(&snapshot_bytes)?;
snapshots.push(snapshot);
}
let mut levels_vec = Vec::with_capacity(level_data.len());
for (level_idx, table_ids) in level_data.iter().enumerate() {
let mut tables = Vec::with_capacity(table_ids.len());
for &table_id in table_ids {
match Self::load_table(table_id, Arc::clone(&opts)) {
Ok(table) => tables.push(table),
Err(err) => {
log::error!("Error loading table {table_id}: {err:?}");
return Err(Error::LoadManifestFail(err.to_string()));
}
}
}
if level_idx > 0 && !tables.is_empty() {
Self::validate_table_sequence_numbers(level_idx as u8, &tables)?;
}
let level = Level {
tables,
};
levels_vec.push(Arc::new(level));
}
let total_tables: usize = levels_vec.iter().map(|l| l.tables.len()).sum();
log::info!(
"Manifest loaded successfully: version={}, log_number={}, last_sequence={}, tables={}, levels={}",
version,
log_number,
last_sequence,
total_tables,
levels_vec.len()
);
let computed_max_seq = levels_vec
.iter()
.flat_map(|level| level.tables.iter())
.filter_map(|table| table.meta.largest_seq_num)
.max()
.unwrap_or(0);
if computed_max_seq != last_sequence {
return Err(Error::LoadManifestFail(format!(
"Manifest last_sequence mismatch: stored={}, computed from tables={}",
last_sequence, computed_max_seq
)));
}
Ok(Self {
path: manifest_path.as_ref().to_path_buf(),
levels: Levels(levels_vec),
hidden_set: HashSet::with_capacity(10),
next_table_id: Arc::new(AtomicU64::new(next_table_id)),
manifest_format_version: version,
snapshots,
log_number,
last_sequence,
})
}
fn validate_table_sequence_numbers(level_idx: u8, tables: &[Arc<Table>]) -> Result<()> {
for table in tables {
let (smallest, largest) =
match (table.meta.smallest_seq_num, table.meta.largest_seq_num) {
(Some(s), Some(l)) => (s, l),
(None, None) => {
return Err(Error::LoadManifestFail(format!(
"Table {} has no sequence numbers (possibly corrupted or empty table)",
table.id
)));
}
(smallest, largest) => {
return Err(Error::LoadManifestFail(format!(
"Table {} has inconsistent sequence numbers: smallest={:?}, largest={:?}",
table.id, smallest, largest
)));
}
};
if smallest > largest {
return Err(Error::LoadManifestFail(format!(
"Table {} has invalid sequence numbers: smallest({}) > largest({})",
table.id, smallest, largest
)));
}
}
if tables.len() > 1 {
for i in 0..tables.len() - 1 {
let current = &tables[i];
let next = &tables[i + 1];
if let (Some(next_smallest), Some(current_largest)) =
(next.meta.smallest_seq_num, current.meta.largest_seq_num)
{
if next_smallest <= current_largest {
return Err(Error::LoadManifestFail(format!(
"Level {} tables have overlapping sequence numbers: Table {} ({:?}-{:?}) and Table {} ({:?}-{:?})",
level_idx,
current.id, current.meta.smallest_seq_num, current.meta.largest_seq_num,
next.id, next.meta.smallest_seq_num, next.meta.largest_seq_num
)));
}
}
}
}
Ok(())
}
fn load_table(table_id: u64, opts: Arc<Options>) -> Result<Arc<Table>> {
let table_file_path = opts.sstable_file_path(table_id);
let file = SysFile::open(&table_file_path)?;
let file: Arc<dyn File> = Arc::new(file);
let file_size = file.size()?;
let table = Arc::new(Table::new(table_id, opts, file, file_size)?);
Ok(table)
}
fn depth(&self) -> u8 {
let len = self.levels.as_ref().len() as u8;
len
}
pub(crate) fn last_level_index(&self) -> u8 {
self.depth() - 1
}
pub(crate) fn iter(&self) -> impl Iterator<Item = Arc<Table>> + '_ {
LevelManifestIterator::new(self)
}
pub(crate) fn min_oldest_vlog_file_id(&self) -> u32 {
self.iter()
.filter_map(|sst| {
let oldest = sst.meta.properties.oldest_vlog_file_id;
if oldest > 0 {
Some(oldest as u32)
} else {
None
}
})
.min()
.unwrap_or(0)
}
pub(crate) fn get_all_tables(&self) -> HashMap<u64, Arc<Table>> {
let mut output = HashMap::new();
for table in self.iter() {
output.insert(table.meta.properties.id, table);
}
output
}
pub(crate) fn unhide_tables(&mut self, keys: &[u64]) {
for key in keys {
self.hidden_set.remove(key);
}
}
pub(crate) fn hide_tables(&mut self, keys: &[u64]) {
for key in keys {
self.hidden_set.insert(*key);
}
}
pub(crate) fn apply_changeset(
&mut self,
changeset: &ManifestChangeSet,
) -> Result<ChangeSetRollback> {
let mut rollback = ChangeSetRollback {
deleted_tables: Vec::new(),
added_table_ids: Vec::new(),
deleted_snapshots: Vec::new(),
added_snapshot_seqs: Vec::new(),
prev_version: None,
prev_log_number: None,
prev_last_sequence: self.last_sequence,
};
if let Some(version) = changeset.manifest_format_version {
rollback.prev_version = Some(self.manifest_format_version);
self.manifest_format_version = version;
}
if let Some(log_num) = changeset.log_number {
if log_num > self.log_number {
rollback.prev_log_number = Some(self.log_number);
self.log_number = log_num;
}
}
for (level, table_id) in &changeset.deleted_tables {
if let Some(level_ref) = self.levels.get_levels_mut().get_mut(*level as usize) {
if let Some(table) = level_ref.tables.iter().find(|t| t.id == *table_id) {
rollback.deleted_tables.push((*level, Arc::clone(table)));
}
Arc::make_mut(level_ref).remove(*table_id);
}
}
for (level, table) in &changeset.new_tables {
if let Some(level_ref) = self.levels.get_levels_mut().get_mut(*level as usize) {
let level_mut = Arc::make_mut(level_ref);
if *level == 0 {
level_mut.insert(Arc::clone(table));
} else {
level_mut.insert_sorted_by_key(Arc::clone(table));
}
rollback.added_table_ids.push((*level, table.id));
}
let largest = table.meta.largest_seq_num.expect("table must have largest_seq_num");
if largest > self.last_sequence {
self.last_sequence = largest;
}
}
let deleted: Vec<_> = self
.snapshots
.iter()
.filter(|s| changeset.deleted_snapshots.contains(&s.seq_num))
.cloned()
.collect();
rollback.deleted_snapshots = deleted;
self.snapshots.retain(|snapshot| !changeset.deleted_snapshots.contains(&snapshot.seq_num));
for snapshot in &changeset.new_snapshots {
self.snapshots.push(snapshot.clone());
rollback.added_snapshot_seqs.push(snapshot.seq_num);
}
Ok(rollback)
}
pub(crate) fn revert_changeset(&mut self, rollback: ChangeSetRollback) {
self.last_sequence = rollback.prev_last_sequence;
if let Some(prev_version) = rollback.prev_version {
self.manifest_format_version = prev_version;
}
if let Some(prev_log_number) = rollback.prev_log_number {
self.log_number = prev_log_number;
}
for (level, table_id) in rollback.added_table_ids {
if let Some(level_ref) = self.levels.get_levels_mut().get_mut(level as usize) {
Arc::make_mut(level_ref).remove(table_id);
}
}
for (level, table) in rollback.deleted_tables {
if let Some(level_ref) = self.levels.get_levels_mut().get_mut(level as usize) {
let level_mut = Arc::make_mut(level_ref);
if level == 0 {
level_mut.insert(table);
} else {
level_mut.insert_sorted_by_key(table);
}
}
}
self.snapshots.retain(|s| !rollback.added_snapshot_seqs.contains(&s.seq_num));
for snapshot in rollback.deleted_snapshots {
self.snapshots.push(snapshot);
}
}
pub(crate) fn next_table_id(&self) -> u64 {
self.next_table_id.fetch_add(1, std::sync::atomic::Ordering::Release)
}
}
pub(crate) fn replace_file_content<P: AsRef<Path>>(
file_path: P,
new_content: &[u8],
) -> std::io::Result<()> {
let target_path = file_path.as_ref();
let directory = target_path
.parent()
.ok_or(std::io::Error::new(std::io::ErrorKind::NotFound, "Parent directory not found"))?;
let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_nanos();
let temp_filename = format!(".tmp_{}_{}", timestamp, rand::rng().random::<u64>());
let temp_path = directory.join(temp_filename);
let original_permissions = std::fs::metadata(target_path).ok().map(|m| m.permissions());
{
let mut temp_file = SysFile::create(&temp_path)?;
temp_file.write_all(new_content)?;
temp_file.sync_all()?;
}
if let Some(permissions) = original_permissions {
if let Err(e) = std::fs::set_permissions(&temp_path, permissions) {
let _ = std::fs::remove_file(&temp_path);
return Err(e);
}
}
if let Err(e) = std::fs::rename(&temp_path, target_path) {
let _ = std::fs::remove_file(&temp_path);
return Err(e);
}
let updated_file = crate::vfs::open_for_sync(target_path)?;
updated_file.sync_all()?;
Ok(())
}
pub(crate) fn write_manifest_to_disk(manifest: &LevelManifest) -> Result<()> {
let next_table_id = manifest.next_table_id.load(Ordering::SeqCst);
let total_tables: usize = manifest.levels.get_levels().iter().map(|l| l.tables.len()).sum();
log::debug!(
"Writing manifest: version={}, log_number={}, last_sequence={}, next_table_id={}, total_tables={}",
manifest.manifest_format_version,
manifest.log_number,
manifest.last_sequence,
next_table_id,
total_tables
);
let mut buf = Vec::new();
buf.write_u16::<BigEndian>(manifest.manifest_format_version)?;
buf.write_u64::<BigEndian>(next_table_id)?;
buf.write_u64::<BigEndian>(manifest.log_number)?;
buf.write_u64::<BigEndian>(manifest.last_sequence)?;
manifest.levels.encode(&mut buf)?;
buf.write_u32::<BigEndian>(manifest.snapshots.len() as u32)?;
for snapshot in &manifest.snapshots {
let snapshot_bytes = snapshot.encode()?;
buf.write_u32::<BigEndian>(snapshot_bytes.len() as u32)?;
buf.extend_from_slice(&snapshot_bytes);
}
replace_file_content(&manifest.path, &buf)?;
log::debug!("Manifest written successfully to {:?}", manifest.path);
Ok(())
}