From 71bb64f929a3aaa503b9d732f46ba090ba9d21db Mon Sep 17 00:00:00 2001 From: taco-paco Date: Fri, 28 Nov 2025 18:30:39 +0700 Subject: [PATCH 1/8] fix: remove heavy deletes # Conflicts: # magicblock-ledger/src/ledger_truncator.rs --- .../src/database/ledger_column.rs | 9 +++ magicblock-ledger/src/database/rocks_db.rs | 10 +++ magicblock-ledger/src/ledger_truncator.rs | 72 +++++++++---------- magicblock-ledger/src/store/api.rs | 9 +++ magicblock-metrics/src/metrics/mod.rs | 14 ++++ 5 files changed, 78 insertions(+), 36 deletions(-) diff --git a/magicblock-ledger/src/database/ledger_column.rs b/magicblock-ledger/src/database/ledger_column.rs index 0965989ba..218ce0a5e 100644 --- a/magicblock-ledger/src/database/ledger_column.rs +++ b/magicblock-ledger/src/database/ledger_column.rs @@ -254,6 +254,15 @@ where write_batch.delete::(key); } + pub fn delete_range( + &self, + from: C::Index, + to: C::Index, + ) -> LedgerResult<()> { + self.backend + .delete_range_cf(self.handle(), C::key(from), C::key(to)) + } + pub fn delete_range_in_batch( &self, write_batch: &mut WriteBatch, diff --git a/magicblock-ledger/src/database/rocks_db.rs b/magicblock-ledger/src/database/rocks_db.rs index a84cfec6f..319fda9da 100644 --- a/magicblock-ledger/src/database/rocks_db.rs +++ b/magicblock-ledger/src/database/rocks_db.rs @@ -121,6 +121,16 @@ impl Rocks { Ok(()) } + pub fn delete_range_cf>( + &self, + cf: &ColumnFamily, + from: K, + to: K, + ) -> LedgerResult<()> { + self.db.delete_range_cf(cf, from, to)?; + Ok(()) + } + /// Delete files whose slot range is within \[`from`, `to`\]. pub fn delete_file_in_range_cf( &self, diff --git a/magicblock-ledger/src/ledger_truncator.rs b/magicblock-ledger/src/ledger_truncator.rs index 98e7e72e1..6358040b0 100644 --- a/magicblock-ledger/src/ledger_truncator.rs +++ b/magicblock-ledger/src/ledger_truncator.rs @@ -1,6 +1,5 @@ use std::{ cmp::min, - ops::ControlFlow, sync::Arc, thread::{self, JoinHandle}, time::Duration, @@ -48,9 +47,6 @@ impl LedgerTrunctationWorker { } pub async fn run(self) { - self.ledger - .initialize_lowest_cleanup_slot() - .expect("Lowest cleanup slot initialization"); let mut interval = interval(self.truncation_time_interval); loop { tokio::select! { @@ -102,6 +98,7 @@ impl LedgerTrunctationWorker { } /// Truncates ledger that is over desired size + /// We rely on present `CompactionFilter` to delete all data pub fn truncate_fat_ledger( &self, current_ledger_size: u64, @@ -136,7 +133,10 @@ impl LedgerTrunctationWorker { "Fat truncation: truncating up to(inclusive): {}", truncate_to_slot ); + self.ledger.set_lowest_cleanup_slot(truncate_to_slot); + Self::delete_slots(&self.ledger, 0, truncate_to_slot)?; + if let Err(err) = self.ledger.flush() { // We will still compact error!("Failed to flush: {}", err); @@ -151,6 +151,31 @@ impl LedgerTrunctationWorker { Ok(()) } + /// Inserts tombstones in slot-ordered columns for range [from; to] inclusive + /// This is a cheap operation since delete_range inserts one range tombstone + /// NOTE: this doesn't cover all the columns, we rely on CompactionFilter to clean the rest + fn delete_slots( + ledger: &Arc, + from_slot: u64, + to_slot: u64, + ) -> LedgerResult<()> { + let start = from_slot; + let end = to_slot + 1; + ledger.delete_range_cf::(start, end)?; + ledger.delete_range_cf::(start, end)?; + ledger.delete_range_cf::(start, end)?; + + // Can cheaply delete SlotSignatures as well + // NOTE: we need to clean (to_slot, u32::MAX) + // since range is exclusive at the end we use (to_slot + 1, 0) + ledger.delete_range_cf::( + (from_slot, 0), + (to_slot + 1, 0), + )?; + + Ok(()) + } + /// Returns range to truncate [from_slot, to_slot] fn estimate_truncation_range( &self, @@ -196,53 +221,27 @@ impl LedgerTrunctationWorker { /// Utility function for splitting truncation into smaller chunks /// Cleans slots [from_slot; to_slot] inclusive range + /// We rely on present `CompactionFilter` to delete all data pub fn truncate_slot_range( ledger: &Arc, from_slot: u64, to_slot: u64, cancellation_token: CancellationToken, ) { - // In order not to torture RocksDB's WriteBatch we split large tasks into chunks - const SINGLE_TRUNCATION_LIMIT: usize = 300; - if to_slot < from_slot { warn!("LedgerTruncator: Nani?"); - return; + return Ok(()); } info!( "LedgerTruncator: truncating slot range [{from_slot}; {to_slot}]" ); - let ledger_copy = ledger.clone(); - (from_slot..=to_slot) - .step_by(SINGLE_TRUNCATION_LIMIT) - .try_for_each(|cur_from_slot| { - if cancellation_token.is_cancelled() { - return ControlFlow::Break(()); - } - - let num_slots_to_truncate = min( - to_slot - cur_from_slot + 1, - SINGLE_TRUNCATION_LIMIT as u64, - ); - let truncate_to_slot = - cur_from_slot + num_slots_to_truncate - 1; - - if let Err(err) = ledger_copy - .delete_slot_range(cur_from_slot, truncate_to_slot) - { - warn!( - "Failed to truncate slots {}-{}: {}", - cur_from_slot, truncate_to_slot, err - ); - } - - ControlFlow::Continue(()) - }); + ledger.set_lowest_cleanup_slot(to_slot); + Self::delete_slots(ledger, from_slot, to_slot)?; // Flush memtables with tombstones prior to compaction - if let Err(err) = ledger_copy.flush() { + if let Err(err) = ledger.flush() { error!("Failed to flush ledger: {err}"); } Self::compact_slot_range( @@ -251,6 +250,7 @@ impl LedgerTrunctationWorker { to_slot, cancellation_token, ); + Ok(()) } /// Synchronous utility function that triggers and awaits compaction on all the columns @@ -316,7 +316,7 @@ impl LedgerTrunctationWorker { compact_cf_or_return!( ledger, cancellation_token, - (Some((from_slot, u32::MIN)), Some((to_slot + 1, u32::MAX))), + (Some((from_slot, u32::MIN)), Some((to_slot + 1, 0))), SlotSignatures ); compact_cf_or_return!( diff --git a/magicblock-ledger/src/store/api.rs b/magicblock-ledger/src/store/api.rs index b1ba01dd8..62704b26f 100644 --- a/magicblock-ledger/src/store/api.rs +++ b/magicblock-ledger/src/store/api.rs @@ -174,6 +174,7 @@ impl Ledger { let (slot, blockhash) = ledger.get_max_blockhash()?; let time = ledger.get_block_time(slot)?.unwrap_or_default(); ledger.latest_block.store(slot, blockhash, time); + ledger.initialize_lowest_cleanup_slot()?; Ok(ledger) } @@ -1216,6 +1217,14 @@ impl Ledger { } } + pub fn delete_range_cf( + &self, + from: C::Index, + to: C::Index, + ) -> LedgerResult<()> { + self.db.column::().delete_range(from, to) + } + /// Permanently removes ledger data for slots in the inclusive range `[from_slot, to_slot]`. /// # Note: /// - This is a destructive operation that cannot be undone diff --git a/magicblock-metrics/src/metrics/mod.rs b/magicblock-metrics/src/metrics/mod.rs index 52e607578..0f33a66ae 100644 --- a/magicblock-metrics/src/metrics/mod.rs +++ b/magicblock-metrics/src/metrics/mod.rs @@ -99,6 +99,15 @@ lazy_static::lazy_static! { SECONDS_1_9.iter()).cloned().collect() ), ).unwrap(); + pub static ref LEDGER_TRUNCATOR_COMPACTION_SECONDS: Histogram = Histogram::with_opts( + HistogramOpts::new( + "ledger_truncator_compaction_seconds", + "Time taken to compact rocksdb columns" + ) + .buckets( + vec![10.0, 30.0, (30 * 60) as f64, (60 * 60) as f64, (3 * 60 * 60) as f64 ] + ), + ).unwrap(); // ----------------- // Accounts @@ -356,6 +365,7 @@ pub(crate) fn register() { register!(LEDGER_PERF_SAMPLES_GAUGE); register!(LEDGER_ACCOUNT_MOD_DATA_GAUGE); register!(LEDGER_COLUMNS_COUNT_DURATION_SECONDS); + register!(LEDGER_TRUNCATOR_COMPACTION_SECONDS); register!(ACCOUNTS_SIZE_GAUGE); register!(ACCOUNTS_COUNT_GAUGE); register!(PENDING_ACCOUNT_CLONES_GAUGE); @@ -454,6 +464,10 @@ where LEDGER_COLUMNS_COUNT_DURATION_SECONDS.observe_closure_duration(f) } +pub fn start_ledger_truncator_compaction_timer() -> HistogramTimer { + LEDGER_TRUNCATOR_COMPACTION_SECONDS.start_timer() +} + pub fn set_accounts_size(value: i64) { ACCOUNTS_SIZE_GAUGE.set(value) } From 0c5d020431c72604a506c4d135d72ab6541a9a35 Mon Sep 17 00:00:00 2001 From: taco-paco Date: Fri, 28 Nov 2025 18:33:06 +0700 Subject: [PATCH 2/8] fix: compilation --- magicblock-ledger/src/ledger_truncator.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/magicblock-ledger/src/ledger_truncator.rs b/magicblock-ledger/src/ledger_truncator.rs index 6358040b0..2c95df656 100644 --- a/magicblock-ledger/src/ledger_truncator.rs +++ b/magicblock-ledger/src/ledger_truncator.rs @@ -89,7 +89,7 @@ impl LedgerTrunctationWorker { from_slot, to_slot, self.cancellation_token.clone(), - ), + )?, None => warn!("Could not estimate truncation range"), } } @@ -227,7 +227,7 @@ impl LedgerTrunctationWorker { from_slot: u64, to_slot: u64, cancellation_token: CancellationToken, - ) { + ) -> LedgerResult<()> { if to_slot < from_slot { warn!("LedgerTruncator: Nani?"); return Ok(()); From 14a42516d7b727159ff1018b4376acaf2abd4137 Mon Sep 17 00:00:00 2001 From: taco-paco Date: Fri, 28 Nov 2025 18:50:21 +0700 Subject: [PATCH 3/8] feat: added metrics --- Cargo.lock | 1 + magicblock-ledger/Cargo.toml | 1 + magicblock-ledger/src/ledger_truncator.rs | 37 ++++++++++++++--------- magicblock-metrics/src/metrics/mod.rs | 15 +++++++++ test-integration/Cargo.lock | 1 + 5 files changed, 41 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7ff85249b..767231e51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3851,6 +3851,7 @@ dependencies = [ "log", "magicblock-accounts-db", "magicblock-core", + "magicblock-metrics", "num-format", "num_cpus", "prost 0.11.9", diff --git a/magicblock-ledger/Cargo.toml b/magicblock-ledger/Cargo.toml index c2302637b..a704ee296 100644 --- a/magicblock-ledger/Cargo.toml +++ b/magicblock-ledger/Cargo.toml @@ -20,6 +20,7 @@ prost = { workspace = true } serde = { workspace = true } magicblock-accounts-db = { workspace = true } magicblock-core = { workspace = true } +magicblock-metrics = { workspace = true } scc = { workspace = true } solana-account-decoder = { workspace = true } solana-measure = { workspace = true } diff --git a/magicblock-ledger/src/ledger_truncator.rs b/magicblock-ledger/src/ledger_truncator.rs index 2c95df656..562051a3d 100644 --- a/magicblock-ledger/src/ledger_truncator.rs +++ b/magicblock-ledger/src/ledger_truncator.rs @@ -6,6 +6,10 @@ use std::{ }; use log::{error, info, warn}; +use magicblock_metrics::metrics::{ + observe_ledger_truncator_delete, start_ledger_truncator_compaction_timer, + HistogramTimer, +}; use solana_measure::measure::Measure; use tokio::{runtime::Builder, time::interval}; use tokio_util::sync::CancellationToken; @@ -159,21 +163,23 @@ impl LedgerTrunctationWorker { from_slot: u64, to_slot: u64, ) -> LedgerResult<()> { - let start = from_slot; - let end = to_slot + 1; - ledger.delete_range_cf::(start, end)?; - ledger.delete_range_cf::(start, end)?; - ledger.delete_range_cf::(start, end)?; - - // Can cheaply delete SlotSignatures as well - // NOTE: we need to clean (to_slot, u32::MAX) - // since range is exclusive at the end we use (to_slot + 1, 0) - ledger.delete_range_cf::( - (from_slot, 0), - (to_slot + 1, 0), - )?; + observe_ledger_truncator_delete(|| { + let start = from_slot; + let end = to_slot + 1; + ledger.delete_range_cf::(start, end)?; + ledger.delete_range_cf::(start, end)?; + ledger.delete_range_cf::(start, end)?; + + // Can cheaply delete SlotSignatures as well + // NOTE: we need to clean (to_slot, u32::MAX) + // since range is exclusive at the end we use (to_slot + 1, 0) + ledger.delete_range_cf::( + (from_slot, 0), + (to_slot + 1, 0), + )?; - Ok(()) + Ok(()) + }) } /// Returns range to truncate [from_slot, to_slot] @@ -278,16 +284,19 @@ impl LedgerTrunctationWorker { struct CompactionMeasure { measure: Measure, + _histogram_timer: HistogramTimer, } impl Drop for CompactionMeasure { fn drop(&mut self) { self.measure.stop(); + // histogram_timer - records on HistogramTimer::drop info!("Manual compaction took: {}", self.measure); } } let _measure = CompactionMeasure { measure: Measure::start("Manual compaction"), + _histogram_timer: start_ledger_truncator_compaction_timer(), }; let start = from_slot; diff --git a/magicblock-metrics/src/metrics/mod.rs b/magicblock-metrics/src/metrics/mod.rs index 0f33a66ae..75330d74a 100644 --- a/magicblock-metrics/src/metrics/mod.rs +++ b/magicblock-metrics/src/metrics/mod.rs @@ -108,6 +108,16 @@ lazy_static::lazy_static! { vec![10.0, 30.0, (30 * 60) as f64, (60 * 60) as f64, (3 * 60 * 60) as f64 ] ), ).unwrap(); + pub static ref LEDGER_TRUNCATOR_DELETE_SECONDS: Histogram = Histogram::with_opts( + HistogramOpts::new( + "ledger_truncator_delete_seconds", + "Time taken to compact rocksdb columns" + ) + .buckets( + vec![0.1, 1.0, 5.0, 10.0, 30.0] + ), + ).unwrap(); + // ----------------- // Accounts @@ -366,6 +376,7 @@ pub(crate) fn register() { register!(LEDGER_ACCOUNT_MOD_DATA_GAUGE); register!(LEDGER_COLUMNS_COUNT_DURATION_SECONDS); register!(LEDGER_TRUNCATOR_COMPACTION_SECONDS); + register!(LEDGER_TRUNCATOR_DELETE_SECONDS); register!(ACCOUNTS_SIZE_GAUGE); register!(ACCOUNTS_COUNT_GAUGE); register!(PENDING_ACCOUNT_CLONES_GAUGE); @@ -468,6 +479,10 @@ pub fn start_ledger_truncator_compaction_timer() -> HistogramTimer { LEDGER_TRUNCATOR_COMPACTION_SECONDS.start_timer() } +pub fn observe_ledger_truncator_delete T>(f: F) -> T { + LEDGER_TRUNCATOR_DELETE_SECONDS.observe_closure_duration(f) +} + pub fn set_accounts_size(value: i64) { ACCOUNTS_SIZE_GAUGE.set(value) } diff --git a/test-integration/Cargo.lock b/test-integration/Cargo.lock index ab8729f0c..d6f6415c6 100644 --- a/test-integration/Cargo.lock +++ b/test-integration/Cargo.lock @@ -3783,6 +3783,7 @@ dependencies = [ "log", "magicblock-accounts-db", "magicblock-core", + "magicblock-metrics", "num-format", "num_cpus", "prost", From ef3b739889c66c7d3a8a064df2393112eb8d60b8 Mon Sep 17 00:00:00 2001 From: taco-paco Date: Fri, 28 Nov 2025 18:56:23 +0700 Subject: [PATCH 4/8] refactor: remove unused code --- magicblock-ledger/src/store/api.rs | 108 ----------------------------- 1 file changed, 108 deletions(-) diff --git a/magicblock-ledger/src/store/api.rs b/magicblock-ledger/src/store/api.rs index 62704b26f..b5af9b874 100644 --- a/magicblock-ledger/src/store/api.rs +++ b/magicblock-ledger/src/store/api.rs @@ -1225,114 +1225,6 @@ impl Ledger { self.db.column::().delete_range(from, to) } - /// Permanently removes ledger data for slots in the inclusive range `[from_slot, to_slot]`. - /// # Note: - /// - This is a destructive operation that cannot be undone - /// - Requires exclusive access to the lowest cleanup slot tracker - /// - All deletions are atomic (either all succeed or none do) - pub fn delete_slot_range( - &self, - from_slot: Slot, - to_slot: Slot, - ) -> LedgerResult<()> { - self.set_lowest_cleanup_slot(to_slot); - - let mut batch = self.db.batch(); - let num_deleted_slots = to_slot + 1 - from_slot; - self.blocktime_cf.delete_range_in_batch( - &mut batch, - from_slot, - to_slot + 1, - ); - self.blockhash_cf.delete_range_in_batch( - &mut batch, - from_slot, - to_slot + 1, - ); - self.perf_samples_cf.delete_range_in_batch( - &mut batch, - from_slot, - to_slot + 1, - ); - - let mut slot_signatures_deleted = 0; - let mut transaction_status_deleted = 0; - let mut transactions_deleted = 0; - let mut transaction_memos_deleted = 0; - let mut address_signatures_deleted = 0; - self.slot_signatures_cf - .iter(IteratorMode::From( - (from_slot, u32::MIN), - IteratorDirection::Forward, - ))? - .take_while(|((slot, _), _)| slot <= &to_slot) - .try_for_each(|((slot, transaction_index), raw_signature)| { - self.slot_signatures_cf - .delete_in_batch(&mut batch, (slot, transaction_index)); - slot_signatures_deleted += 1; - - let signature = Signature::try_from(raw_signature.as_ref())?; - self.transaction_status_cf - .delete_in_batch(&mut batch, (signature, slot)); - transaction_status_deleted += 1; - - self.transaction_cf - .delete_in_batch(&mut batch, (signature, slot)); - transactions_deleted += 1; - - self.transaction_memos_cf - .delete_in_batch(&mut batch, (signature, slot)); - transaction_memos_deleted += 1; - - let transaction = self - .transaction_cf - .get_protobuf((signature, slot))? - .map(VersionedTransaction::from) - .ok_or(LedgerError::TransactionNotFound)?; - - transaction.message.static_account_keys().iter().for_each( - |address| { - self.address_signatures_cf.delete_in_batch( - &mut batch, - (*address, slot, transaction_index, signature), - ); - address_signatures_deleted += 1; - }, - ); - - // TODO(edwin): add AccountModData cleanup - Ok::<_, LedgerError>(()) - })?; - - self.db.write(batch)?; - - self.blocktime_cf - .try_decrease_entry_counter(num_deleted_slots); - self.blockhash_cf - .try_decrease_entry_counter(num_deleted_slots); - self.perf_samples_cf - .try_decrease_entry_counter(num_deleted_slots); - self.slot_signatures_cf - .try_decrease_entry_counter(slot_signatures_deleted); - self.transaction_status_cf - .try_decrease_entry_counter(transaction_status_deleted); - self.transaction_cf - .try_decrease_entry_counter(transactions_deleted); - self.transaction_memos_cf - .try_decrease_entry_counter(transaction_memos_deleted); - self.address_signatures_cf - .try_decrease_entry_counter(address_signatures_deleted); - - // To not spend time querying DB for value we set drop the counter - // This shouldn't happen very often due to rarity of actual truncations. - self.transaction_successful_status_count - .store(DIRTY_COUNT, Ordering::Release); - self.transaction_failed_status_count - .store(DIRTY_COUNT, Ordering::Release); - - Ok(()) - } - pub fn compact_slot_range_cf( &self, from: Option, From 83abe2cc5a2906752cd521c7cac7b638562752b8 Mon Sep 17 00:00:00 2001 From: taco-paco Date: Mon, 1 Dec 2025 12:28:49 +0700 Subject: [PATCH 5/8] feat: add HasColumn trait + impls --- magicblock-ledger/src/store/api.rs | 37 +++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/magicblock-ledger/src/store/api.rs b/magicblock-ledger/src/store/api.rs index b5af9b874..552d98752 100644 --- a/magicblock-ledger/src/store/api.rs +++ b/magicblock-ledger/src/store/api.rs @@ -1217,12 +1217,16 @@ impl Ledger { } } - pub fn delete_range_cf( + pub fn delete_range_cf( &self, from: C::Index, to: C::Index, - ) -> LedgerResult<()> { - self.db.column::().delete_range(from, to) + ) -> LedgerResult<()> + where + C: Column + ColumnName, + Self: HasColumn, + { + >::column(self).delete_range(from, to) } pub fn compact_slot_range_cf( @@ -1274,6 +1278,33 @@ impl Ledger { } } +pub trait HasColumn +where + C: Column + ColumnName, +{ + fn column(&self) -> &LedgerColumn; +} + +macro_rules! impl_has_column { + ($cf_ty:ident, $field:ident) => { + impl HasColumn for Ledger { + fn column(&self) -> &LedgerColumn { + &self.$field + } + } + }; +} + +impl_has_column!(TransactionStatus, transaction_status_cf); +impl_has_column!(AddressSignatures, address_signatures_cf); +impl_has_column!(SlotSignatures, slot_signatures_cf); +impl_has_column!(Blocktime, blocktime_cf); +impl_has_column!(Blockhash, blockhash_cf); +impl_has_column!(Transaction, transaction_cf); +impl_has_column!(TransactionMemos, transaction_memos_cf); +impl_has_column!(PerfSamples, perf_samples_cf); +impl_has_column!(AccountModDatas, account_mod_datas_cf); + // ----------------- // Tests // ----------------- From 9c1b17c336e1d0e8d81ef5167888cefe75334d37 Mon Sep 17 00:00:00 2001 From: taco-paco Date: Mon, 1 Dec 2025 13:15:22 +0700 Subject: [PATCH 6/8] feat: reset counters for unknown columns --- magicblock-ledger/src/ledger_truncator.rs | 52 +++++++++++++++++++++-- magicblock-ledger/src/store/api.rs | 6 +++ 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/magicblock-ledger/src/ledger_truncator.rs b/magicblock-ledger/src/ledger_truncator.rs index 562051a3d..a3317918a 100644 --- a/magicblock-ledger/src/ledger_truncator.rs +++ b/magicblock-ledger/src/ledger_truncator.rs @@ -1,6 +1,6 @@ use std::{ cmp::min, - sync::Arc, + sync::{atomic::Ordering, Arc}, thread::{self, JoinHandle}, time::Duration, }; @@ -15,11 +15,15 @@ use tokio::{runtime::Builder, time::interval}; use tokio_util::sync::CancellationToken; use crate::{ - database::columns::{ - AddressSignatures, Blockhash, Blocktime, PerfSamples, SlotSignatures, - Transaction, TransactionMemos, TransactionStatus, + database::{ + columns, + columns::{ + AddressSignatures, Blockhash, Blocktime, PerfSamples, + SlotSignatures, Transaction, TransactionMemos, TransactionStatus, + }, }, errors::LedgerResult, + store::api::HasColumn, Ledger, }; @@ -167,8 +171,28 @@ impl LedgerTrunctationWorker { let start = from_slot; let end = to_slot + 1; ledger.delete_range_cf::(start, end)?; + >::with_column( + ledger.as_ref(), + |column| { + column.try_decrease_entry_counter(end - start); + }, + ); + ledger.delete_range_cf::(start, end)?; + >::with_column( + ledger.as_ref(), + |column| { + column.try_decrease_entry_counter(end - start); + }, + ); + ledger.delete_range_cf::(start, end)?; + >::with_column( + ledger.as_ref(), + |column| { + column.try_decrease_entry_counter(end - start); + }, + ); // Can cheaply delete SlotSignatures as well // NOTE: we need to clean (to_slot, u32::MAX) @@ -178,6 +202,26 @@ impl LedgerTrunctationWorker { (to_slot + 1, 0), )?; + // Reset counters for other columns + // where we can't know how many el-ts gets deleted + macro_rules! reset_entry_counter { + ($ledger:expr, $cf_ty:ty $(,)?) => { + >::with_column( + $ledger.as_ref(), + |column| { + column + .entry_counter + .store(columns::DIRTY_COUNT, Ordering::Relaxed); + }, + ); + }; + } + reset_entry_counter!(ledger, SlotSignatures); + reset_entry_counter!(ledger, TransactionStatus); + reset_entry_counter!(ledger, AddressSignatures); + reset_entry_counter!(ledger, Transaction); + reset_entry_counter!(ledger, TransactionMemos); + Ok(()) }) } diff --git a/magicblock-ledger/src/store/api.rs b/magicblock-ledger/src/store/api.rs index 552d98752..eac754f9f 100644 --- a/magicblock-ledger/src/store/api.rs +++ b/magicblock-ledger/src/store/api.rs @@ -1283,6 +1283,12 @@ where C: Column + ColumnName, { fn column(&self) -> &LedgerColumn; + fn with_column(&self, f: F) -> R + where + F: FnOnce(&LedgerColumn) -> R, + { + f(self.column()) + } } macro_rules! impl_has_column { From c4ec184e57ecc41c2907b22c6f353298bc3693cb Mon Sep 17 00:00:00 2001 From: taco-paco Date: Mon, 1 Dec 2025 13:30:50 +0700 Subject: [PATCH 7/8] wip: remove outdated tests --- magicblock-ledger/src/store/api.rs | 86 ------------------------------ 1 file changed, 86 deletions(-) diff --git a/magicblock-ledger/src/store/api.rs b/magicblock-ledger/src/store/api.rs index eac754f9f..00a762c99 100644 --- a/magicblock-ledger/src/store/api.rs +++ b/magicblock-ledger/src/store/api.rs @@ -2412,90 +2412,4 @@ mod tests { assert_eq!(sig_info_dos.memo, Some("Test Dos Memo".to_string())); } } - - #[test] - fn test_truncate_slots() { - let ledger_path = get_tmp_ledger_path_auto_delete!(); - let store = Ledger::open(ledger_path.path()).unwrap(); - - // Create test data - let slots_to_delete = [10, 15]; - let slots_to_preserve = [20]; - let test_data: Vec<_> = slots_to_delete - .iter() - .chain(slots_to_preserve.iter()) - .map(|&slot| { - let sig = Signature::new_unique(); - let (tx, sanitized) = - create_confirmed_transaction(slot, 5, Some(100), None); - (sig, slot, tx, sanitized) - }) - .collect(); - - // Write data to ledger - test_data.iter().for_each(|(sig, slot, tx, sanitized)| { - store - .write_transaction( - *sig, - *slot, - sanitized.clone(), - tx.tx_with_meta.get_status_meta().unwrap(), - ) - .unwrap(); - store.write_block(*slot, 100, Hash::new_unique()).unwrap(); - store - .write_transaction_memos( - sig, - *slot, - format!("Memo for slot {}", slot), - ) - .unwrap(); - }); - - // Truncate slots 10-15 (should remove first two entries) - assert!(store - .delete_slot_range( - *slots_to_delete.first().unwrap(), - *slots_to_delete.last().unwrap() - ) - .is_ok()); - - // Consistency checks - let (to_delete, to_preserve) = - test_data.split_at(slots_to_delete.len()); - to_delete.iter().for_each(|(sig, slot, _, _)| { - assert!(store - .transaction_cf - .get_protobuf((*sig, *slot)) - .unwrap() - .is_none()); - assert!(store - .transaction_status_cf - .get_protobuf((*sig, *slot)) - .unwrap() - .is_none()); - assert!(store.blocktime_cf.get(*slot).unwrap().is_none()); - assert!(store - .read_transaction_memos(*sig, *slot) - .unwrap() - .is_none()); - }); - to_preserve.iter().for_each(|(sig, slot, _, _)| { - assert!(store - .transaction_cf - .get_protobuf((*sig, *slot)) - .unwrap() - .is_some()); - assert!(store - .transaction_status_cf - .get_protobuf((*sig, *slot)) - .unwrap() - .is_some()); - assert!(store.blocktime_cf.get(*slot).unwrap().is_some()); - assert_eq!( - store.read_transaction_memos(*sig, *slot).unwrap(), - Some(format!("Memo for slot {}", slot)) - ); - }); - } } From 1618b75ab332e44b1cf0b719231c94e87a851eb9 Mon Sep 17 00:00:00 2001 From: taco-paco Date: Mon, 1 Dec 2025 14:04:46 +0700 Subject: [PATCH 8/8] fix: comment --- magicblock-metrics/src/metrics/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/magicblock-metrics/src/metrics/mod.rs b/magicblock-metrics/src/metrics/mod.rs index 75330d74a..e1a872217 100644 --- a/magicblock-metrics/src/metrics/mod.rs +++ b/magicblock-metrics/src/metrics/mod.rs @@ -111,7 +111,7 @@ lazy_static::lazy_static! { pub static ref LEDGER_TRUNCATOR_DELETE_SECONDS: Histogram = Histogram::with_opts( HistogramOpts::new( "ledger_truncator_delete_seconds", - "Time taken to compact rocksdb columns" + "Time taken to delete rocksdb slot ranges" ) .buckets( vec![0.1, 1.0, 5.0, 10.0, 30.0]