Skip to content

Commit d1c8beb

Browse files
author
Zorglub4242
committed
Add RocksDB preset system and WAL directory support for HDD optimization
Implements configurable RocksDB presets and WAL directory placement to optimize archive nodes on HDD storage, addressing Issue #681. Features: - Two RocksDB presets: 'default' (SSD/NVMe) and 'archive' (HDD) - Archive preset optimized for HDD with 256MB write buffer, BlobDB, aggressive compression, and 12 MB/s rate limiter - --rocksdb-preset CLI flag to select preset - --rocksdb-wal-dir CLI flag for hybrid NVMe+HDD setups - Auto-generated unique WAL subdirectories per database Archive preset configuration: - 256MB write buffer (4x default) for better batching on HDD - BlobDB with ZSTD compression for large values - Tiered compression: LZ4 for L0-L4, ZSTD level 22 for L5+ - 12 MB/s rate limiter to prevent I/O spikes - 2GB LRU block cache with partitioned Bloom filters - 4MB read-ahead for compactions Changes: - database/src/db/rocksdb_preset.rs: New preset system - database/src/db/conn_builder.rs: Add preset and WAL directory support - kaspad/src/args.rs: Add CLI flags - kaspad/src/daemon.rs: Apply preset to all databases - consensus/src/consensus/factory.rs: Pass preset to consensus DBs - database/src/db.rs, database/src/lib.rs: Export RocksDbPreset Related: #681
1 parent 4826b38 commit d1c8beb

File tree

8 files changed

+358
-12
lines changed

8 files changed

+358
-12
lines changed

consensus/src/consensus/factory.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ use kaspa_consensusmanager::{ConsensusFactory, ConsensusInstance, DynConsensusCt
99
use kaspa_core::{debug, time::unix_now, warn};
1010
use kaspa_database::{
1111
prelude::{
12-
BatchDbWriter, CachePolicy, CachedDbAccess, CachedDbItem, DirectDbWriter, StoreError, StoreResult, StoreResultExtensions, DB,
12+
BatchDbWriter, CachePolicy, CachedDbAccess, CachedDbItem, DirectDbWriter, RocksDbPreset, StoreError, StoreResult,
13+
StoreResultExtensions, DB,
1314
},
1415
registry::DatabaseStorePrefixes,
1516
};
@@ -255,6 +256,8 @@ pub struct Factory {
255256
tx_script_cache_counters: Arc<TxScriptCacheCounters>,
256257
fd_budget: i32,
257258
mining_rules: Arc<MiningRules>,
259+
rocksdb_preset: RocksDbPreset,
260+
wal_dir: Option<PathBuf>,
258261
}
259262

260263
impl Factory {
@@ -268,6 +271,8 @@ impl Factory {
268271
tx_script_cache_counters: Arc<TxScriptCacheCounters>,
269272
fd_budget: i32,
270273
mining_rules: Arc<MiningRules>,
274+
rocksdb_preset: RocksDbPreset,
275+
wal_dir: Option<PathBuf>,
271276
) -> Self {
272277
assert!(fd_budget > 0, "fd_budget has to be positive");
273278
let mut config = config.clone();
@@ -286,6 +291,8 @@ impl Factory {
286291
tx_script_cache_counters,
287292
fd_budget,
288293
mining_rules,
294+
rocksdb_preset,
295+
wal_dir,
289296
};
290297
factory.delete_inactive_consensus_entries();
291298
factory
@@ -316,6 +323,8 @@ impl ConsensusFactory for Factory {
316323
.with_db_path(dir)
317324
.with_parallelism(self.db_parallelism)
318325
.with_files_limit(self.fd_budget / 2) // active and staging consensuses should have equal budgets
326+
.with_preset(self.rocksdb_preset)
327+
.with_wal_dir(self.wal_dir.clone())
319328
.build()
320329
.unwrap();
321330

@@ -351,6 +360,8 @@ impl ConsensusFactory for Factory {
351360
.with_db_path(dir)
352361
.with_parallelism(self.db_parallelism)
353362
.with_files_limit(self.fd_budget / 2) // active and staging consensuses should have equal budgets
363+
.with_preset(self.rocksdb_preset)
364+
.with_wal_dir(self.wal_dir.clone())
354365
.build()
355366
.unwrap();
356367

database/src/db.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ use std::path::PathBuf;
44

55
pub use conn_builder::ConnBuilder;
66
use kaspa_utils::fd_budget::FDGuard;
7+
pub use rocksdb_preset::RocksDbPreset;
78

89
mod conn_builder;
10+
mod rocksdb_preset;
911

1012
/// The DB type used for Kaspad stores
1113
pub struct DB {

database/src/db/conn_builder.rs

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use super::rocksdb_preset::RocksDbPreset;
12
use crate::db::DB;
23
use rocksdb::{DBWithThreadMode, MultiThreaded};
34
use std::{path::PathBuf, sync::Arc};
@@ -13,6 +14,8 @@ pub struct ConnBuilder<Path, const STATS_ENABLED: bool, StatsPeriod, FDLimit> {
1314
files_limit: FDLimit,
1415
mem_budget: usize,
1516
stats_period: StatsPeriod,
17+
preset: RocksDbPreset,
18+
wal_dir: Option<PathBuf>,
1619
}
1720

1821
impl Default for ConnBuilder<Unspecified, false, Unspecified, Unspecified> {
@@ -24,6 +27,8 @@ impl Default for ConnBuilder<Unspecified, false, Unspecified, Unspecified> {
2427
mem_budget: 64 * 1024 * 1024,
2528
stats_period: Unspecified,
2629
files_limit: Unspecified,
30+
preset: RocksDbPreset::Default,
31+
wal_dir: None,
2732
}
2833
}
2934
}
@@ -37,6 +42,8 @@ impl<Path, const STATS_ENABLED: bool, StatsPeriod, FDLimit> ConnBuilder<Path, ST
3742
parallelism: self.parallelism,
3843
mem_budget: self.mem_budget,
3944
stats_period: self.stats_period,
45+
preset: self.preset,
46+
wal_dir: self.wal_dir,
4047
}
4148
}
4249
pub fn with_create_if_missing(self, create_if_missing: bool) -> ConnBuilder<Path, STATS_ENABLED, StatsPeriod, FDLimit> {
@@ -56,8 +63,16 @@ impl<Path, const STATS_ENABLED: bool, StatsPeriod, FDLimit> ConnBuilder<Path, ST
5663
parallelism: self.parallelism,
5764
mem_budget: self.mem_budget,
5865
stats_period: self.stats_period,
66+
preset: self.preset,
67+
wal_dir: self.wal_dir,
5968
}
6069
}
70+
pub fn with_preset(self, preset: RocksDbPreset) -> ConnBuilder<Path, STATS_ENABLED, StatsPeriod, FDLimit> {
71+
ConnBuilder { preset, ..self }
72+
}
73+
pub fn with_wal_dir(self, wal_dir: Option<PathBuf>) -> ConnBuilder<Path, STATS_ENABLED, StatsPeriod, FDLimit> {
74+
ConnBuilder { wal_dir, ..self }
75+
}
6176
}
6277

6378
impl<Path, FDLimit> ConnBuilder<Path, false, Unspecified, FDLimit> {
@@ -69,6 +84,8 @@ impl<Path, FDLimit> ConnBuilder<Path, false, Unspecified, FDLimit> {
6984
files_limit: self.files_limit,
7085
mem_budget: self.mem_budget,
7186
stats_period: self.stats_period,
87+
preset: self.preset,
88+
wal_dir: self.wal_dir,
7289
}
7390
}
7491
}
@@ -82,6 +99,8 @@ impl<Path, StatsPeriod, FDLimit> ConnBuilder<Path, true, StatsPeriod, FDLimit> {
8299
files_limit: self.files_limit,
83100
mem_budget: self.mem_budget,
84101
stats_period: Unspecified,
102+
preset: self.preset,
103+
wal_dir: self.wal_dir,
85104
}
86105
}
87106
pub fn with_stats_period(self, stats_period: impl Into<u32>) -> ConnBuilder<Path, true, u32, FDLimit> {
@@ -92,18 +111,39 @@ impl<Path, StatsPeriod, FDLimit> ConnBuilder<Path, true, StatsPeriod, FDLimit> {
92111
files_limit: self.files_limit,
93112
mem_budget: self.mem_budget,
94113
stats_period: stats_period.into(),
114+
preset: self.preset,
115+
wal_dir: self.wal_dir,
95116
}
96117
}
97118
}
98119

99120
macro_rules! default_opts {
100121
($self: expr) => {{
101122
let mut opts = rocksdb::Options::default();
102-
if $self.parallelism > 1 {
103-
opts.increase_parallelism($self.parallelism as i32);
123+
124+
// Apply the preset configuration (includes parallelism and compaction settings)
125+
$self.preset.apply_to_options(&mut opts, $self.parallelism, $self.mem_budget);
126+
127+
// Configure WAL directory if specified (for RAM cache / tmpfs)
128+
// Auto-generate unique subdirectory from database path to avoid conflicts
129+
if let Some(ref wal_base) = $self.wal_dir {
130+
let db_name = $self
131+
.db_path
132+
.file_name()
133+
.and_then(|n| n.to_str())
134+
.expect(&format!("Invalid database path: {}", $self.db_path.display()));
135+
let wal_subdir = wal_base.join(db_name);
136+
137+
// Create subdirectory if needed (each DB gets its own WAL space)
138+
std::fs::create_dir_all(&wal_subdir).expect(&format!(
139+
"Failed to create WAL subdirectory {}: {}",
140+
wal_subdir.display(),
141+
"error"
142+
));
143+
144+
opts.set_wal_dir(&wal_subdir);
104145
}
105146

106-
opts.optimize_level_style_compaction($self.mem_budget);
107147
let guard = kaspa_utils::fd_budget::acquire_guard($self.files_limit)?;
108148
opts.set_max_open_files($self.files_limit);
109149
opts.create_if_missing($self.create_if_missing);
@@ -114,17 +154,19 @@ macro_rules! default_opts {
114154
impl ConnBuilder<PathBuf, false, Unspecified, i32> {
115155
pub fn build(self) -> Result<Arc<DB>, kaspa_utils::fd_budget::Error> {
116156
let (opts, guard) = default_opts!(self)?;
117-
let db = Arc::new(DB::new(<DBWithThreadMode<MultiThreaded>>::open(&opts, self.db_path.to_str().unwrap()).unwrap(), guard));
118-
Ok(db)
157+
let db_path_str = self.db_path.to_str().expect(&format!("Invalid UTF-8 in database path: {}", self.db_path.display()));
158+
let db = <DBWithThreadMode<MultiThreaded>>::open(&opts, db_path_str).expect("Failed to open database");
159+
Ok(Arc::new(DB::new(db, guard)))
119160
}
120161
}
121162

122163
impl ConnBuilder<PathBuf, true, Unspecified, i32> {
123164
pub fn build(self) -> Result<Arc<DB>, kaspa_utils::fd_budget::Error> {
124165
let (mut opts, guard) = default_opts!(self)?;
125166
opts.enable_statistics();
126-
let db = Arc::new(DB::new(<DBWithThreadMode<MultiThreaded>>::open(&opts, self.db_path.to_str().unwrap()).unwrap(), guard));
127-
Ok(db)
167+
let db_path_str = self.db_path.to_str().expect(&format!("Invalid UTF-8 in database path: {}", self.db_path.display()));
168+
let db = <DBWithThreadMode<MultiThreaded>>::open(&opts, db_path_str).expect("Failed to open database");
169+
Ok(Arc::new(DB::new(db, guard)))
128170
}
129171
}
130172

@@ -134,7 +176,8 @@ impl ConnBuilder<PathBuf, true, u32, i32> {
134176
opts.enable_statistics();
135177
opts.set_report_bg_io_stats(true);
136178
opts.set_stats_dump_period_sec(self.stats_period);
137-
let db = Arc::new(DB::new(<DBWithThreadMode<MultiThreaded>>::open(&opts, self.db_path.to_str().unwrap()).unwrap(), guard));
138-
Ok(db)
179+
let db_path_str = self.db_path.to_str().expect(&format!("Invalid UTF-8 in database path: {}", self.db_path.display()));
180+
let db = <DBWithThreadMode<MultiThreaded>>::open(&opts, db_path_str).expect("Failed to open database");
181+
Ok(Arc::new(DB::new(db, guard)))
139182
}
140183
}

0 commit comments

Comments
 (0)