Skip to content

Commit aa5664d

Browse files
taco-pacoDodecahedr0x
authored andcommitted
feat: return ledger + accountsdb metrics (#624)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Performance** * Compaction now preserves account-modification data during cleanup to avoid losing important account-related entries. * Faster metadata access via column-count caching, reducing overhead during storage operations. * **Monitoring** * Continuous metrics for ledger storage size, accounts storage size, and account counts. * New duration histogram for column-count measurements and renamed/updated execution-time histogram for improved observability. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent c949dec commit aa5664d

6 files changed

Lines changed: 181 additions & 94 deletions

File tree

‎magicblock-api/src/tickers.rs‎

Lines changed: 65 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -109,67 +109,70 @@ pub fn init_system_metrics_ticker(
109109
accountsdb: &Arc<AccountsDb>,
110110
token: CancellationToken,
111111
) -> tokio::task::JoinHandle<()> {
112-
// fn try_set_ledger_counts(ledger: &Ledger) {
113-
// macro_rules! try_set_ledger_count {
114-
// ($name:ident) => {
115-
// paste::paste! {
116-
// match ledger.[< count_ $name >]() {
117-
// Ok(count) => {
118-
// metrics::[< set_ledger_ $name _count >](count);
119-
// }
120-
// Err(err) => warn!(
121-
// "Failed to get ledger {} count: {:?}",
122-
// stringify!($name),
123-
// err
124-
// ),
125-
// }
126-
// }
127-
// };
128-
// }
129-
// try_set_ledger_count!(block_times);
130-
// try_set_ledger_count!(blockhashes);
131-
// try_set_ledger_count!(slot_signatures);
132-
// try_set_ledger_count!(address_signatures);
133-
// try_set_ledger_count!(transaction_status);
134-
// try_set_ledger_count!(transaction_successful_status);
135-
// try_set_ledger_count!(transaction_failed_status);
136-
// try_set_ledger_count!(transactions);
137-
// try_set_ledger_count!(transaction_memos);
138-
// try_set_ledger_count!(perf_samples);
139-
// try_set_ledger_count!(account_mod_data);
140-
// }
141-
//
142-
// fn try_set_ledger_storage_size(ledger: &Ledger) {
143-
// match ledger.storage_size() {
144-
// Ok(byte_size) => metrics::set_ledger_size(byte_size),
145-
// Err(err) => warn!("Failed to get ledger storage size: {:?}", err),
146-
// }
147-
// }
148-
// fn set_accounts_storage_size(bank: &Bank) {
149-
// let byte_size = bank.accounts_db_storage_size();
150-
// metrics::set_accounts_size(byte_size);
151-
// }
152-
// fn set_accounts_count(bank: &Bank) {
153-
// metrics::set_accounts_count(bank.accounts_db.get_accounts_count());
154-
// }
155-
//
156-
// let ledger = ledger.clone();
157-
// let bank = bank.clone();
158-
// tokio::task::spawn(async move {
159-
// loop {
160-
// tokio::select! {
161-
// _ = tokio::time::sleep(tick_duration) => {
162-
// try_set_ledger_storage_size(&ledger);
163-
// set_accounts_storage_size(&bank);
164-
// try_set_ledger_counts(&ledger);
165-
// set_accounts_count(&bank);
166-
// },
167-
// _ = token.cancelled() => {
168-
// break;
169-
// }
170-
// }
171-
// }
172-
// })
112+
fn try_set_ledger_counts(ledger: &Ledger) {
113+
macro_rules! try_set_ledger_count {
114+
($name:ident) => {
115+
paste::paste! {
116+
match ledger.[< count_ $name >]() {
117+
Ok(count) => {
118+
metrics::[< set_ledger_ $name _count >](count);
119+
}
120+
Err(err) => warn!(
121+
"Failed to get ledger {} count: {:?}",
122+
stringify!($name),
123+
err
124+
),
125+
}
126+
}
127+
};
128+
}
129+
try_set_ledger_count!(block_times);
130+
try_set_ledger_count!(blockhashes);
131+
try_set_ledger_count!(slot_signatures);
132+
try_set_ledger_count!(address_signatures);
133+
try_set_ledger_count!(transaction_status);
134+
try_set_ledger_count!(transaction_successful_status);
135+
try_set_ledger_count!(transaction_failed_status);
136+
try_set_ledger_count!(transactions);
137+
try_set_ledger_count!(transaction_memos);
138+
try_set_ledger_count!(perf_samples);
139+
try_set_ledger_count!(account_mod_data);
140+
}
141+
142+
fn try_set_ledger_storage_size(ledger: &Ledger) {
143+
match ledger.storage_size() {
144+
Ok(byte_size) => metrics::set_ledger_size(byte_size),
145+
Err(err) => warn!("Failed to get ledger storage size: {:?}", err),
146+
}
147+
}
148+
fn set_accounts_storage_size(accounts_db: &AccountsDb) {
149+
let byte_size = accounts_db.storage_size();
150+
metrics::set_accounts_size(byte_size.try_into().unwrap_or(i64::MAX));
151+
}
152+
fn set_accounts_count(accounts_db: &AccountsDb) {
153+
metrics::set_accounts_count(
154+
accounts_db
155+
.get_accounts_count()
156+
.try_into()
157+
.unwrap_or(i64::MAX),
158+
);
159+
}
173160

174-
tokio::task::spawn(async move {})
161+
let ledger = ledger.clone();
162+
let bank = accountsdb.clone();
163+
tokio::task::spawn(async move {
164+
loop {
165+
tokio::select! {
166+
_ = tokio::time::sleep(tick_duration) => {
167+
try_set_ledger_storage_size(&ledger);
168+
set_accounts_storage_size(&bank);
169+
metrics::observe_columns_count_duration(|| try_set_ledger_counts(&ledger));
170+
set_accounts_count(&bank);
171+
},
172+
_ = token.cancelled() => {
173+
break;
174+
}
175+
}
176+
}
177+
})
175178
}

‎magicblock-ledger/src/database/columns.rs‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,9 @@ pub trait Column {
129129
// first item in the key.
130130
fn as_index(slot: Slot) -> Self::Index;
131131
fn slot(index: Self::Index) -> Slot;
132+
fn keep_all_on_compaction() -> bool {
133+
false
134+
}
132135
}
133136

134137
pub trait ColumnName {
@@ -651,6 +654,11 @@ impl Column for AccountModDatas {
651654
fn as_index(slot: Slot) -> Self::Index {
652655
slot
653656
}
657+
658+
/// We don't clean AccountModData on compaction as it isn't slot based
659+
fn keep_all_on_compaction() -> bool {
660+
true
661+
}
654662
}
655663

656664
impl TypedColumn for AccountModDatas {

‎magicblock-ledger/src/database/compaction_filter.rs‎

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use std::{
77
},
88
};
99

10-
use log::trace;
1110
use rocksdb::{
1211
compaction_filter::CompactionFilter,
1312
compaction_filter_factory::{
@@ -82,21 +81,17 @@ pub(crate) struct PurgedSlotFilter<C: Column + ColumnName> {
8281
impl<C: Column + ColumnName> CompactionFilter for PurgedSlotFilter<C> {
8382
fn filter(
8483
&mut self,
85-
level: u32,
84+
_level: u32,
8685
key: &[u8],
8786
_value: &[u8],
8887
) -> CompactionDecision {
8988
use rocksdb::CompactionDecision::*;
90-
trace!("CompactionFilter: triggered!");
89+
if C::keep_all_on_compaction() {
90+
return Keep;
91+
}
9192

9293
let slot_in_key = C::slot(C::index(key));
9394
if slot_in_key < self.oldest_slot {
94-
trace!(
95-
"CompactionFilter: removing key. level: {}, slot: {}",
96-
level,
97-
slot_in_key
98-
);
99-
10095
// It is safe to delete this key
10196
// since those slots were truncated anyway
10297
Remove

‎magicblock-ledger/src/database/ledger_column.rs‎

Lines changed: 70 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ use std::{
77
};
88

99
use bincode::{deserialize, serialize};
10-
use log::{error, warn};
10+
use log::warn;
1111
use prost::Message;
12-
use rocksdb::{properties as RocksProperties, ColumnFamily};
12+
use rocksdb::{properties as RocksProperties, CStrLike, ColumnFamily};
1313
use serde::de::DeserializeOwned;
1414

1515
use super::{
@@ -228,7 +228,7 @@ where
228228
/// [here](https://github.com/facebook/rocksdb/blob/08809f5e6cd9cc4bc3958dd4d59457ae78c76660/include/rocksdb/db.h#L654-L689).
229229
pub fn get_int_property(
230230
&self,
231-
name: &'static std::ffi::CStr,
231+
name: impl CStrLike,
232232
) -> Result<i64, LedgerError> {
233233
self.backend.get_int_property_cf(self.handle(), name)
234234
}
@@ -277,23 +277,45 @@ where
277277
self.backend.flush_cf(self.handle())
278278
}
279279

280+
#[inline(always)]
281+
fn is_sequential_cf<T: ColumnName>() -> bool {
282+
matches!(
283+
T::NAME,
284+
crate::database::columns::Blocktime::NAME
285+
| crate::database::columns::Blockhash::NAME
286+
| crate::database::columns::PerfSamples::NAME
287+
| crate::database::columns::AccountModDatas::NAME
288+
)
289+
}
290+
291+
/// Initialize value when current one is `DIRTY_VALUE`
292+
/// Value isn't set if current doesn't equal `DIRTY_VALUE`
293+
fn init_column_count_cache(&self) -> LedgerResult<()> {
294+
let count = if Self::is_sequential_cf::<C>() {
295+
get_column_count_sequential_column(self)? as i64
296+
} else {
297+
get_column_count_complex_column(self)? as i64
298+
};
299+
300+
// We can ignore error here since it means value already initialized
301+
let _ = self.entry_counter.compare_exchange(
302+
DIRTY_COUNT,
303+
count,
304+
Ordering::AcqRel,
305+
Ordering::Relaxed,
306+
);
307+
308+
Ok(())
309+
}
310+
280311
pub fn count_column_using_cache(&self) -> LedgerResult<i64> {
281312
let cached = self.entry_counter.load(Ordering::Relaxed);
282313
if cached != DIRTY_COUNT {
283-
return Ok(cached);
314+
Ok(cached)
315+
} else {
316+
self.init_column_count_cache()?;
317+
Ok(self.entry_counter.load(Ordering::Acquire))
284318
}
285-
286-
self
287-
.iter(IteratorMode::Start)
288-
.map(Iterator::count)
289-
.map(|val| if val > i64::MAX as usize {
290-
// NOTE: this value is only used for metrics/diagnostics and
291-
// aside from the fact that we will never encounter this case,
292-
// it is good enough to return i64::MAX
293-
error!("Column {} count is too large: {} for metrics, returning max.", C::NAME, val);
294-
i64::MAX
295-
} else { val as i64 })
296-
.inspect(|updated| self.entry_counter.store(*updated, Ordering::Relaxed))
297319
}
298320

299321
/// Increases entries counter if it's not [`DIRTY_COUNT`]
@@ -540,6 +562,38 @@ where
540562
}
541563
}
542564

565+
/// When column key format is sequentially incremented key, like: `SlotColumn`
566+
/// We can simplify extraction of count by calculating `LastKey - FirstKey + 1`
567+
fn get_column_count_sequential_column<C: Column + ColumnName>(
568+
ledger_column: &LedgerColumn<C>,
569+
) -> LedgerResult<u64> {
570+
let last_key = ledger_column.iter(IteratorMode::End)?.next();
571+
let start_key = ledger_column.iter(IteratorMode::Start)?.next();
572+
let count = match (start_key, last_key) {
573+
(Some((start_key, _)), Some((last_key, _))) => {
574+
let last_slot = C::slot(last_key);
575+
let start_slot = C::slot(start_key);
576+
577+
last_slot - start_slot + 1
578+
}
579+
// Empty ColumnFamily
580+
_ => 0,
581+
};
582+
583+
Ok(count)
584+
}
585+
586+
/// For complex columns, like: `AddressSignatures`
587+
/// We get column count using rocksdb's "estimate-num-keys" proprty
588+
/// Due to properies of how we use DB this value shall be ~correct on start
589+
fn get_column_count_complex_column<C: Column + ColumnName>(
590+
ledger_column: &LedgerColumn<C>,
591+
) -> LedgerResult<u64> {
592+
const ESTIMATE_NUM_KEYS: &str = "rocksdb.estimate-num-keys";
593+
594+
Ok(ledger_column.get_int_property(ESTIMATE_NUM_KEYS)? as u64)
595+
}
596+
543597
/// Increases entries counter if it's not [`DIRTY_COUNT`]
544598
/// Otherwise just skips it until it is set
545599
pub fn try_increase_entry_counter(entry_counter: &AtomicI64, by: u64) {

‎magicblock-ledger/src/database/rocks_db.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::{
88
};
99

1010
use rocksdb::{
11-
AsColumnFamilyRef, ColumnFamily, DBIterator, DBPinnableSlice,
11+
AsColumnFamilyRef, CStrLike, ColumnFamily, DBIterator, DBPinnableSlice,
1212
DBRawIterator, FlushOptions, IteratorMode as RocksIteratorMode, LiveFile,
1313
Options, WriteBatch as RWriteBatch, DB,
1414
};
@@ -237,7 +237,7 @@ impl Rocks {
237237
pub fn get_int_property_cf(
238238
&self,
239239
cf: &ColumnFamily,
240-
name: &'static std::ffi::CStr,
240+
name: impl CStrLike,
241241
) -> LedgerResult<i64> {
242242
match self.db.property_int_value_cf(cf, name) {
243243
Ok(Some(value)) => Ok(value.try_into().unwrap()),

‎magicblock-metrics/src/metrics/mod.rs‎

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,20 @@ lazy_static::lazy_static! {
8282
static ref LEDGER_ACCOUNT_MOD_DATA_GAUGE: IntGauge = IntGauge::new(
8383
"ledger_account_mod_data_gauge", "Ledger Account Mod Data Gauge",
8484
).unwrap();
85+
pub static ref LEDGER_COLUMNS_COUNT_DURATION_SECONDS: Histogram = Histogram::with_opts(
86+
HistogramOpts::new(
87+
"ledger_columns_count_duration_seconds",
88+
"Time taken to compute ledger columns counts"
89+
)
90+
.buckets(
91+
MICROS_10_90.iter().chain(
92+
MICROS_100_900.iter()).chain(
93+
MILLIS_1_9.iter()).chain(
94+
MILLIS_10_90.iter()).chain(
95+
MILLIS_100_900.iter()).chain(
96+
SECONDS_1_9.iter()).cloned().collect()
97+
),
98+
).unwrap();
8599

86100
// -----------------
87101
// Accounts
@@ -193,14 +207,11 @@ lazy_static::lazy_static! {
193207

194208
static ref COMMITTOR_INTENT_EXECUTION_TIME_HISTOGRAM: HistogramVec = HistogramVec::new(
195209
HistogramOpts::new(
196-
"committor_intent_execution_time_histogram",
210+
"committor_intent_execution_time_histogram_v2",
197211
"Time in seconds spent on intent execution"
198212
)
199213
.buckets(
200-
MILLIS_1_9.iter()
201-
.chain(MILLIS_10_90.iter())
202-
.chain(MILLIS_100_900.iter())
203-
.chain(SECONDS_1_9.iter()).cloned().collect(),
214+
vec![0.01, 0.1, 1.0, 3.0, 5.0, 10.0, 15.0, 20.0, 25.0]
204215
),
205216
&["intent_kind", "outcome_kind"],
206217
).unwrap();
@@ -234,6 +245,7 @@ pub(crate) fn register() {
234245
register!(LEDGER_TRANSACTION_MEMOS_GAUGE);
235246
register!(LEDGER_PERF_SAMPLES_GAUGE);
236247
register!(LEDGER_ACCOUNT_MOD_DATA_GAUGE);
248+
register!(LEDGER_COLUMNS_COUNT_DURATION_SECONDS);
237249
register!(ACCOUNTS_SIZE_GAUGE);
238250
register!(ACCOUNTS_COUNT_GAUGE);
239251
register!(PENDING_ACCOUNT_CLONES_GAUGE);
@@ -317,6 +329,21 @@ pub fn set_ledger_account_mod_data_count(count: i64) {
317329
LEDGER_ACCOUNT_MOD_DATA_GAUGE.set(count);
318330
}
319331

332+
pub fn observe_columns_count_duration<F, T>(f: F) -> T
333+
where
334+
F: FnOnce() -> T,
335+
{
336+
LEDGER_COLUMNS_COUNT_DURATION_SECONDS.observe_closure_duration(f)
337+
}
338+
339+
pub fn set_accounts_size(value: i64) {
340+
ACCOUNTS_SIZE_GAUGE.set(value)
341+
}
342+
343+
pub fn set_accounts_count(value: i64) {
344+
ACCOUNTS_COUNT_GAUGE.set(value)
345+
}
346+
320347
pub fn inc_pending_clone_requests() {
321348
PENDING_ACCOUNT_CLONES_GAUGE.inc()
322349
}

0 commit comments

Comments
 (0)