(LedgerStore) Hidden validator argument for RocksDB perf samples (#24682)

#### Problem
Currently, the number of RocksDB perf samples is controlled by an env arg
which is later handled using a lazy_static variable.  However, there is a known
performance overhead of using lazy_static as mentioned in
https://github.com/solana-labs/solana/pull/6472.

#### Summary of Changes
Instead, this PR uses a hidden validator argument, --rocksdb-perf-sample-interval,
for controlling how often RocksDB read/write performance sample is collected.
This commit is contained in:
Yueh-Hsuan Chiang 2022-04-29 15:28:50 -07:00 committed by GitHub
parent cb501807ca
commit b56c091b37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 45 additions and 29 deletions

View File

@ -748,7 +748,8 @@ impl Rocks {
}
fn write(&self, batch: RWriteBatch) -> Result<()> {
let is_perf_context_enabled = maybe_collect_perf_context();
let is_perf_context_enabled =
maybe_collect_perf_context(self.column_options.rocks_perf_sample_interval);
let result = self.db.write(batch);
if is_perf_context_enabled {
report_write_perf_context(rocksdb_metric_header!(
@ -2001,6 +2002,11 @@ pub struct LedgerColumnOptions {
// Determine the way to compress column families which are eligible for
// compression.
pub compression_type: BlockstoreCompressionType,
// Control how often RocksDB read/write performance samples are collected.
// If the value is greater than 0, then RocksDB read/write perf sample
// will be collected once for every `rocks_perf_sample_interval` ops.
pub rocks_perf_sample_interval: usize,
}
impl Default for LedgerColumnOptions {
@ -2008,6 +2014,7 @@ impl Default for LedgerColumnOptions {
Self {
shred_storage_type: ShredStorageType::RocksLevel,
compression_type: BlockstoreCompressionType::default(),
rocks_perf_sample_interval: 0,
}
}
}
@ -2177,7 +2184,8 @@ where
C: Column + ColumnName + ColumnMetrics,
{
pub fn get_bytes(&self, key: C::Index) -> Result<Option<Vec<u8>>> {
let is_perf_context_enabled = maybe_collect_perf_context();
let is_perf_context_enabled =
maybe_collect_perf_context(self.column_options.rocks_perf_sample_interval);
let result = self.backend.get_cf(self.handle(), &C::key(key));
if is_perf_context_enabled {
report_read_perf_context(C::rocksdb_get_perf_metric_header(&self.column_options));
@ -2252,7 +2260,8 @@ where
}
pub fn put_bytes(&self, key: C::Index, value: &[u8]) -> Result<()> {
let is_perf_context_enabled = maybe_collect_perf_context();
let is_perf_context_enabled =
maybe_collect_perf_context(self.column_options.rocks_perf_sample_interval);
let result = self.backend.put_cf(self.handle(), &C::key(key), value);
if is_perf_context_enabled {
report_write_perf_context(C::rocksdb_put_perf_metric_header(&self.column_options));
@ -2279,32 +2288,16 @@ mod rocks_metrics_utils {
},
std::cell::RefCell,
};
const METRIC_SAMPLES_1K: i32 = 1000;
// The default number of rocksdb perf samples in 1K
const ROCKSDB_PERF_CONTEXT_SAMPLES_IN_1K_DEFAULT: i32 = 0;
lazy_static! {
// The number of RocksDB performance counter samples in 1000.
static ref ROCKSDB_PERF_CONTEXT_SAMPLES_IN_1K: i32 =
std::env::var("SOLANA_METRICS_ROCKSDB_PERF_SAMPLES_IN_1K")
.map(|x| {
x.parse().expect("Failed to parse SOLANA_METRICS_ROCKSDB_PERF_SAMPLES_IN_1K")
}).unwrap_or(ROCKSDB_PERF_CONTEXT_SAMPLES_IN_1K_DEFAULT);
}
// Thread local instance of RocksDB's PerfContext.
thread_local! {static PER_THREAD_ROCKS_PERF_CONTEXT: RefCell<PerfContext> = RefCell::new(PerfContext::default());}
/// The function enables RocksDB's PerfContext in N out of 1000
/// where N is ROCKSDB_PERF_CONTEXT_SAMPLES_IN_1K.
///
/// Returns true if the PerfContext is enabled.
pub fn maybe_collect_perf_context() -> bool {
if *ROCKSDB_PERF_CONTEXT_SAMPLES_IN_1K <= 0 {
pub(crate) fn maybe_collect_perf_context(sample_interval: usize) -> bool {
if sample_interval == 0 {
return false;
}
if thread_rng().gen_range(0, METRIC_SAMPLES_1K) > *ROCKSDB_PERF_CONTEXT_SAMPLES_IN_1K {
if thread_rng().gen_range(0, sample_interval) > 0 {
return false;
}
set_perf_stats(PerfStatsLevel::EnableTime);
@ -2316,7 +2309,7 @@ mod rocks_metrics_utils {
/// Reports the collected PerfContext and disables the PerfContext after
/// reporting.
pub fn report_read_perf_context(metric_header: &'static str) {
pub(crate) fn report_read_perf_context(metric_header: &'static str) {
PER_THREAD_ROCKS_PERF_CONTEXT.with(|perf_context_cell| {
set_perf_stats(PerfStatsLevel::Disable);
let perf_context = perf_context_cell.borrow();
@ -2570,7 +2563,8 @@ where
{
pub fn get(&self, key: C::Index) -> Result<Option<C::Type>> {
let mut result = Ok(None);
let is_perf_context_enabled = maybe_collect_perf_context();
let is_perf_context_enabled =
maybe_collect_perf_context(self.column_options.rocks_perf_sample_interval);
if let Some(serialized_value) = self.backend.get_cf(self.handle(), &C::key(key))? {
let value = deserialize(&serialized_value)?;
@ -2584,7 +2578,8 @@ where
}
pub fn put(&self, key: C::Index, value: &C::Type) -> Result<()> {
let is_perf_context_enabled = maybe_collect_perf_context();
let is_perf_context_enabled =
maybe_collect_perf_context(self.column_options.rocks_perf_sample_interval);
let serialized_value = serialize(value)?;
let result = self
@ -2598,7 +2593,8 @@ where
}
pub fn delete(&self, key: C::Index) -> Result<()> {
let is_perf_context_enabled = maybe_collect_perf_context();
let is_perf_context_enabled =
maybe_collect_perf_context(self.column_options.rocks_perf_sample_interval);
let result = self.backend.delete_cf(self.handle(), &C::key(key));
if is_perf_context_enabled {
report_write_perf_context(C::rocksdb_delete_perf_metric_header(&self.column_options));
@ -2615,7 +2611,8 @@ where
&self,
key: C::Index,
) -> Result<Option<C::Type>> {
let is_perf_context_enabled = maybe_collect_perf_context();
let is_perf_context_enabled =
maybe_collect_perf_context(self.column_options.rocks_perf_sample_interval);
let result = self.backend.get_cf(self.handle(), &C::key(key));
if is_perf_context_enabled {
report_read_perf_context(C::rocksdb_get_perf_metric_header(&self.column_options));
@ -2633,7 +2630,8 @@ where
}
pub fn get_protobuf(&self, key: C::Index) -> Result<Option<C::Type>> {
let is_perf_context_enabled = maybe_collect_perf_context();
let is_perf_context_enabled =
maybe_collect_perf_context(self.column_options.rocks_perf_sample_interval);
let result = self.backend.get_cf(self.handle(), &C::key(key));
if is_perf_context_enabled {
report_read_perf_context(C::rocksdb_get_perf_metric_header(&self.column_options));
@ -2650,7 +2648,8 @@ where
let mut buf = Vec::with_capacity(value.encoded_len());
value.encode(&mut buf)?;
let is_perf_context_enabled = maybe_collect_perf_context();
let is_perf_context_enabled =
maybe_collect_perf_context(self.column_options.rocks_perf_sample_interval);
let result = self.backend.put_cf(self.handle(), &C::key(key), &buf);
if is_perf_context_enabled {
report_write_perf_context(C::rocksdb_put_perf_metric_header(&self.column_options));

View File

@ -1029,6 +1029,18 @@ pub fn main() {
transaction status data. \
Turning on compression can save ~10% of the ledger size."),
)
.arg(
Arg::with_name("rocksdb_perf_sample_interval")
.hidden(true)
.long("rocksdb-perf-sample-interval")
.value_name("ROCKS_PERF_SAMPLE_INTERVAL")
.takes_value(true)
.validator(is_parsable::<usize>)
.default_value("0")
.help("Controls how often RocksDB read/write performance sample is collected. \
Reads/writes perf samples are collected in 1 / ROCKS_PERF_SAMPLE_INTERVAL sampling rate."),
)
.arg(
Arg::with_name("skip_poh_verify")
.long("skip-poh-verify")
@ -2726,6 +2738,11 @@ pub fn main() {
),
},
},
rocks_perf_sample_interval: value_t_or_exit!(
matches,
"rocksdb_perf_sample_interval",
usize
),
};
if matches.is_present("halt_on_known_validators_accounts_hash_mismatch") {