diff --git a/CHANGELOG.md b/CHANGELOG.md index cefc210..8eb77a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -361,9 +361,9 @@ BLOCK_CACHE_SIZE_MB=256 **Basic Usage**: ```rust -use lsm_kv_store::storage::builder::SstableBuilder; -use lsm_kv_store::infra::config::StorageConfig; -use lsm_kv_store::core::log_record::LogRecord; +use apexstore::storage::builder::SstableBuilder; +use apexstore::infra::config::StorageConfig; +use apexstore::core::log_record::LogRecord; // Create builder let config = StorageConfig::default(); diff --git a/Cargo.lock b/Cargo.lock index 5658660..41a2280 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -248,6 +248,30 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" +[[package]] +name = "apexstore" +version = "2.1.0" +dependencies = [ + "actix-cors", + "actix-web", + "bincode", + "bloomfilter", + "crc32fast", + "criterion", + "dotenvy", + "lru", + "lz4_flex", + "rand 0.8.5", + "serde", + "serde_derive", + "serde_json", + "tempfile", + "thiserror", + "tokio", + "tracing", + "tracing-subscriber", +] + [[package]] name = "autocfg" version = "1.5.0" @@ -1037,30 +1061,6 @@ dependencies = [ "hashbrown 0.15.5", ] -[[package]] -name = "lsm-kv-store" -version = "2.1.0" -dependencies = [ - "actix-cors", - "actix-web", - "bincode", - "bloomfilter", - "crc32fast", - "criterion", - "dotenvy", - "lru", - "lz4_flex", - "rand 0.8.5", - "serde", - "serde_derive", - "serde_json", - "tempfile", - "thiserror", - "tokio", - "tracing", - "tracing-subscriber", -] - [[package]] name = "lz4_flex" version = "0.11.5" diff --git a/docs/RELEASE_NOTES_v1.3.0.md b/docs/RELEASE_NOTES_v1.3.0.md index f7fbed0..13f60ce 100644 --- a/docs/RELEASE_NOTES_v1.3.0.md +++ b/docs/RELEASE_NOTES_v1.3.0.md @@ -77,7 +77,7 @@ pub struct MetaBlock { **Builder API Usage**: ```rust -use lsm_kv_store::storage::builder::SstableBuilder; +use apexstore::storage::builder::SstableBuilder; // Create builder with automatic block management let mut builder = SstableBuilder::new( diff --git a/docs/SETUP.md b/docs/SETUP.md index 2141f54..75da6a4 100644 --- a/docs/SETUP.md +++ b/docs/SETUP.md @@ -367,7 +367,7 @@ export RUST_LOG=debug RUST_LOG=trace cargo run # Filter by module -RUST_LOG=lsm_kv_store::core::engine=debug cargo run +RUST_LOG=apexstore::core::engine=debug cargo run ``` --- diff --git a/docs/implementation-plans/IMPLEMENTATION_PLAN_31_36.md b/docs/implementation-plans/IMPLEMENTATION_PLAN_31_36.md index 43c9c4d..d4303c8 100644 --- a/docs/implementation-plans/IMPLEMENTATION_PLAN_31_36.md +++ b/docs/implementation-plans/IMPLEMENTATION_PLAN_31_36.md @@ -835,8 +835,8 @@ pub async fn start_server( ```rust use actix_web::{test, App}; -use lsm_kv_store::api::auth::TokenManager; -use lsm_kv_store::api::server::configure_routes; +use apexstore::api::auth::TokenManager; +use apexstore::api::server::configure_routes; use std::sync::Arc; #[actix_web::test] @@ -882,7 +882,7 @@ async fn test_valid_token_access() { let (token, _) = token_manager .create( "test-token".to_string(), - vec![lsm_kv_store::api::auth::token::Permission::Write], + vec![apexstore::api::auth::token::Permission::Write], None, ) .unwrap(); diff --git a/src/bin/server.rs b/src/bin/server.rs index 6ae834c..2c7e35b 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -1,4 +1,4 @@ -use lsm_kv_store::{LsmConfig, LsmEngine}; +use apexstore::{LsmConfig, LsmEngine}; use std::env; use std::io; use std::path::PathBuf; @@ -21,7 +21,7 @@ async fn main() -> std::io::Result<()> { println!("╚═══════════════════════════════════════════════════════╝\n"); // Load server configuration from environment - let server_config = lsm_kv_store::api::ServerConfig::from_env(); + let server_config = apexstore::api::ServerConfig::from_env(); // Load LSM engine configuration from environment let data_dir = env::var("DATA_DIR").unwrap_or_else(|_| "./.lsm_data".to_string()); @@ -67,7 +67,10 @@ async fn main() -> std::io::Result<()> { Ok(abs_path) => println!(" Data Directory: {}", abs_path.display()), Err(_) => println!(" Data Directory: {} (will be created)", data_dir), } - println!(" MemTable Max Size: {} MB", memtable_max_size / 1024 / 1024); + println!( + " MemTable Max Size: {} MB", + memtable_max_size / 1024 / 1024 + ); println!(" Block Size: {} bytes", block_size); println!(" Block Cache: {} MB", block_cache_size_mb); println!(" Sparse Index Interval: {}", sparse_index_interval); @@ -78,12 +81,14 @@ async fn main() -> std::io::Result<()> { Ok(engine) => engine, Err(e) => { eprintln!("❌ Error initializing LSM Engine: {}", e); - eprintln!("💡 Tip: if you don't need to recover unflushed writes, rename/delete wal.log and try again."); + eprintln!( + "💡 Tip: if you don't need to recover unflushed writes, rename/delete wal.log and try again." + ); return Err(io::Error::new(io::ErrorKind::InvalidData, e.to_string())); } }; println!("✓ Engine initialized successfully!\n"); - lsm_kv_store::api::start_server(engine, server_config).await + apexstore::api::start_server(engine, server_config).await } diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 3714c10..b9616ee 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -1,4 +1,4 @@ -use lsm_kv_store::{LsmConfig, LsmEngine}; +use apexstore::{LsmConfig, LsmEngine}; use std::io::{self, Write}; use std::path::PathBuf; diff --git a/src/core/memtable.rs b/src/core/memtable.rs index 78f0d38..7ab9026 100644 --- a/src/core/memtable.rs +++ b/src/core/memtable.rs @@ -1,4 +1,5 @@ use crate::core::log_record::LogRecord; +use crate::storage::iterator::MemTableIterator; use std::collections::BTreeMap; pub struct MemTable { @@ -34,10 +35,44 @@ impl MemTable { self.data.get(key).cloned() } + /// Returns a StorageIterator over all entries (backward compatible) pub fn iter_ordered(&self) -> impl Iterator { self.data.iter() } + /// Returns a MemTableIterator starting from the beginning + /// + /// This is the preferred method for using the StorageIterator trait. + /// + /// # Example + /// ```ignore + /// let mut iter = memtable.iter(); + /// while iter.is_valid() { + /// println!("{}={:?}", String::from_utf8_lossy(iter.key()), iter.value()); + /// iter.next(); + /// } + /// ``` + pub fn iter(&self) -> MemTableIterator<'_> { + MemTableIterator::new(&self.data) + } + + /// Returns a MemTableIterator starting from a specific key + /// + /// # Arguments + /// * `start_key` - The key to start iteration from (inclusive) + /// + /// # Example + /// ```ignore + /// let mut iter = memtable.iter_from("key_100"); + /// while iter.is_valid() { + /// // Iterate from key_100 onwards + /// iter.next(); + /// } + /// ``` + pub fn iter_from(&self, start_key: &str) -> MemTableIterator<'_> { + MemTableIterator::new_from(&self.data, start_key) + } + pub fn clear(&mut self) -> usize { let count = self.data.len(); self.data.clear(); @@ -49,3 +84,46 @@ impl MemTable { record.key.len() + record.value.len() + 32 } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::storage::iterator::StorageIterator; + + #[test] + fn test_memtable_iter() { + let mut memtable = MemTable::new(1024); + memtable.insert(LogRecord::new("key1".to_string(), b"value1".to_vec())); + memtable.insert(LogRecord::new("key2".to_string(), b"value2".to_vec())); + memtable.insert(LogRecord::new("key3".to_string(), b"value3".to_vec())); + + let mut iter = memtable.iter(); + let mut count = 0; + + while iter.is_valid() { + count += 1; + iter.next(); + } + + assert_eq!(count, 3); + } + + #[test] + fn test_memtable_iter_from() { + let mut memtable = MemTable::new(1024); + memtable.insert(LogRecord::new("key1".to_string(), b"value1".to_vec())); + memtable.insert(LogRecord::new("key2".to_string(), b"value2".to_vec())); + memtable.insert(LogRecord::new("key3".to_string(), b"value3".to_vec())); + + let mut iter = memtable.iter_from("key2"); + assert!(iter.is_valid()); + assert_eq!(iter.key(), b"key2"); + + iter.next(); + assert!(iter.is_valid()); + assert_eq!(iter.key(), b"key3"); + + iter.next(); + assert!(!iter.is_valid()); + } +} diff --git a/src/lib.rs b/src/lib.rs index e9f73cd..1c57ec1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,19 +45,19 @@ //! # fn main() -> Result<(), Box> { //! // Create engine with default configuration //! let config = LsmConfig::default(); -//! let mut engine = LsmEngine::open(config)?; +//! let engine = LsmEngine::new(config)?; //! //! // Write data -//! engine.put("user:1", b"Alice")?; -//! engine.put("user:2", b"Bob")?; +//! engine.set("user:1".to_string(), b"Alice".to_vec())?; +//! engine.set("user:2".to_string(), b"Bob".to_vec())?; //! //! // Read data -//! if let Some(record) = engine.get("user:1")? { -//! println!("Value: {:?}", record.value); +//! if let Some(value) = engine.get("user:1")? { +//! println!("Value: {:?}", value); //! } //! //! // Delete data -//! engine.delete("user:1")?; +//! engine.delete("user:1".to_string())?; //! # Ok(()) //! # } //! ``` diff --git a/src/main.rs b/src/main.rs index 54426da..85947b2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,8 @@ -use lsm_kv_store::{LsmConfig, LsmEngine}; +use apexstore::{LsmConfig, LsmEngine}; fn main() -> Result<(), Box> { let config = LsmConfig::builder() - .dir_path("/var/lib/lsm_kv_store/data") + .dir_path("/var/lib/apexstore/data") .build()?; let _engine = LsmEngine::new(config)?; diff --git a/src/storage/iterator.rs b/src/storage/iterator.rs new file mode 100644 index 0000000..bfe6499 --- /dev/null +++ b/src/storage/iterator.rs @@ -0,0 +1,349 @@ +//! Storage Iterator Abstraction +//! +//! This module provides a unified iterator interface (`StorageIterator`) that abstracts +//! iteration over different storage layers (MemTable, SSTable, etc.). +//! +//! The trait enables: +//! - Range queries across storage layers +//! - Merge operations during compaction +//! - Prefix scans and filtered iterations + +use crate::core::log_record::LogRecord; +use std::collections::btree_map; + +/// Unified iterator interface for storage layers +/// +/// This trait provides a common abstraction for iterating over key-value pairs +/// stored in different layers of the LSM tree (MemTable, SSTables). +/// +/// # Example +/// +/// ```ignore +/// let mut iter = memtable.iter(); +/// iter.seek(b"key_100"); +/// +/// while iter.is_valid() { +/// let key = iter.key(); +/// let value = iter.value(); +/// println!("{}={:?}", String::from_utf8_lossy(key), value); +/// iter.next(); +/// } +/// ``` +pub trait StorageIterator { + /// Returns the current key as a byte slice + /// + /// # Panics + /// May panic if called when `is_valid()` returns `false` + fn key(&self) -> &[u8]; + + /// Returns the current value (LogRecord) + /// + /// # Panics + /// May panic if called when `is_valid()` returns `false` + fn value(&self) -> &LogRecord; + + /// Returns `true` if the iterator is pointing to valid data + /// + /// Must be checked before calling `key()` or `value()` + fn is_valid(&self) -> bool; + + /// Advances the iterator to the next position + /// + /// After calling `next()`, you must check `is_valid()` again + fn next(&mut self); + + /// Positions the iterator at the first key >= `key` + /// + /// If no such key exists, the iterator becomes invalid. + /// + /// # Arguments + /// * `key` - The target key to seek to + fn seek(&mut self, key: &[u8]); +} + +/// Iterator over MemTable entries +/// +/// Wraps a `BTreeMap::Range` iterator to provide the `StorageIterator` interface. +/// Keys are automatically sorted by the BTreeMap. +pub struct MemTableIterator<'a> { + inner: btree_map::Range<'a, String, LogRecord>, + current: Option<(&'a String, &'a LogRecord)>, +} + +impl<'a> MemTableIterator<'a> { + /// Creates a new iterator starting from the beginning of the MemTable + /// + /// # Arguments + /// * `data` - Reference to the BTreeMap backing the MemTable + pub fn new(data: &'a btree_map::BTreeMap) -> Self { + let mut inner = data.range::(..); // Full range + let current = inner.next(); + Self { inner, current } + } + + /// Creates a new iterator starting from a specific key + /// + /// # Arguments + /// * `data` - Reference to the BTreeMap backing the MemTable + /// * `start_key` - The key to start iteration from (inclusive) + pub fn new_from(data: &'a btree_map::BTreeMap, start_key: &str) -> Self { + let mut inner = data.range::(start_key.to_string()..); // Range from start_key to end + let current = inner.next(); + Self { inner, current } + } +} + +impl<'a> StorageIterator for MemTableIterator<'a> { + fn key(&self) -> &[u8] { + self.current + .expect("key() called on invalid iterator") + .0 + .as_bytes() + } + + fn value(&self) -> &LogRecord { + self.current + .expect("value() called on invalid iterator") + .1 + } + + fn is_valid(&self) -> bool { + self.current.is_some() + } + + fn next(&mut self) { + self.current = self.inner.next(); + } + + fn seek(&mut self, key: &[u8]) { + // We need to iterate until we find a key >= seek target + while let Some((current_key, _)) = self.current { + if current_key.as_bytes() >= key { + // Found a key >= seek target + return; + } + self.current = self.inner.next(); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::collections::BTreeMap; + + fn create_test_record(key: &str, value: &[u8]) -> LogRecord { + LogRecord::new(key.to_string(), value.to_vec()) + } + + fn create_test_memtable() -> BTreeMap { + let mut map = BTreeMap::new(); + map.insert( + "key_001".to_string(), + create_test_record("key_001", b"value_001"), + ); + map.insert( + "key_010".to_string(), + create_test_record("key_010", b"value_010"), + ); + map.insert( + "key_020".to_string(), + create_test_record("key_020", b"value_020"), + ); + map.insert( + "key_030".to_string(), + create_test_record("key_030", b"value_030"), + ); + map.insert( + "key_100".to_string(), + create_test_record("key_100", b"value_100"), + ); + map + } + + #[test] + fn test_iterator_basic() { + let map = create_test_memtable(); + let mut iter = MemTableIterator::new(&map); + + // First key + assert!(iter.is_valid()); + assert_eq!(iter.key(), b"key_001"); + assert_eq!(iter.value().value, b"value_001"); + + // Second key + iter.next(); + assert!(iter.is_valid()); + assert_eq!(iter.key(), b"key_010"); + assert_eq!(iter.value().value, b"value_010"); + + // Third key + iter.next(); + assert!(iter.is_valid()); + assert_eq!(iter.key(), b"key_020"); + } + + #[test] + fn test_iterator_full_scan() { + let map = create_test_memtable(); + let mut iter = MemTableIterator::new(&map); + + let mut count = 0; + let expected_keys = ["key_001", "key_010", "key_020", "key_030", "key_100"]; + + while iter.is_valid() { + let key = String::from_utf8(iter.key().to_vec()).unwrap(); + assert_eq!(key, expected_keys[count]); + count += 1; + iter.next(); + } + + assert_eq!(count, 5, "Should iterate over all 5 keys"); + } + + #[test] + fn test_iterator_seek_exact() { + let map = create_test_memtable(); + let mut iter = MemTableIterator::new(&map); + + // Seek to exact key + iter.seek(b"key_020"); + assert!(iter.is_valid()); + assert_eq!(iter.key(), b"key_020"); + assert_eq!(iter.value().value, b"value_020"); + + // Continue iterating + iter.next(); + assert!(iter.is_valid()); + assert_eq!(iter.key(), b"key_030"); + } + + #[test] + fn test_iterator_seek_between() { + let map = create_test_memtable(); + let mut iter = MemTableIterator::new(&map); + + // Seek to key between existing keys (should find next key) + iter.seek(b"key_015"); + assert!(iter.is_valid()); + assert_eq!(iter.key(), b"key_020"); // Next key after key_015 + } + + #[test] + fn test_iterator_seek_before_first() { + let map = create_test_memtable(); + let mut iter = MemTableIterator::new(&map); + + // Seek before first key + iter.seek(b"key_000"); + assert!(iter.is_valid()); + assert_eq!(iter.key(), b"key_001"); // First key + } + + #[test] + fn test_iterator_seek_after_last() { + let map = create_test_memtable(); + let mut iter = MemTableIterator::new(&map); + + // Seek after last key + iter.seek(b"key_999"); + assert!(!iter.is_valid()); // No keys >= key_999 + } + + #[test] + fn test_iterator_seek_last_key() { + let map = create_test_memtable(); + let mut iter = MemTableIterator::new(&map); + + // Seek to last key + iter.seek(b"key_100"); + assert!(iter.is_valid()); + assert_eq!(iter.key(), b"key_100"); + + // Next should be invalid + iter.next(); + assert!(!iter.is_valid()); + } + + #[test] + fn test_iterator_empty_memtable() { + let map = BTreeMap::new(); + let iter = MemTableIterator::new(&map); + + // Should be invalid from the start + assert!(!iter.is_valid()); + } + + #[test] + fn test_iterator_single_entry() { + let mut map = BTreeMap::new(); + map.insert( + "only_key".to_string(), + create_test_record("only_key", b"only_value"), + ); + + let mut iter = MemTableIterator::new(&map); + + assert!(iter.is_valid()); + assert_eq!(iter.key(), b"only_key"); + + iter.next(); + assert!(!iter.is_valid()); + } + + #[test] + fn test_iterator_new_from() { + let map = create_test_memtable(); + + // Start from key_020 + let mut iter = MemTableIterator::new_from(&map, "key_020"); + + assert!(iter.is_valid()); + assert_eq!(iter.key(), b"key_020"); + + iter.next(); + assert!(iter.is_valid()); + assert_eq!(iter.key(), b"key_030"); + + iter.next(); + assert!(iter.is_valid()); + assert_eq!(iter.key(), b"key_100"); + + iter.next(); + assert!(!iter.is_valid()); + } + + #[test] + fn test_iterator_deleted_records() { + let mut map = BTreeMap::new(); + map.insert( + "key_001".to_string(), + create_test_record("key_001", b"value_001"), + ); + map.insert( + "key_002".to_string(), + LogRecord::tombstone("key_002".to_string()), + ); + map.insert( + "key_003".to_string(), + create_test_record("key_003", b"value_003"), + ); + + let mut iter = MemTableIterator::new(&map); + + // Should iterate over all entries, including tombstones + assert!(iter.is_valid()); + assert_eq!(iter.key(), b"key_001"); + assert!(!iter.value().is_deleted); + + iter.next(); + assert!(iter.is_valid()); + assert_eq!(iter.key(), b"key_002"); + assert!(iter.value().is_deleted); // Tombstone + + iter.next(); + assert!(iter.is_valid()); + assert_eq!(iter.key(), b"key_003"); + assert!(!iter.value().is_deleted); + } +} diff --git a/src/storage/mod.rs b/src/storage/mod.rs index c22a2e0..5ca4dbb 100644 --- a/src/storage/mod.rs +++ b/src/storage/mod.rs @@ -2,5 +2,6 @@ pub mod block; pub mod builder; pub mod cache; pub mod config; +pub mod iterator; pub mod reader; pub mod wal; diff --git a/tests/integration_sstable_v2.rs b/tests/integration_sstable_v2.rs index a96804e..97fde74 100644 --- a/tests/integration_sstable_v2.rs +++ b/tests/integration_sstable_v2.rs @@ -1,9 +1,9 @@ -use lsm_kv_store::core::log_record::LogRecord; -use lsm_kv_store::infra::config::StorageConfig; -use lsm_kv_store::infra::error::Result; -use lsm_kv_store::storage::builder::SstableBuilder; -use lsm_kv_store::storage::cache::GlobalBlockCache; -use lsm_kv_store::storage::reader::SstableReader; +use apexstore::core::log_record::LogRecord; +use apexstore::infra::config::StorageConfig; +use apexstore::infra::error::Result; +use apexstore::storage::builder::SstableBuilder; +use apexstore::storage::cache::GlobalBlockCache; +use apexstore::storage::reader::SstableReader; use std::sync::Arc; use tempfile::tempdir; @@ -97,7 +97,10 @@ fn test_sstable_v2_multiple_blocks() -> Result<()> { let mut reader = SstableReader::open(path, config, cache)?; // Verify metadata shows multiple blocks - assert!(reader.metadata().blocks.len() > 1, "Should have multiple blocks"); + assert!( + reader.metadata().blocks.len() > 1, + "Should have multiple blocks" + ); // Verify all records are readable for i in 0..100 { @@ -130,7 +133,10 @@ fn test_sstable_v2_bloom_filter_effectiveness() -> Result<()> { // All existing keys should pass Bloom filter for i in 0..500 { let key = format!("existing_key_{:04}", i); - assert!(reader.might_contain(&key), "Existing key should pass Bloom filter"); + assert!( + reader.might_contain(&key), + "Existing key should pass Bloom filter" + ); } // Count false positives for non-existent keys @@ -139,7 +145,11 @@ fn test_sstable_v2_bloom_filter_effectiveness() -> Result<()> { .count(); // With 1% FP rate and 500 checks, expect < 10 false positives - assert!(false_positives < 10, "Too many false positives: {}", false_positives); + assert!( + false_positives < 10, + "Too many false positives: {}", + false_positives + ); Ok(()) } @@ -165,14 +175,26 @@ fn test_sstable_v2_boundary_keys() -> Result<()> { assert!(reader.get("zzz")?.is_some(), "Last key should exist"); // Test keys before first - assert!(reader.get("000")?.is_none(), "Key before first should not exist"); - assert!(reader.get("aa")?.is_none(), "Key before first should not exist"); + assert!( + reader.get("000")?.is_none(), + "Key before first should not exist" + ); + assert!( + reader.get("aa")?.is_none(), + "Key before first should not exist" + ); // Test keys after last - assert!(reader.get("zzzz")?.is_none(), "Key after last should not exist"); + assert!( + reader.get("zzzz")?.is_none(), + "Key after last should not exist" + ); // Test keys between boundaries - assert!(reader.get("bbb")?.is_none(), "Non-existent key should not exist"); + assert!( + reader.get("bbb")?.is_none(), + "Non-existent key should not exist" + ); assert!(reader.get("mmm")?.is_some(), "Middle key should exist"); Ok(()) @@ -188,9 +210,12 @@ fn test_sstable_v2_scan() -> Result<()> { // Write ordered records let mut builder = SstableBuilder::new(path.clone(), config.clone(), 222)?; let test_keys = vec!["apple", "banana", "cherry", "date", "elderberry"]; - + for key in &test_keys { - builder.add(key.as_bytes(), &create_test_record(key, format!("{}_value", key).as_bytes()))?; + builder.add( + key.as_bytes(), + &create_test_record(key, format!("{}_value", key).as_bytes()), + )?; } builder.finish()?; @@ -202,7 +227,11 @@ fn test_sstable_v2_scan() -> Result<()> { // Verify order is preserved for (i, key) in test_keys.iter().enumerate() { - assert_eq!(records[i].0, key.as_bytes(), "Key order should be preserved"); + assert_eq!( + records[i].0, + key.as_bytes(), + "Key order should be preserved" + ); } Ok(()) @@ -282,7 +311,10 @@ fn test_sstable_v2_empty_key() -> Result<()> { // Write with empty string key let mut builder = SstableBuilder::new(path.clone(), config.clone(), 555)?; builder.add(b"", &create_test_record("", b"empty_key_value"))?; - builder.add(b"normal_key", &create_test_record("normal_key", b"normal_value"))?; + builder.add( + b"normal_key", + &create_test_record("normal_key", b"normal_value"), + )?; builder.finish()?; let mut reader = SstableReader::open(path, config, cache)?; @@ -312,7 +344,10 @@ fn test_sstable_v2_unicode_keys() -> Result<()> { unicode_keys.sort(); for key in &unicode_keys { - builder.add(key.as_bytes(), &create_test_record(key, format!("{}_value", key).as_bytes()))?; + builder.add( + key.as_bytes(), + &create_test_record(key, format!("{}_value", key).as_bytes()), + )?; } builder.finish()?; @@ -324,7 +359,12 @@ fn test_sstable_v2_unicode_keys() -> Result<()> { assert!(record.is_some(), "Unicode key '{}' should exist", key); if let Some(r) = record { let expected = format!("{}_value", key); - assert_eq!(r.value, expected.as_bytes(), "Value for '{}' should match", key); + assert_eq!( + r.value, + expected.as_bytes(), + "Value for '{}' should match", + key + ); } } diff --git a/tests/restart.rs b/tests/restart.rs index d458b6e..ef47822 100644 --- a/tests/restart.rs +++ b/tests/restart.rs @@ -1,4 +1,4 @@ -use lsm_kv_store::{LsmConfig, LsmEngine, LsmError}; +use apexstore::{LsmConfig, LsmEngine, LsmError}; use tempfile::tempdir; use std::fs::OpenOptions;