#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum SyncMode {
None = 0,
#[default]
Normal = 1,
Full = 2,
}
impl From<i32> for SyncMode {
fn from(value: i32) -> Self {
match value {
0 => SyncMode::None,
2 => SyncMode::Full,
_ => SyncMode::Normal,
}
}
}
impl From<SyncMode> for i32 {
fn from(mode: SyncMode) -> Self {
mode as i32
}
}
#[derive(Debug, Clone)]
pub struct PersistenceConfig {
pub enabled: bool,
pub sync_mode: SyncMode,
pub checkpoint_interval: u32,
pub compact_threshold: u32,
pub wal_flush_trigger: usize,
pub wal_buffer_size: usize,
pub wal_max_size: usize,
pub commit_batch_size: u32,
pub sync_interval_ms: u32,
pub wal_compression: bool,
pub volume_compression: bool,
pub compression_threshold: usize,
pub keep_snapshots: u32,
pub checkpoint_on_close: bool,
pub target_volume_rows: usize,
}
impl Default for PersistenceConfig {
fn default() -> Self {
Self {
enabled: true,
sync_mode: SyncMode::Normal,
checkpoint_interval: 60, compact_threshold: 4, wal_flush_trigger: 32 * 1024, wal_buffer_size: 64 * 1024, wal_max_size: 64 * 1024 * 1024, commit_batch_size: 100, sync_interval_ms: 1000, wal_compression: true, volume_compression: true, compression_threshold: 64, keep_snapshots: 3, checkpoint_on_close: true, target_volume_rows: 1_048_576, }
}
}
impl PersistenceConfig {
pub fn new() -> Self {
Self::default()
}
pub fn durable() -> Self {
Self {
enabled: true,
sync_mode: SyncMode::Full,
checkpoint_interval: 30, compact_threshold: 4,
wal_flush_trigger: 8 * 1024, wal_buffer_size: 32 * 1024, wal_max_size: 32 * 1024 * 1024, commit_batch_size: 1, sync_interval_ms: 0, wal_compression: true,
volume_compression: true,
compression_threshold: 64,
keep_snapshots: 3,
checkpoint_on_close: true,
target_volume_rows: 1_048_576,
}
}
pub fn fast() -> Self {
Self {
enabled: true,
sync_mode: SyncMode::None,
checkpoint_interval: 120, compact_threshold: 8,
wal_flush_trigger: 64 * 1024, wal_buffer_size: 128 * 1024, wal_max_size: 128 * 1024 * 1024, commit_batch_size: 500, sync_interval_ms: 100, wal_compression: true,
volume_compression: true,
compression_threshold: 64,
keep_snapshots: 3,
checkpoint_on_close: true,
target_volume_rows: 2_097_152, }
}
pub fn with_sync_mode(mut self, mode: SyncMode) -> Self {
self.sync_mode = mode;
self
}
pub fn with_checkpoint_interval(mut self, seconds: u32) -> Self {
self.checkpoint_interval = if seconds == 0 { 0 } else { seconds.max(5) };
self
}
pub fn with_compact_threshold(mut self, count: u32) -> Self {
self.compact_threshold = count;
self
}
pub fn with_wal_compression(mut self, enabled: bool) -> Self {
self.wal_compression = enabled;
self
}
pub fn with_volume_compression(mut self, enabled: bool) -> Self {
self.volume_compression = enabled;
self
}
pub fn with_compression(mut self, enabled: bool) -> Self {
self.wal_compression = enabled;
self.volume_compression = enabled;
self
}
pub fn with_compression_threshold(mut self, bytes: usize) -> Self {
self.compression_threshold = bytes;
self
}
pub fn with_keep_snapshots(mut self, count: u32) -> Self {
self.keep_snapshots = count;
self
}
pub fn with_target_volume_rows(mut self, rows: usize) -> Self {
self.target_volume_rows = rows.max(65_536);
self
}
}
#[derive(Debug, Clone)]
pub struct CleanupConfig {
pub enabled: bool,
pub interval_secs: u64,
pub deleted_row_retention_secs: u64,
pub transaction_retention_secs: u64,
}
impl Default for CleanupConfig {
fn default() -> Self {
Self {
enabled: true,
interval_secs: 60,
deleted_row_retention_secs: 300,
transaction_retention_secs: 3600,
}
}
}
impl CleanupConfig {
pub fn disabled() -> Self {
Self {
enabled: false,
..Default::default()
}
}
pub fn with_interval_secs(mut self, secs: u64) -> Self {
self.interval_secs = secs;
self
}
pub fn with_deleted_row_retention_secs(mut self, secs: u64) -> Self {
self.deleted_row_retention_secs = secs;
self
}
pub fn with_transaction_retention_secs(mut self, secs: u64) -> Self {
self.transaction_retention_secs = secs;
self
}
}
#[derive(Debug, Clone, Default)]
pub struct Config {
pub path: Option<String>,
pub persistence: PersistenceConfig,
pub cleanup: CleanupConfig,
}
impl Config {
pub fn in_memory() -> Self {
Self {
path: None,
persistence: PersistenceConfig {
enabled: false,
..Default::default()
},
cleanup: CleanupConfig::default(),
}
}
pub fn with_path<P: Into<String>>(path: P) -> Self {
Self {
path: Some(path.into()),
persistence: PersistenceConfig::default(),
cleanup: CleanupConfig::default(),
}
}
pub fn is_persistent(&self) -> bool {
self.path.is_some() && self.persistence.enabled
}
pub fn with_persistence(mut self, config: PersistenceConfig) -> Self {
self.persistence = config;
self
}
pub fn with_cleanup(mut self, config: CleanupConfig) -> Self {
self.cleanup = config;
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sync_mode_default() {
assert_eq!(SyncMode::default(), SyncMode::Normal);
}
#[test]
fn test_sync_mode_from_i32() {
assert_eq!(SyncMode::from(0), SyncMode::None);
assert_eq!(SyncMode::from(1), SyncMode::Normal);
assert_eq!(SyncMode::from(2), SyncMode::Full);
assert_eq!(SyncMode::from(99), SyncMode::Normal); }
#[test]
fn test_persistence_config_default() {
let config = PersistenceConfig::default();
assert!(config.enabled);
assert_eq!(config.sync_mode, SyncMode::Normal);
assert_eq!(config.checkpoint_interval, 60);
assert_eq!(config.compact_threshold, 4);
assert_eq!(config.wal_flush_trigger, 32 * 1024);
assert_eq!(config.wal_buffer_size, 64 * 1024);
assert_eq!(config.wal_max_size, 64 * 1024 * 1024);
assert_eq!(config.commit_batch_size, 100);
assert_eq!(config.sync_interval_ms, 1000);
assert!(config.wal_compression);
assert_eq!(config.compression_threshold, 64);
assert_eq!(config.keep_snapshots, 3);
}
#[test]
fn test_persistence_config_durable() {
let config = PersistenceConfig::durable();
assert_eq!(config.sync_mode, SyncMode::Full);
assert_eq!(config.commit_batch_size, 1);
assert_eq!(config.sync_interval_ms, 0);
}
#[test]
fn test_persistence_config_fast() {
let config = PersistenceConfig::fast();
assert_eq!(config.sync_mode, SyncMode::None);
assert_eq!(config.commit_batch_size, 500);
}
#[test]
fn test_persistence_config_builder() {
let config = PersistenceConfig::new()
.with_sync_mode(SyncMode::Full)
.with_checkpoint_interval(120)
.with_compact_threshold(8);
assert_eq!(config.sync_mode, SyncMode::Full);
assert_eq!(config.checkpoint_interval, 120);
assert_eq!(config.compact_threshold, 8);
}
#[test]
fn test_persistence_config_compression() {
let config = PersistenceConfig::new().with_compression(false);
assert!(!config.wal_compression);
assert!(!config.volume_compression);
let config = PersistenceConfig::new().with_wal_compression(false);
assert!(!config.wal_compression);
assert!(config.volume_compression);
let config = PersistenceConfig::new().with_volume_compression(false);
assert!(config.wal_compression); assert!(!config.volume_compression);
let config = PersistenceConfig::new().with_compression_threshold(128);
assert_eq!(config.compression_threshold, 128);
}
#[test]
fn test_config_in_memory() {
let config = Config::in_memory();
assert!(config.path.is_none());
assert!(!config.persistence.enabled);
assert!(!config.is_persistent());
}
#[test]
fn test_config_with_path() {
let config = Config::with_path("/tmp/test.db");
assert_eq!(config.path, Some("/tmp/test.db".to_string()));
assert!(config.persistence.enabled);
assert!(config.is_persistent());
}
#[test]
fn test_config_builder() {
let config =
Config::with_path("/tmp/test.db").with_persistence(PersistenceConfig::durable());
assert!(config.is_persistent());
assert_eq!(config.persistence.sync_mode, SyncMode::Full);
}
}