use std::time::Duration;
use super::scheduler::CheckpointConfig;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum DurabilityMode {
Volatile,
#[default]
Lazy,
Durable,
Paranoid,
}
impl DurabilityMode {
pub fn writes_wal(&self) -> bool {
!matches!(self, DurabilityMode::Volatile)
}
pub fn sync_on_commit(&self) -> bool {
matches!(self, DurabilityMode::Durable | DurabilityMode::Paranoid)
}
pub fn sync_on_every_op(&self) -> bool {
matches!(self, DurabilityMode::Paranoid)
}
pub fn uses_batching(&self) -> bool {
matches!(self, DurabilityMode::Lazy)
}
pub fn enables_checkpointing(&self) -> bool {
!matches!(self, DurabilityMode::Volatile)
}
pub fn recommended_flush_interval_ms(&self) -> u64 {
match self {
DurabilityMode::Volatile => 0,
DurabilityMode::Lazy => 50,
DurabilityMode::Durable => 0, DurabilityMode::Paranoid => 0, }
}
pub fn recommended_flush_count(&self) -> usize {
match self {
DurabilityMode::Volatile => usize::MAX,
DurabilityMode::Lazy => 1000,
DurabilityMode::Durable => 1, DurabilityMode::Paranoid => 1, }
}
pub fn recommended_checkpoint_interval(&self) -> Duration {
match self {
DurabilityMode::Volatile => Duration::from_secs(u64::MAX),
DurabilityMode::Lazy => Duration::from_secs(30),
DurabilityMode::Durable => Duration::from_secs(30),
DurabilityMode::Paranoid => Duration::from_secs(10),
}
}
pub fn parse(s: &str) -> Option<Self> {
match s.to_lowercase().as_str() {
"volatile" => Some(DurabilityMode::Volatile),
"lazy" => Some(DurabilityMode::Lazy),
"durable" => Some(DurabilityMode::Durable),
"paranoid" => Some(DurabilityMode::Paranoid),
_ => None,
}
}
pub fn as_str(&self) -> &'static str {
match self {
DurabilityMode::Volatile => "volatile",
DurabilityMode::Lazy => "lazy",
DurabilityMode::Durable => "durable",
DurabilityMode::Paranoid => "paranoid",
}
}
}
impl std::fmt::Display for DurabilityMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum TransactionDurability {
#[default]
Default,
ForceDurable,
AllowLazy,
ForceVolatile,
}
impl TransactionDurability {
pub fn resolve(&self, database_mode: DurabilityMode) -> DurabilityMode {
match self {
TransactionDurability::Default => database_mode,
TransactionDurability::ForceDurable => DurabilityMode::Durable,
TransactionDurability::AllowLazy => {
match database_mode {
DurabilityMode::Volatile => DurabilityMode::Volatile,
DurabilityMode::Lazy => DurabilityMode::Lazy,
DurabilityMode::Durable | DurabilityMode::Paranoid => DurabilityMode::Lazy,
}
}
TransactionDurability::ForceVolatile => DurabilityMode::Volatile,
}
}
pub fn from_sql_hint(s: &str) -> Option<Self> {
match s.to_uppercase().as_str() {
"DEFAULT" => Some(TransactionDurability::Default),
"DURABLE" => Some(TransactionDurability::ForceDurable),
"LAZY" => Some(TransactionDurability::AllowLazy),
"VOLATILE" => Some(TransactionDurability::ForceVolatile),
_ => None,
}
}
}
#[derive(Debug, Clone)]
pub struct DurabilityConfig {
pub mode: DurabilityMode,
pub wal_flush_interval_ms: u64,
pub wal_flush_batch_size: usize,
pub checkpoint: CheckpointConfig,
}
impl Default for DurabilityConfig {
fn default() -> Self {
Self::lazy()
}
}
impl DurabilityConfig {
pub fn volatile() -> Self {
Self {
mode: DurabilityMode::Volatile,
wal_flush_interval_ms: 0,
wal_flush_batch_size: usize::MAX,
checkpoint: CheckpointConfig { auto_checkpoint: false, ..Default::default() },
}
}
pub fn lazy() -> Self {
Self {
mode: DurabilityMode::Lazy,
wal_flush_interval_ms: 50,
wal_flush_batch_size: 1000,
checkpoint: CheckpointConfig::default(),
}
}
pub fn durable() -> Self {
Self {
mode: DurabilityMode::Durable,
wal_flush_interval_ms: 0,
wal_flush_batch_size: 1,
checkpoint: CheckpointConfig::default(),
}
}
pub fn paranoid() -> Self {
Self {
mode: DurabilityMode::Paranoid,
wal_flush_interval_ms: 0,
wal_flush_batch_size: 1,
checkpoint: CheckpointConfig {
interval: Duration::from_secs(10),
..Default::default()
},
}
}
pub fn from_mode(mode: DurabilityMode) -> Self {
match mode {
DurabilityMode::Volatile => Self::volatile(),
DurabilityMode::Lazy => Self::lazy(),
DurabilityMode::Durable => Self::durable(),
DurabilityMode::Paranoid => Self::paranoid(),
}
}
pub fn with_flush_interval_ms(mut self, ms: u64) -> Self {
self.wal_flush_interval_ms = ms;
self
}
pub fn with_flush_batch_size(mut self, size: usize) -> Self {
self.wal_flush_batch_size = size;
self
}
pub fn with_checkpoint_interval(mut self, interval: Duration) -> Self {
self.checkpoint.interval = interval;
self
}
pub fn with_checkpoint_wal_size_threshold(mut self, bytes: u64) -> Self {
self.checkpoint.wal_size_threshold = bytes;
self
}
pub fn with_manual_checkpoint_only(mut self) -> Self {
self.checkpoint.auto_checkpoint = false;
self
}
pub fn browser_default() -> Self {
Self::volatile()
}
pub fn development() -> Self {
Self::volatile()
}
pub fn production() -> Self {
Self::lazy()
}
pub fn critical() -> Self {
Self::durable()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_durability_mode_defaults() {
assert_eq!(DurabilityMode::default(), DurabilityMode::Lazy);
}
#[test]
fn test_durability_mode_properties() {
assert!(!DurabilityMode::Volatile.writes_wal());
assert!(!DurabilityMode::Volatile.sync_on_commit());
assert!(!DurabilityMode::Volatile.enables_checkpointing());
assert!(DurabilityMode::Lazy.writes_wal());
assert!(!DurabilityMode::Lazy.sync_on_commit());
assert!(DurabilityMode::Lazy.uses_batching());
assert!(DurabilityMode::Lazy.enables_checkpointing());
assert!(DurabilityMode::Durable.writes_wal());
assert!(DurabilityMode::Durable.sync_on_commit());
assert!(!DurabilityMode::Durable.sync_on_every_op());
assert!(DurabilityMode::Durable.enables_checkpointing());
assert!(DurabilityMode::Paranoid.writes_wal());
assert!(DurabilityMode::Paranoid.sync_on_commit());
assert!(DurabilityMode::Paranoid.sync_on_every_op());
assert!(DurabilityMode::Paranoid.enables_checkpointing());
}
#[test]
fn test_durability_mode_parsing() {
assert_eq!(DurabilityMode::parse("volatile"), Some(DurabilityMode::Volatile));
assert_eq!(DurabilityMode::parse("LAZY"), Some(DurabilityMode::Lazy));
assert_eq!(DurabilityMode::parse("Durable"), Some(DurabilityMode::Durable));
assert_eq!(DurabilityMode::parse("PARANOID"), Some(DurabilityMode::Paranoid));
assert_eq!(DurabilityMode::parse("invalid"), None);
}
#[test]
fn test_transaction_durability_resolve() {
assert_eq!(
TransactionDurability::Default.resolve(DurabilityMode::Lazy),
DurabilityMode::Lazy
);
assert_eq!(
TransactionDurability::Default.resolve(DurabilityMode::Durable),
DurabilityMode::Durable
);
assert_eq!(
TransactionDurability::ForceDurable.resolve(DurabilityMode::Volatile),
DurabilityMode::Durable
);
assert_eq!(
TransactionDurability::ForceDurable.resolve(DurabilityMode::Lazy),
DurabilityMode::Durable
);
assert_eq!(
TransactionDurability::AllowLazy.resolve(DurabilityMode::Durable),
DurabilityMode::Lazy
);
assert_eq!(
TransactionDurability::AllowLazy.resolve(DurabilityMode::Paranoid),
DurabilityMode::Lazy
);
assert_eq!(
TransactionDurability::AllowLazy.resolve(DurabilityMode::Volatile),
DurabilityMode::Volatile
);
assert_eq!(
TransactionDurability::ForceVolatile.resolve(DurabilityMode::Paranoid),
DurabilityMode::Volatile
);
}
#[test]
fn test_durability_config_presets() {
let volatile = DurabilityConfig::volatile();
assert_eq!(volatile.mode, DurabilityMode::Volatile);
assert!(!volatile.checkpoint.auto_checkpoint);
let lazy = DurabilityConfig::lazy();
assert_eq!(lazy.mode, DurabilityMode::Lazy);
assert_eq!(lazy.wal_flush_interval_ms, 50);
let durable = DurabilityConfig::durable();
assert_eq!(durable.mode, DurabilityMode::Durable);
assert_eq!(durable.wal_flush_batch_size, 1);
let paranoid = DurabilityConfig::paranoid();
assert_eq!(paranoid.mode, DurabilityMode::Paranoid);
assert_eq!(paranoid.checkpoint.interval, Duration::from_secs(10));
}
#[test]
fn test_durability_config_builder() {
let config = DurabilityConfig::lazy()
.with_flush_interval_ms(100)
.with_flush_batch_size(500)
.with_checkpoint_interval(Duration::from_secs(60));
assert_eq!(config.wal_flush_interval_ms, 100);
assert_eq!(config.wal_flush_batch_size, 500);
assert_eq!(config.checkpoint.interval, Duration::from_secs(60));
}
}