Skip to content

Commit ed6f38f

Browse files
committed
Allow concurrent DB background operations for SSDs
Full compactions can be done much faster when running with `db_parallelism=8`.
1 parent 6bfaba9 commit ed6f38f

File tree

4 files changed

+40
-19
lines changed

4 files changed

+40
-19
lines changed

internal/config_specification.toml

+6
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ name = "db_log_dir"
3434
type = "std::path::PathBuf"
3535
doc = "Directory to store index database internal log (default: same as specified by `db_dir`)"
3636

37+
[[param]]
38+
name = "db_parallelism"
39+
type = "u8"
40+
doc = "Max threads to use for DB background operations (flushes and compactions)"
41+
default = "1"
42+
3743
[[param]]
3844
name = "daemon_dir"
3945
type = "std::path::PathBuf"

src/config.rs

+2
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ pub struct Config {
127127
pub network: Network,
128128
pub db_path: PathBuf,
129129
pub db_log_dir: Option<PathBuf>,
130+
pub db_parallelism: u8,
130131
pub daemon_auth: SensitiveAuth,
131132
pub daemon_rpc_addr: SocketAddr,
132133
pub daemon_p2p_addr: SocketAddr,
@@ -354,6 +355,7 @@ impl Config {
354355
network: config.network,
355356
db_path: config.db_dir,
356357
db_log_dir: config.db_log_dir,
358+
db_parallelism: config.db_parallelism,
357359
daemon_auth,
358360
daemon_rpc_addr,
359361
daemon_p2p_addr,

src/db.rs

+31-19
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,14 @@ impl Default for Config {
9595
}
9696
}
9797

98-
fn default_opts() -> rocksdb::Options {
98+
fn default_opts(parallelism: u8) -> rocksdb::Options {
9999
let mut block_opts = rocksdb::BlockBasedOptions::default();
100100
block_opts.set_checksum_type(rocksdb::ChecksumType::CRC32c);
101101

102102
let mut opts = rocksdb::Options::default();
103+
opts.increase_parallelism(parallelism.into());
104+
opts.set_max_subcompactions(parallelism.into());
105+
103106
opts.set_keep_log_file_num(10);
104107
opts.set_max_open_files(16);
105108
opts.set_compaction_style(rocksdb::DBCompactionStyle::Level);
@@ -114,23 +117,27 @@ fn default_opts() -> rocksdb::Options {
114117
}
115118

116119
impl DBStore {
117-
fn create_cf_descriptors() -> Vec<rocksdb::ColumnFamilyDescriptor> {
120+
fn create_cf_descriptors(parallelism: u8) -> Vec<rocksdb::ColumnFamilyDescriptor> {
118121
COLUMN_FAMILIES
119122
.iter()
120-
.map(|&name| rocksdb::ColumnFamilyDescriptor::new(name, default_opts()))
123+
.map(|&name| rocksdb::ColumnFamilyDescriptor::new(name, default_opts(parallelism)))
121124
.collect()
122125
}
123126

124-
fn open_internal(path: &Path, log_dir: Option<&Path>) -> Result<Self> {
125-
let mut db_opts = default_opts();
127+
fn open_internal(path: &Path, log_dir: Option<&Path>, parallelism: u8) -> Result<Self> {
128+
let mut db_opts = default_opts(parallelism);
126129
db_opts.create_if_missing(true);
127130
db_opts.create_missing_column_families(true);
128131
if let Some(d) = log_dir {
129132
db_opts.set_db_log_dir(d);
130133
}
131134

132-
let db = rocksdb::DB::open_cf_descriptors(&db_opts, path, Self::create_cf_descriptors())
133-
.with_context(|| format!("failed to open DB: {}", path.display()))?;
135+
let db = rocksdb::DB::open_cf_descriptors(
136+
&db_opts,
137+
path,
138+
Self::create_cf_descriptors(parallelism),
139+
)
140+
.with_context(|| format!("failed to open DB: {}", path.display()))?;
134141
let live_files = db.live_files()?;
135142
info!(
136143
"{:?}: {} SST files, {} GB, {} Grows",
@@ -155,8 +162,13 @@ impl DBStore {
155162
}
156163

157164
/// Opens a new RocksDB at the specified location.
158-
pub fn open(path: &Path, log_dir: Option<&Path>, auto_reindex: bool) -> Result<Self> {
159-
let mut store = Self::open_internal(path, log_dir)?;
165+
pub fn open(
166+
path: &Path,
167+
log_dir: Option<&Path>,
168+
auto_reindex: bool,
169+
parallelism: u8,
170+
) -> Result<Self> {
171+
let mut store = Self::open_internal(path, log_dir, parallelism)?;
160172
let config = store.get_config();
161173
debug!("DB {:?}", config);
162174
let mut config = config.unwrap_or_default(); // use default config when DB is empty
@@ -182,13 +194,13 @@ impl DBStore {
182194
);
183195
// close DB before deletion
184196
drop(store);
185-
rocksdb::DB::destroy(&default_opts(), path).with_context(|| {
197+
rocksdb::DB::destroy(&default_opts(parallelism), path).with_context(|| {
186198
format!(
187199
"re-index required but the old database ({}) can not be deleted",
188200
path.display()
189201
)
190202
})?;
191-
store = Self::open_internal(path, log_dir)?;
203+
store = Self::open_internal(path, log_dir, parallelism)?;
192204
config = Config::default(); // re-init config after dropping DB
193205
}
194206
if config.compacted {
@@ -432,13 +444,13 @@ mod tests {
432444
fn test_reindex_new_format() {
433445
let dir = tempfile::tempdir().unwrap();
434446
{
435-
let store = DBStore::open(dir.path(), None, false).unwrap();
447+
let store = DBStore::open(dir.path(), None, false, 1).unwrap();
436448
let mut config = store.get_config().unwrap();
437449
config.format += 1;
438450
store.set_config(config);
439451
};
440452
assert_eq!(
441-
DBStore::open(dir.path(), None, false)
453+
DBStore::open(dir.path(), None, false, 1)
442454
.err()
443455
.unwrap()
444456
.to_string(),
@@ -449,7 +461,7 @@ mod tests {
449461
)
450462
);
451463
{
452-
let store = DBStore::open(dir.path(), None, true).unwrap();
464+
let store = DBStore::open(dir.path(), None, true, 1).unwrap();
453465
store.flush();
454466
let config = store.get_config().unwrap();
455467
assert_eq!(config.format, CURRENT_FORMAT);
@@ -467,14 +479,14 @@ mod tests {
467479
db.put(b"F", b"").unwrap(); // insert legacy DB compaction marker (in 'default' column family)
468480
};
469481
assert_eq!(
470-
DBStore::open(dir.path(), None, false)
482+
DBStore::open(dir.path(), None, false, 1)
471483
.err()
472484
.unwrap()
473485
.to_string(),
474486
format!("re-index required due to legacy format",)
475487
);
476488
{
477-
let store = DBStore::open(dir.path(), None, true).unwrap();
489+
let store = DBStore::open(dir.path(), None, true, 1).unwrap();
478490
store.flush();
479491
let config = store.get_config().unwrap();
480492
assert_eq!(config.format, CURRENT_FORMAT);
@@ -484,7 +496,7 @@ mod tests {
484496
#[test]
485497
fn test_db_prefix_scan() {
486498
let dir = tempfile::tempdir().unwrap();
487-
let store = DBStore::open(dir.path(), None, true).unwrap();
499+
let store = DBStore::open(dir.path(), None, true, 1).unwrap();
488500

489501
let items = [
490502
*b"ab ",
@@ -509,15 +521,15 @@ mod tests {
509521
#[test]
510522
fn test_db_log_in_same_dir() {
511523
let dir1 = tempfile::tempdir().unwrap();
512-
let _store = DBStore::open(dir1.path(), None, true).unwrap();
524+
let _store = DBStore::open(dir1.path(), None, true, 1).unwrap();
513525

514526
// LOG file is created in dir1
515527
let dir_files = list_log_files(dir1.path());
516528
assert_eq!(dir_files, vec![OsStr::new("LOG")]);
517529

518530
let dir2 = tempfile::tempdir().unwrap();
519531
let dir3 = tempfile::tempdir().unwrap();
520-
let _store = DBStore::open(dir2.path(), Some(dir3.path()), true).unwrap();
532+
let _store = DBStore::open(dir2.path(), Some(dir3.path()), true, 1).unwrap();
521533

522534
// *_LOG file is not created in dir2, but in dir3
523535
let dir_files = list_log_files(dir2.path());

src/tracker.rs

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ impl Tracker {
3636
&config.db_path,
3737
config.db_log_dir.as_deref(),
3838
config.auto_reindex,
39+
config.db_parallelism,
3940
)?;
4041
let chain = Chain::new(config.network);
4142
Ok(Self {

0 commit comments

Comments
 (0)