use std::path::{Path, PathBuf};
use std::sync::Arc;
use radixdb_catalog::{CatalogGeneration as RuntimeCatalogGeneration, ObjectId};
use crate::v6::{
CatalogGeneration, CatalogWalReplayLimits, ControlRecord, FormatResult,
PhysicalGenerationSnapshot, ReachabilityAllowance, ReachabilityLimits, UnavailableIndex,
WalReplayFloor,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct RecoveryLimits {
reachability: ReachabilityLimits,
catalog_wal: CatalogWalReplayLimits,
}
impl RecoveryLimits {
pub const fn new(
reachability: ReachabilityLimits,
catalog_wal: CatalogWalReplayLimits,
) -> Self {
Self {
reachability,
catalog_wal,
}
}
pub const fn reachability(self) -> ReachabilityLimits {
self.reachability
}
pub const fn catalog_wal(self) -> CatalogWalReplayLimits {
self.catalog_wal
}
}
#[derive(Debug, Clone, Copy)]
pub struct DataWalRecoveryContext<'a> {
root: &'a Path,
floor: WalReplayFloor,
physical: &'a PhysicalGenerationSnapshot,
catalog: &'a RuntimeCatalogGeneration,
metadata_allowance: ReachabilityAllowance,
}
impl<'a> DataWalRecoveryContext<'a> {
pub(crate) const fn new(
root: &'a Path,
floor: WalReplayFloor,
physical: &'a PhysicalGenerationSnapshot,
catalog: &'a RuntimeCatalogGeneration,
metadata_allowance: ReachabilityAllowance,
) -> Self {
Self {
root,
floor,
physical,
catalog,
metadata_allowance,
}
}
pub const fn root(self) -> &'a Path {
self.root
}
pub const fn floor(self) -> WalReplayFloor {
self.floor
}
pub const fn physical(self) -> &'a PhysicalGenerationSnapshot {
self.physical
}
pub const fn catalog(self) -> &'a RuntimeCatalogGeneration {
self.catalog
}
pub const fn metadata_allowance(self) -> ReachabilityAllowance {
self.metadata_allowance
}
}
pub trait WalRecovery {
type State;
fn read_catalog_transactions(
&mut self,
floor: WalReplayFloor,
byte_budget: u64,
) -> FormatResult<Vec<u8>>;
fn replay_data(
&mut self,
context: DataWalRecoveryContext<'_>,
) -> FormatResult<DataWalRecoveryOutcome<Self::State>>;
}
#[derive(Debug)]
pub struct DataWalRecoveryOutcome<State> {
state: State,
last_lsn: u64,
applied_transactions: u64,
applied_entries: u64,
skipped_entries: u64,
runtime_table_ids: Vec<ObjectId>,
}
impl<State> DataWalRecoveryOutcome<State> {
pub fn new(
state: State,
last_lsn: u64,
applied_transactions: u64,
applied_entries: u64,
skipped_entries: u64,
runtime_table_ids: Vec<ObjectId>,
) -> Self {
Self {
state,
last_lsn,
applied_transactions,
applied_entries,
skipped_entries,
runtime_table_ids,
}
}
pub(crate) fn into_parts(self) -> (State, DataWalRecoveryReport, Vec<ObjectId>) {
(
self.state,
DataWalRecoveryReport {
last_lsn: self.last_lsn,
applied_transactions: self.applied_transactions,
applied_entries: self.applied_entries,
skipped_entries: self.skipped_entries,
},
self.runtime_table_ids,
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CatalogRecoveryReport {
replay_floor: WalReplayFloor,
decoded_transactions: u64,
replayed_transactions: u64,
committed_bytes: u64,
incomplete_tail_bytes: u64,
base_generation: CatalogGeneration,
final_generation: CatalogGeneration,
}
impl CatalogRecoveryReport {
#[allow(clippy::too_many_arguments)]
pub(crate) const fn new(
replay_floor: WalReplayFloor,
decoded_transactions: u64,
replayed_transactions: u64,
committed_bytes: u64,
incomplete_tail_bytes: u64,
base_generation: CatalogGeneration,
final_generation: CatalogGeneration,
) -> Self {
Self {
replay_floor,
decoded_transactions,
replayed_transactions,
committed_bytes,
incomplete_tail_bytes,
base_generation,
final_generation,
}
}
pub const fn replay_floor(self) -> WalReplayFloor {
self.replay_floor
}
pub const fn decoded_transactions(self) -> u64 {
self.decoded_transactions
}
pub const fn replayed_transactions(self) -> u64 {
self.replayed_transactions
}
pub const fn committed_bytes(self) -> u64 {
self.committed_bytes
}
pub const fn incomplete_tail_bytes(self) -> u64 {
self.incomplete_tail_bytes
}
pub const fn base_generation(self) -> CatalogGeneration {
self.base_generation
}
pub const fn final_generation(self) -> CatalogGeneration {
self.final_generation
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DataWalRecoveryReport {
last_lsn: u64,
applied_transactions: u64,
applied_entries: u64,
skipped_entries: u64,
}
impl DataWalRecoveryReport {
pub const fn last_lsn(self) -> u64 {
self.last_lsn
}
pub const fn applied_transactions(self) -> u64 {
self.applied_transactions
}
pub const fn applied_entries(self) -> u64 {
self.applied_entries
}
pub const fn skipped_entries(self) -> u64 {
self.skipped_entries
}
}
pub struct RecoveredDatabase<State> {
root: PathBuf,
control: ControlRecord,
physical: PhysicalGenerationSnapshot,
catalog: Arc<RuntimeCatalogGeneration>,
unavailable_indexes: Vec<UnavailableIndex>,
catalog_report: CatalogRecoveryReport,
data_report: DataWalRecoveryReport,
runtime_state: State,
}
impl<State> RecoveredDatabase<State> {
#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
root: PathBuf,
control: ControlRecord,
physical: PhysicalGenerationSnapshot,
catalog: RuntimeCatalogGeneration,
unavailable_indexes: Vec<UnavailableIndex>,
catalog_report: CatalogRecoveryReport,
data_report: DataWalRecoveryReport,
runtime_state: State,
) -> Self {
Self {
root,
control,
physical,
catalog: Arc::new(catalog),
unavailable_indexes,
catalog_report,
data_report,
runtime_state,
}
}
pub fn root(&self) -> &Path {
&self.root
}
pub const fn control(&self) -> ControlRecord {
self.control
}
pub const fn physical(&self) -> &PhysicalGenerationSnapshot {
&self.physical
}
pub const fn catalog(&self) -> &Arc<RuntimeCatalogGeneration> {
&self.catalog
}
pub fn unavailable_indexes(&self) -> &[UnavailableIndex] {
&self.unavailable_indexes
}
pub const fn catalog_report(&self) -> CatalogRecoveryReport {
self.catalog_report
}
pub const fn data_report(&self) -> DataWalRecoveryReport {
self.data_report
}
pub fn into_runtime_state(self) -> State {
self.runtime_state
}
}