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  }