github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-core-master/validator/src/state/state_pruning_manager.rs (about) 1 /* 2 * Copyright 2018 Intel Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * ------------------------------------------------------------------------------ 16 */ 17 use std::cmp::Ordering; 18 use std::collections::BinaryHeap; 19 20 use database::lmdb::LmdbDatabase; 21 use metrics; 22 use state::merkle::MerkleDatabase; 23 24 lazy_static! { 25 static ref COLLECTOR: metrics::MetricsCollectorHandle = 26 metrics::get_collector("sawtooth_validator.state"); 27 } 28 29 /// The StatePruneManager manages a collection of state root hashes that will be 30 /// prune from the MerkleDatabase at intervals. Pruning will occur by decimating 31 /// the state root hashes. I.e. ten percent (rounded down) of the state roots in 32 /// the queue will be pruned. This allows state roots to remain in the queue for 33 /// a period of time, on the chance that they are from a chain that has been 34 /// abandoned and then re-chosen as the primary chain. 35 pub struct StatePruningManager { 36 // Contains the state root hashes slated for pruning 37 state_root_prune_queue: BinaryHeap<PruneCandidate>, 38 state_database: LmdbDatabase, 39 } 40 41 #[derive(Eq, PartialEq, Debug, Ord)] 42 struct PruneCandidate(u64, String); 43 44 impl PartialOrd for PruneCandidate { 45 fn partial_cmp(&self, other: &PruneCandidate) -> Option<Ordering> { 46 Some(Ordering::reverse(self.0.cmp(&other.0))) 47 } 48 } 49 50 impl StatePruningManager { 51 pub fn new(state_database: LmdbDatabase) -> Self { 52 StatePruningManager { 53 state_root_prune_queue: BinaryHeap::new(), 54 state_database, 55 } 56 } 57 58 /// Updates the pruning queue. Abandoned roots will be added to the queue. 59 /// Added roots will be removed from the queue. This ensures that the state 60 /// roots won't be removed, regardless of the chain state. 61 pub fn update_queue(&mut self, added_roots: &[&str], abandoned_roots: &[(u64, &str)]) { 62 // add the roots that have been abandoned. 63 for (height, state_root_hash) in abandoned_roots { 64 self.add_to_queue(*height, state_root_hash); 65 } 66 // Remove any state root hashes from the pruning queue that we may have switched 67 // back too from an alternate chain 68 let mut new_queue = BinaryHeap::with_capacity(0); 69 ::std::mem::swap(&mut self.state_root_prune_queue, &mut new_queue); 70 self.state_root_prune_queue = new_queue 71 .into_iter() 72 .filter(|candidate| { 73 if !added_roots.contains(&candidate.1.as_str()) { 74 true 75 } else { 76 debug!("Removing {} from pruning queue", candidate.1); 77 false 78 } 79 }) 80 .collect(); 81 } 82 83 /// Add a single state root to the pruning queue. 84 pub fn add_to_queue(&mut self, height: u64, state_root_hash: &str) { 85 let state_root_hash = state_root_hash.into(); 86 if !self.state_root_prune_queue 87 .iter() 88 .any(|candidate| candidate.1 == state_root_hash) 89 { 90 debug!("Adding {} to pruning queue", state_root_hash); 91 self.state_root_prune_queue 92 .push(PruneCandidate(height, state_root_hash)); 93 } 94 } 95 96 /// Executes prune on any state root hash at or below the given depth. 97 pub fn execute(&mut self, at_depth: u64) { 98 let mut prune_candidates = vec![]; 99 100 loop { 101 if let Some(candidate) = self.state_root_prune_queue.pop() { 102 if candidate.0 <= at_depth { 103 prune_candidates.push(candidate); 104 } else { 105 self.state_root_prune_queue.push(candidate); 106 break; 107 } 108 } else { 109 break; 110 } 111 } 112 113 let mut total_pruned_entries: usize = 0; 114 for candidate in prune_candidates { 115 match MerkleDatabase::prune(&self.state_database, &candidate.1) { 116 Ok(removed_keys) => { 117 total_pruned_entries += removed_keys.len(); 118 119 // the state root was not pruned (it is likely the root of a 120 // fork), so push it back into the queue. 121 if removed_keys.is_empty() { 122 self.state_root_prune_queue.push(candidate); 123 } else { 124 let mut state_roots_pruned_count = 125 COLLECTOR.counter("StatePruneManager.state_roots_pruned", None, None); 126 state_roots_pruned_count.inc(); 127 } 128 } 129 Err(err) => { 130 error!("Unable to prune state root {}: {:?}", candidate.1, err); 131 self.state_root_prune_queue.push(candidate); 132 } 133 } 134 } 135 136 if total_pruned_entries > 0 { 137 info!( 138 "Pruned {} keys from the Global state Database", 139 total_pruned_entries 140 ); 141 } 142 } 143 } 144 145 #[cfg(test)] 146 mod tests { 147 use super::*; 148 149 #[test] 150 fn ordering_candidates() { 151 let mut heap = ::std::collections::BinaryHeap::new(); 152 153 heap.push(PruneCandidate(2, "two".into())); 154 heap.push(PruneCandidate(3, "three".into())); 155 heap.push(PruneCandidate(3, "another_three".into())); 156 heap.push(PruneCandidate(4, "four".into())); 157 158 assert_eq!(heap.pop(), Some(PruneCandidate(2, "two".into()))); 159 assert_eq!(heap.pop(), Some(PruneCandidate(3, "another_three".into()))); 160 assert_eq!(heap.pop(), Some(PruneCandidate(3, "three".into()))); 161 assert_eq!(heap.pop(), Some(PruneCandidate(4, "four".into()))); 162 assert_eq!(heap.pop(), None); 163 } 164 }