1414 * limitations under the License.
1515 * ------------------------------------------------------------------------------
1616 */
17- use std:: collections:: VecDeque ;
17+ use std:: cmp:: Ordering ;
18+ use std:: collections:: BinaryHeap ;
1819
1920use database:: lmdb:: LmdbDatabase ;
2021use metrics;
@@ -33,76 +34,105 @@ lazy_static! {
3334/// abandoned and then re-chosen as the primary chain.
3435pub struct StatePruningManager {
3536 // Contains the state root hashes slated for pruning
36- state_root_prune_queue : VecDeque < String > ,
37+ state_root_prune_queue : BinaryHeap < PruneCandidate > ,
3738 state_database : LmdbDatabase ,
3839}
3940
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+
4050impl StatePruningManager {
4151 pub fn new ( state_database : LmdbDatabase ) -> Self {
4252 StatePruningManager {
43- state_root_prune_queue : VecDeque :: new ( ) ,
53+ state_root_prune_queue : BinaryHeap :: new ( ) ,
4454 state_database,
4555 }
4656 }
4757
4858 /// Updates the pruning queue. Abandoned roots will be added to the queue.
4959 /// Added roots will be removed from the queue. This ensures that the state
5060 /// roots won't be removed, regardless of the chain state.
51- pub fn update_queue ( & mut self , added_roots : & [ & str ] , _abandoned_roots : & [ & str ] ) {
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+ }
5266 // Remove any state root hashes from the pruning queue that we may have switched
5367 // back too from an alternate chain
54- added_roots. iter ( ) . for_each ( |state_root_hash| {
55- self . state_root_prune_queue . retain ( |hash| {
56- if hash != state_root_hash {
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 ( ) ) {
5774 true
5875 } else {
59- debug ! ( "Removing {} from pruning queue" , state_root_hash ) ;
76+ debug ! ( "Removing {} from pruning queue" , candidate . 1 ) ;
6077 false
6178 }
6279 } )
63- } ) ;
80+ . collect ( ) ;
6481 }
6582
6683 /// Add a single state root to the pruning queue.
67- pub fn add_to_queue ( & mut self , state_root_hash : & str ) {
84+ pub fn add_to_queue ( & mut self , height : u64 , state_root_hash : & str ) {
6885 let state_root_hash = state_root_hash. into ( ) ;
69- if !self . state_root_prune_queue . contains ( & state_root_hash) {
86+ if !self . state_root_prune_queue
87+ . iter ( )
88+ . any ( |candidate| candidate. 1 == state_root_hash)
89+ {
7090 debug ! ( "Adding {} to pruning queue" , state_root_hash) ;
71- self . state_root_prune_queue . push_back ( state_root_hash) ;
91+ self . state_root_prune_queue
92+ . push ( PruneCandidate ( height, state_root_hash) ) ;
7293 }
7394 }
7495
75- /// Remove the first ten percent (floored) of the prune queue .
76- pub fn decimate ( & mut self ) {
77- let prune_count = ( self . state_root_prune_queue . len ( ) as f64 / 10.0 ) . floor ( ) as usize ;
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 ! [ ] ;
7899
79- let mut total_pruned_entries: usize = 0 ;
80- for _ in 0 ..prune_count {
81- if let Some ( state_root) = self . state_root_prune_queue . pop_front ( ) {
82- match MerkleDatabase :: prune ( & self . state_database , & state_root) {
83- Ok ( removed_keys) => {
84- total_pruned_entries += removed_keys. len ( ) ;
85-
86- let mut state_roots_pruned_count =
87- COLLECTOR . counter ( "StatePruneManager.state_roots_pruned" , None , None ) ;
88- state_roots_pruned_count. inc ( ) ;
89-
90- // the state root was not pruned (it is likely the root of a
91- // fork), so push it back into the queue.
92- if removed_keys. is_empty ( ) {
93- self . state_root_prune_queue . push_back ( state_root) ;
94- }
95- }
96- Err ( err) => {
97- error ! ( "Unable to prune state root {}: {:?}" , state_root, err) ;
98- self . state_root_prune_queue . push_back ( state_root) ;
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 ;
100107 }
101108 } else {
102109 break ;
103110 }
104111 }
105112
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+ let mut state_roots_pruned_count =
120+ COLLECTOR . counter ( "StatePruneManager.state_roots_pruned" , None , None ) ;
121+ state_roots_pruned_count. inc ( ) ;
122+
123+ // the state root was not pruned (it is likely the root of a
124+ // fork), so push it back into the queue.
125+ if removed_keys. is_empty ( ) {
126+ self . state_root_prune_queue . push ( candidate) ;
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+
106136 if total_pruned_entries > 0 {
107137 info ! (
108138 "Pruned {} keys from the Global state Database" ,
@@ -111,3 +141,24 @@ impl StatePruningManager {
111141 }
112142 }
113143}
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+ }
0 commit comments