use amaters::core::storage::compression::CompressionType;
use amaters::core::storage::{
BlockCacheConfig, BloomFilterConfig, CompactionConfig, CompactionStrategy, LsmTree,
LsmTreeConfig, MemtableConfig, SSTableConfig,
};
use amaters::core::{CipherBlob, Key};
fn main() -> amaters::core::Result<()> {
println!("=== AmateRS Storage Engine Example ===\n");
let temp_dir = std::env::temp_dir().join("amaters_storage_engine_example");
let data_dir = temp_dir.join("data");
let wal_dir = temp_dir.join("wal");
if temp_dir.exists() {
std::fs::remove_dir_all(&temp_dir).ok();
}
println!("--- Custom Configuration ---");
let config = LsmTreeConfig {
data_dir: data_dir.clone(),
wal_dir: wal_dir.clone(),
memtable_config: MemtableConfig {
max_size_bytes: 32 * 1024, enable_wal: true,
},
sstable_config: SSTableConfig {
block_size: 4096,
compression_type: CompressionType::Lz4,
},
block_cache_config: BlockCacheConfig::default(),
compaction_config: CompactionConfig {
strategy: CompactionStrategy::LevelBased,
l0_threshold: 2, level_multiplier: 10,
..CompactionConfig::default()
},
value_log_config: None,
max_levels: 7,
l0_compaction_threshold: 2,
level_size_multiplier: 10,
};
println!(
" Memtable size: {} KB",
config.memtable_config.max_size_bytes / 1024
);
println!(
" SSTable block size: {} bytes",
config.sstable_config.block_size
);
println!(
" Compression: {:?}",
config.sstable_config.compression_type
);
println!(
" Compaction strategy: {:?}",
config.compaction_config.strategy
);
println!(
" L0 threshold: {}",
config.compaction_config.l0_threshold
);
println!(" Max levels: {}", config.max_levels);
println!();
let tree = LsmTree::with_config(config)?;
println!("LSM-Tree created at: {}\n", temp_dir.display());
println!("--- Bulk Write ---");
let num_entries = 500;
let value_size = 256;
for i in 0..num_entries {
let key = Key::from_str(&format!("record:{:06}", i));
let mut value_data = Vec::with_capacity(value_size);
for j in 0..value_size {
value_data.push(((i + j) % 256) as u8);
}
let value = CipherBlob::new(value_data);
tree.put(key, value)?;
if (i + 1) % 100 == 0 {
println!(" Written {}/{} entries", i + 1, num_entries);
}
}
println!();
println!("--- Statistics After Writes ---");
print_stats(&tree);
println!();
println!("--- Data Verification ---");
let mut verified = 0;
let mut missing = 0;
for i in 0..num_entries {
let key = Key::from_str(&format!("record:{:06}", i));
match tree.get(&key)? {
Some(value) => {
let expected_first = (i % 256) as u8;
if value.as_bytes().first() == Some(&expected_first) {
verified += 1;
}
}
None => {
missing += 1;
}
}
}
println!(
" Verified: {}/{} Missing: {}",
verified, num_entries, missing
);
println!();
println!("--- Range Scan ---");
let start = Key::from_str("record:000100");
let end = Key::from_str("record:000200");
let results = tree.range(&start, &end)?;
println!(
" Range [record:000100, record:000200): {} results",
results.len()
);
if let Some((first_key, first_value)) = results.first() {
println!(
" First: {} ({} bytes)",
String::from_utf8_lossy(first_key.as_bytes()),
first_value.len()
);
}
if let Some((last_key, last_value)) = results.last() {
println!(
" Last: {} ({} bytes)",
String::from_utf8_lossy(last_key.as_bytes()),
last_value.len()
);
}
println!();
println!("--- Flush & Close ---");
tree.flush()?;
println!(" All pending writes flushed to disk.");
print_stats(&tree);
tree.close()?;
println!(" LSM-Tree closed gracefully.");
println!();
println!("--- WAL Recovery ---");
let recovery_config = LsmTreeConfig {
data_dir: data_dir.clone(),
wal_dir: wal_dir.clone(),
memtable_config: MemtableConfig {
max_size_bytes: 32 * 1024,
enable_wal: true,
},
sstable_config: SSTableConfig {
block_size: 4096,
compression_type: CompressionType::Lz4,
},
block_cache_config: BlockCacheConfig::default(),
compaction_config: CompactionConfig::default(),
value_log_config: None,
max_levels: 7,
l0_compaction_threshold: 4,
level_size_multiplier: 10,
};
let recovered_tree = LsmTree::with_config(recovery_config)?;
println!(" LSM-Tree recovered from disk.");
let check_key = Key::from_str("record:000042");
match recovered_tree.get(&check_key)? {
Some(value) => {
println!(
" Verified record:000042 after recovery: {} bytes",
value.len()
);
}
None => {
println!(" record:000042 not found after recovery (may be in WAL only)");
}
}
print_stats(&recovered_tree);
recovered_tree.close()?;
println!(" Recovery complete.\n");
println!("--- Bloom Filter Config ---");
let bloom_config = BloomFilterConfig {
expected_elements: 100_000,
false_positive_rate: 0.001, };
println!(" Expected elements: {}", bloom_config.expected_elements);
println!(
" False positive rate: {}%",
bloom_config.false_positive_rate * 100.0
);
println!();
std::fs::remove_dir_all(&temp_dir).ok();
println!("Cleanup complete. Example finished.");
Ok(())
}
fn print_stats(tree: &LsmTree) {
let stats = tree.stats();
println!(" Statistics:");
println!(" Memtable size: {} bytes", stats.memtable_size);
println!(" Number of levels: {}", stats.num_levels);
println!(
" Cache hit rate: {:.2}%",
stats.cache_hit_rate * 100.0
);
println!(" Cache size: {} bytes", stats.cache_size);
for level in &stats.levels {
if !level.sstables.is_empty() {
println!(
" Level {}: {} SSTables, {} bytes total",
level.level,
level.sstables.len(),
level.total_size
);
}
}
let cs = &stats.compaction_stats;
if cs.compactions_completed > 0 {
println!(
" Compaction: {} completed, {} bytes read, {} bytes written",
cs.compactions_completed, cs.bytes_read, cs.bytes_written
);
}
}