@@ -7,9 +7,9 @@ use std::{
77} ;
88
99use bincode:: { deserialize, serialize} ;
10- use log:: { error , warn} ;
10+ use log:: warn;
1111use prost:: Message ;
12- use rocksdb:: { properties as RocksProperties , ColumnFamily } ;
12+ use rocksdb:: { properties as RocksProperties , CStrLike , ColumnFamily } ;
1313use serde:: de:: DeserializeOwned ;
1414
1515use 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
545599pub fn try_increase_entry_counter ( entry_counter : & AtomicI64 , by : u64 ) {
0 commit comments