-
Notifications
You must be signed in to change notification settings - Fork 5.7k
Fix the boundary inconsistency between delete_file_in_range and delete_range #27201
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -145,8 +145,8 @@ impl Blockstore { | |
| self.run_purge_with_stats(from_slot, to_slot, purge_type, &mut PurgeStats::default()) | ||
| } | ||
|
|
||
| /// A helper function to `purge_slots` that executes the ledger clean up | ||
| /// from `from_slot` to `to_slot`. | ||
| /// A helper function to `purge_slots` that executes the ledger clean up. | ||
| /// The cleanup applies to \[`from_slot`, `to_slot`\]. | ||
| /// | ||
| /// When `from_slot` is 0, any sst-file with a key-range completely older | ||
| /// than `to_slot` will also be deleted. | ||
|
|
@@ -161,9 +161,6 @@ impl Blockstore { | |
| .db | ||
| .batch() | ||
| .expect("Database Error: Failed to get write batch"); | ||
| // delete range cf is not inclusive | ||
| let to_slot = to_slot.saturating_add(1); | ||
|
|
||
| let mut delete_range_timer = Measure::start("delete_range"); | ||
| let mut columns_purged = self | ||
| .db | ||
|
|
@@ -444,15 +441,18 @@ impl Blockstore { | |
| /// deserializing each slot being purged and iterating through all | ||
| /// transactions to determine the keys of individual records. | ||
| /// | ||
| /// The purge range applies to \[`from_slot`, `to_slot`\]. | ||
| /// | ||
| /// **This method is very slow.** | ||
| fn purge_special_columns_exact( | ||
| &self, | ||
| batch: &mut WriteBatch, | ||
| from_slot: Slot, | ||
| to_slot: Slot, // Exclusive | ||
| to_slot: Slot, | ||
| ) -> Result<()> { | ||
| let mut index0 = self.transaction_status_index_cf.get(0)?.unwrap_or_default(); | ||
| let mut index1 = self.transaction_status_index_cf.get(1)?.unwrap_or_default(); | ||
| let to_slot = to_slot.saturating_add(1); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: there's more rusty way: for slot in from_slot..=to_slot {
...
}
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for spotting this. Will have a quick fix for this. |
||
| for slot in from_slot..to_slot { | ||
| let slot_entries = self.get_any_valid_slot_entries(slot, 0); | ||
| let transactions = slot_entries | ||
|
|
@@ -501,22 +501,18 @@ impl Blockstore { | |
| if let Some(purged_index) = self.toggle_transaction_status_index( | ||
| write_batch, | ||
| w_active_transaction_status_index, | ||
| to_slot, | ||
| to_slot + 1, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. like this
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm in the process of revisiting deletion- and ledger-cleanup-related code and adding missing tests if any. Just want to leave a comment here that this has been covered by the existing check. Below is the test failure log if I remove |
||
| )? { | ||
| *columns_purged &= self | ||
| .db | ||
| .delete_range_cf::<cf::TransactionStatus>( | ||
| write_batch, | ||
| purged_index, | ||
| purged_index + 1, | ||
| ) | ||
| .delete_range_cf::<cf::TransactionStatus>(write_batch, purged_index, purged_index) | ||
| .is_ok() | ||
| & self | ||
| .db | ||
| .delete_range_cf::<cf::AddressSignatures>( | ||
| write_batch, | ||
| purged_index, | ||
| purged_index + 1, | ||
| purged_index, | ||
| ) | ||
| .is_ok(); | ||
| } | ||
|
|
@@ -783,7 +779,7 @@ pub mod tests { | |
| ); | ||
| } | ||
|
|
||
| fn clear_and_repopulate_transaction_statuses( | ||
| fn clear_and_repopulate_transaction_statuses_for_test( | ||
| blockstore: &mut Blockstore, | ||
| index0_max_slot: u64, | ||
| index1_max_slot: u64, | ||
|
|
@@ -795,11 +791,11 @@ pub mod tests { | |
| .unwrap(); | ||
| blockstore | ||
| .db | ||
| .delete_range_cf::<cf::TransactionStatus>(&mut write_batch, 0, 3) | ||
| .delete_range_cf::<cf::TransactionStatus>(&mut write_batch, 0, 2) | ||
| .unwrap(); | ||
| blockstore | ||
| .db | ||
| .delete_range_cf::<cf::TransactionStatusIndex>(&mut write_batch, 0, 3) | ||
| .delete_range_cf::<cf::TransactionStatusIndex>(&mut write_batch, 0, 2) | ||
| .unwrap(); | ||
| blockstore.db.write(write_batch).unwrap(); | ||
| blockstore.initialize_transaction_status_index().unwrap(); | ||
|
|
@@ -897,7 +893,7 @@ pub mod tests { | |
| let index1_max_slot = 19; | ||
|
|
||
| // Test purge outside bounds | ||
| clear_and_repopulate_transaction_statuses( | ||
| clear_and_repopulate_transaction_statuses_for_test( | ||
| &mut blockstore, | ||
| index0_max_slot, | ||
| index1_max_slot, | ||
|
|
@@ -944,7 +940,7 @@ pub mod tests { | |
| drop(status_entry_iterator); | ||
|
|
||
| // Test purge inside index 0 | ||
| clear_and_repopulate_transaction_statuses( | ||
| clear_and_repopulate_transaction_statuses_for_test( | ||
| &mut blockstore, | ||
| index0_max_slot, | ||
| index1_max_slot, | ||
|
|
@@ -993,7 +989,7 @@ pub mod tests { | |
| drop(status_entry_iterator); | ||
|
|
||
| // Test purge inside index 0 at upper boundary | ||
| clear_and_repopulate_transaction_statuses( | ||
| clear_and_repopulate_transaction_statuses_for_test( | ||
| &mut blockstore, | ||
| index0_max_slot, | ||
| index1_max_slot, | ||
|
|
@@ -1044,7 +1040,7 @@ pub mod tests { | |
| drop(status_entry_iterator); | ||
|
|
||
| // Test purge inside index 1 at lower boundary | ||
| clear_and_repopulate_transaction_statuses( | ||
| clear_and_repopulate_transaction_statuses_for_test( | ||
| &mut blockstore, | ||
| index0_max_slot, | ||
| index1_max_slot, | ||
|
|
@@ -1092,7 +1088,7 @@ pub mod tests { | |
| drop(status_entry_iterator); | ||
|
|
||
| // Test purge across index boundaries | ||
| clear_and_repopulate_transaction_statuses( | ||
| clear_and_repopulate_transaction_statuses_for_test( | ||
| &mut blockstore, | ||
| index0_max_slot, | ||
| index1_max_slot, | ||
|
|
@@ -1142,7 +1138,7 @@ pub mod tests { | |
| drop(status_entry_iterator); | ||
|
|
||
| // Test purge include complete index 1 | ||
| clear_and_repopulate_transaction_statuses( | ||
| clear_and_repopulate_transaction_statuses_for_test( | ||
| &mut blockstore, | ||
| index0_max_slot, | ||
| index1_max_slot, | ||
|
|
@@ -1189,7 +1185,7 @@ pub mod tests { | |
| drop(status_entry_iterator); | ||
|
|
||
| // Test purge all | ||
| clear_and_repopulate_transaction_statuses( | ||
| clear_and_repopulate_transaction_statuses_for_test( | ||
| &mut blockstore, | ||
| index0_max_slot, | ||
| index1_max_slot, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -455,6 +455,7 @@ impl Rocks { | |
| Ok(()) | ||
| } | ||
|
|
||
| /// Delete files whose slot range is within \[`from`, `to`\]. | ||
| fn delete_file_in_range_cf( | ||
| &self, | ||
| cf: &ColumnFamily, | ||
|
|
@@ -1119,17 +1120,23 @@ impl Database { | |
| Ok(fs_extra::dir::get_size(&self.path)?) | ||
| } | ||
|
|
||
| // Adds a range to delete to the given write batch | ||
| /// Adds a \[`from`, `to`\] range to delete to the given write batch | ||
| pub fn delete_range_cf<C>(&self, batch: &mut WriteBatch, from: Slot, to: Slot) -> Result<()> | ||
| where | ||
| C: Column + ColumnName, | ||
| { | ||
| let cf = self.cf_handle::<C>(); | ||
| // Note that the default behavior of rocksdb's delete_range_cf deletes | ||
| // files within [from, to), while our purge logic applies to [from, to]. | ||
| // | ||
| // For consistency, we make our delete_range_cf works for [from, to] by | ||
| // adjusting the `to` slot range by 1. | ||
| let from_index = C::as_index(from); | ||
| let to_index = C::as_index(to); | ||
| let to_index = C::as_index(to.saturating_add(1)); | ||
| batch.delete_range_cf::<C>(cf, from_index, to_index) | ||
| } | ||
|
|
||
| /// Delete files whose slot range is within \[`from`, `to`\]. | ||
| pub fn delete_file_in_range_cf<C>(&self, from: Slot, to: Slot) -> Result<()> | ||
| where | ||
| C: Column + ColumnName, | ||
|
|
@@ -1439,11 +1446,17 @@ impl<'a> WriteBatch<'a> { | |
| self.map[C::NAME] | ||
| } | ||
|
|
||
| pub fn delete_range_cf<C: Column>( | ||
| /// Adds a \[`from`, `to`) range deletion entry to the batch. | ||
| /// | ||
| /// Note that the \[`from`, `to`) deletion range of WriteBatch::delete_range_cf | ||
| /// is different from \[`from`, `to`\] of Database::delete_range_cf as we makes | ||
| /// the semantics of Database::delete_range_cf matches the blockstore purge | ||
| /// logic. | ||
| fn delete_range_cf<C: Column>( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @yhchiang-sol I'm very happy about our new consistent interval handling with that said, I think we should put the range manipulation code as deep as possible for encapsulation. so, i think this non- Then, we can remove these rather extra justification comment about different semantics put at the docstring in is there strong reason we're adjusting the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The type here is C::Index, which could be u64, (u64, u64), Pubkey, Signature, (u64, Signature, Slot), and (u64, Pubkey, Slot, Signature). We will need to implement the C::saturating_add for each of them. Might be worth-trying I think, although some types might be tricky. If we want to move lower to the rocksdb delete_range_cf, then is more difficult to perform +1 as it takes arbitrary byte array. A cleaner solution is to make RocksDB's range delete optionally perform the inclusive deletion based on the WriteOptions where we will add a new boolean indicating inclusive deletion, but I guess this would take a much longer route as we need to carry this information into range-deletion key format and update the internal range-deletion logic to honor this. So probably good for now I think. |
||
| &mut self, | ||
| cf: &ColumnFamily, | ||
| from: C::Index, | ||
| to: C::Index, | ||
| to: C::Index, // exclusive | ||
| ) -> Result<()> { | ||
| self.write_batch | ||
| .delete_range_cf(cf, C::key(from), C::key(to)); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.