Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions src/conf/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ pub struct Config {
#[validate(range(min = 1024, max = 65535))]
pub port: u16,
pub memory: u64,
/// When enabled, all RocksDB column families share a single block cache
/// built once in `StorageOptions::from_config`. When disabled, each
/// database instance builds its own independent block cache.
pub share_block_cache: bool,
pub small_compaction_threshold: usize,
pub small_compaction_duration_threshold: usize,
pub rocksdb_max_subcompactions: u32,
Expand Down Expand Up @@ -134,6 +138,7 @@ impl std::fmt::Debug for Config {
f.debug_struct("Config")
.field("port", &self.port)
.field("memory", &self.memory)
.field("share_block_cache", &self.share_block_cache)
.field(
"small_compaction_threshold",
&self.small_compaction_threshold,
Expand Down Expand Up @@ -242,6 +247,7 @@ impl Default for Config {
port: DEFAULT_PORT,
timeout: 50,
memory: 1024 * 1024 * 1024, // 1GB
share_block_cache: true,
log_dir: "./kiwi_data/logs".to_string(),
data_dir: "./kiwi_data/db".to_string(),
redis_compatible_mode: false,
Expand Down Expand Up @@ -369,6 +375,15 @@ impl Config {
config.memory =
parse_memory(&value).map_err(|e| Error::MemoryParse { source: e })?;
}
"share-block-cache" => {
config.share_block_cache =
parse_bool_from_string(&value).map_err(|e| Error::InvalidConfig {
source: serde_ini::de::Error::Custom(format!(
"Invalid share-block-cache: {}",
e
)),
})?;
}
"small-compaction-threshold" => {
config.small_compaction_threshold =
value.parse().map_err(|e| Error::InvalidConfig {
Expand Down Expand Up @@ -817,3 +832,51 @@ impl Config {
rocksdb::BlockBasedOptions::default()
}
}

#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]

use super::*;
use std::io::Write;
use std::sync::atomic::{AtomicU64, Ordering};

static TEMP_COUNTER: AtomicU64 = AtomicU64::new(0);

#[test]
fn default_share_block_cache_is_enabled() {
let config = Config::default();
assert!(config.share_block_cache);
}

/// Write `content` to a uniquely-named temp file and load it as a [`Config`].
/// The unique name avoids collisions when tests run in parallel.
fn load_from_str(content: &str) -> Result<Config, Error> {
let n = TEMP_COUNTER.fetch_add(1, Ordering::SeqCst);
let filename = format!("kiwi_test_sbc_{}_{}.conf", std::process::id(), n);
let path = std::env::temp_dir().join(filename);
let mut f = std::fs::File::create(&path).unwrap();
writeln!(f, "{}", content).unwrap();
drop(f);
let result = Config::load(path.to_str().unwrap());
let _ = std::fs::remove_file(&path);
result
}

#[test]
fn parse_share_block_cache_yes() {
let config = load_from_str("share-block-cache yes").unwrap();
assert!(config.share_block_cache);
}

#[test]
fn parse_share_block_cache_no() {
let config = load_from_str("share-block-cache no").unwrap();
assert!(!config.share_block_cache);
}

#[test]
fn parse_share_block_cache_invalid() {
assert!(load_from_str("share-block-cache maybe").is_err());
}
}
1 change: 1 addition & 0 deletions src/conf/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ mod tests {
port: 999,
timeout: 100,
redis_compatible_mode: false,
share_block_cache: true,
log_dir: "".to_string(),
data_dir: "./kiwi_data/db".to_string(),
memory: 1024,
Expand Down
20 changes: 19 additions & 1 deletion src/storage/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@

//! Storage engine options and configurations

use rocksdb::{BlockBasedOptions, Options};
use std::sync::Arc;

use rocksdb::{BlockBasedOptions, Cache, Options};

use crate::error::{OptionNotDynamicallyModifiableSnafu, Result};

Expand Down Expand Up @@ -58,6 +60,10 @@ pub struct StorageOptions {
pub block_cache_size: usize,
/// Whether to share block cache across column families
pub share_block_cache: bool,
/// Shared block cache built once in `from_config` and reused by every
/// `Redis` instance through the shared `Arc<StorageOptions>`.
/// `None` means no shared cache; each instance falls back to its own.
pub block_cache: Option<Arc<Cache>>,
/// Maximum size for statistics
pub statistics_max_size: usize,
/// Threshold for small value compaction
Expand Down Expand Up @@ -92,6 +98,7 @@ impl Default for StorageOptions {
table_options: BlockBasedOptions::default(),
block_cache_size: 8 << 30, // 8GB
share_block_cache: true,
block_cache: None,
statistics_max_size: 0,
small_compaction_threshold: 5000,
small_compaction_duration_threshold: 10000,
Expand All @@ -113,9 +120,20 @@ impl StorageOptions {
/// Build StorageOptions from a loaded [`conf::config::Config`].
pub fn from_config(config: &conf::config::Config) -> Self {
let rocksdb_opts = config.get_rocksdb_options();
// Build the shared block cache once when sharing is enabled and a
// memory budget is configured. Every `Redis` instance receives the
// same `Arc<StorageOptions>` and therefore reuses this single cache.
let block_cache = if config.share_block_cache && config.memory > 0 {
Some(Arc::new(rocksdb::Cache::new_lru_cache(
config.memory as usize,
)))
} else {
None
};
Self {
options: rocksdb_opts,
block_cache_size: config.memory as usize,
block_cache,
small_compaction_threshold: config.small_compaction_threshold,
small_compaction_duration_threshold: config.small_compaction_duration_threshold,
db_instance_num: config.db_instance_num,
Expand Down
19 changes: 15 additions & 4 deletions src/storage/src/redis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -474,10 +474,21 @@ impl Redis {
table_opts.set_block_size(size);
}

// Set block cache
if !storage_options.share_block_cache && storage_options.block_cache_size > 0 {
let cache = rocksdb::Cache::new_lru_cache(storage_options.block_cache_size);
table_opts.set_block_cache(&cache);
// Set block cache.
//
// When sharing is enabled, a single cache is built once in
// `StorageOptions::from_config` and shared by every `Redis` instance
// through the shared `Arc<StorageOptions>`. Otherwise each instance
// builds its own independent cache when `block_cache_size > 0`.
match &storage_options.block_cache {
Some(shared) => {
table_opts.set_block_cache(shared);
}
None if !storage_options.share_block_cache && storage_options.block_cache_size > 0 => {
let cache = rocksdb::Cache::new_lru_cache(storage_options.block_cache_size);
table_opts.set_block_cache(&cache);
}
_ => {}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
}

// Set table properties collector factory for LogIndex tracking
Expand Down
Loading