//! RocksDB store management use crate::cf; use chainfire_types::error::StorageError; use rocksdb::{BoundColumnFamily, ColumnFamilyDescriptor, Options, DB}; use std::path::Path; use std::sync::Arc; /// RocksDB store wrapper with column families pub struct RocksStore { db: Arc, } impl RocksStore { /// Open or create a RocksDB database at the given path pub fn new(path: impl AsRef) -> Result { let path = path.as_ref(); let mut db_opts = Options::default(); db_opts.create_if_missing(true); db_opts.create_missing_column_families(true); db_opts.set_max_background_jobs(4); db_opts.set_bytes_per_sync(1024 * 1024); // 1MB // Define column families let cf_descriptors = vec![ ColumnFamilyDescriptor::new(cf::LOGS, Self::logs_cf_options()), ColumnFamilyDescriptor::new(cf::META, Self::meta_cf_options()), ColumnFamilyDescriptor::new(cf::KV, Self::kv_cf_options()), ColumnFamilyDescriptor::new(cf::SNAPSHOT, Self::snapshot_cf_options()), ]; let db = DB::open_cf_descriptors(&db_opts, path, cf_descriptors) .map_err(|e| StorageError::RocksDb(e.to_string()))?; Ok(Self { db: Arc::new(db) }) } /// Get the underlying DB handle pub fn db(&self) -> &Arc { &self.db } /// Get a column family handle pub fn cf_handle(&self, name: &str) -> Option>> { self.db.cf_handle(name) } /// Options for the logs column family fn logs_cf_options() -> Options { let mut opts = Options::default(); // Optimize for sequential reads/writes opts.set_write_buffer_size(64 * 1024 * 1024); // 64MB opts.set_max_write_buffer_number(3); opts } /// Options for the metadata column family fn meta_cf_options() -> Options { let mut opts = Options::default(); // Small, frequently updated opts.set_write_buffer_size(16 * 1024 * 1024); // 16MB opts } /// Options for the KV column family fn kv_cf_options() -> Options { let mut opts = Options::default(); // Optimize for point lookups and range scans opts.set_write_buffer_size(128 * 1024 * 1024); // 128MB opts.set_max_write_buffer_number(4); // Enable bloom filters for faster lookups opts.set_prefix_extractor(rocksdb::SliceTransform::create_fixed_prefix(8)); opts } /// Options for the snapshot column family fn snapshot_cf_options() -> Options { let mut opts = Options::default(); opts.set_write_buffer_size(32 * 1024 * 1024); // 32MB opts } } impl Clone for RocksStore { fn clone(&self) -> Self { Self { db: Arc::clone(&self.db), } } } #[cfg(test)] mod tests { use super::*; use tempfile::tempdir; #[test] fn test_create_store() { let dir = tempdir().unwrap(); let store = RocksStore::new(dir.path()).unwrap(); // Verify all column families exist assert!(store.cf_handle(cf::LOGS).is_some()); assert!(store.cf_handle(cf::META).is_some()); assert!(store.cf_handle(cf::KV).is_some()); assert!(store.cf_handle(cf::SNAPSHOT).is_some()); } #[test] fn test_reopen_store() { let dir = tempdir().unwrap(); // Create and close { let store = RocksStore::new(dir.path()).unwrap(); let cf = store.cf_handle(cf::META).unwrap(); store .db() .put_cf(&cf, b"test_key", b"test_value") .unwrap(); } // Reopen and verify data persisted { let store = RocksStore::new(dir.path()).unwrap(); let cf = store.cf_handle(cf::META).unwrap(); let value = store.db().get_cf(&cf, b"test_key").unwrap(); assert_eq!(value, Some(b"test_value".to_vec())); } } }