@@ -44,6 +44,25 @@ pub(crate) enum TransactionState {
4444 Dropped ,
4545}
4646
47+
48+ /// Treshold of operation before running a garbage colletion
49+ /// on a transaction operation.
50+ /// Should be same as `TRIGGER_COMMIT_GC` or higher
51+ /// (we most likely do not want lower as transaction are
52+ /// possibly more frequent than commit).
53+ const TRIGGER_TRANSACTION_GC : usize = 10_000 ;
54+
55+ /// Treshold of operation before running a garbage colletion
56+ /// on a commit operation.
57+ /// We may want a lower value than for a transaction, even
58+ /// a 1 if we want to do it between every operation.
59+ const TRIGGER_COMMIT_GC : usize = 1_000 ;
60+
61+ /// Used to count big content as multiple operation.
62+ /// This is a number of octet.
63+ /// Set to 0 to ignore.
64+ const ADD_CONTENT_SIZE_UNIT : usize = 64 ;
65+
4766/// The overlayed changes to state to be queried on top of the backend.
4867///
4968/// A transaction shares all prospective changes within an inner overlay
@@ -55,6 +74,10 @@ pub struct OverlayedChanges {
5574 /// Changes trie configuration. None by default, but could be installed by the
5675 /// runtime if it supports change tries.
5776 pub ( crate ) changes_trie_config : Option < ChangesTrieConfig > ,
77+ /// Counter of number of operation between garbage collection.
78+ /// Add or delete cost one, additional cost per size by counting a fix size
79+ /// as a unit.
80+ pub ( crate ) operation_from_last_gc : usize ,
5881}
5982
6083/// The storage value, used inside OverlayedChanges.
@@ -683,10 +706,18 @@ impl OverlayedChanges {
683706 None
684707 }
685708
709+ fn add_cost_op ( & mut self , val : & Option < Vec < u8 > > ) {
710+ let content_cost = if ADD_CONTENT_SIZE_UNIT > 0 {
711+ val. as_ref ( ) . map ( |s| s. len ( ) / ADD_CONTENT_SIZE_UNIT ) . unwrap_or ( 0 )
712+ } else { 0 } ;
713+ self . operation_from_last_gc += 1 + content_cost;
714+ }
715+
686716 /// Inserts the given key-value pair into the prospective change set.
687717 ///
688718 /// `None` can be used to delete a value specified by the given key.
689719 pub fn set_storage ( & mut self , key : Vec < u8 > , val : Option < Vec < u8 > > ) {
720+ self . add_cost_op ( & val) ;
690721 let extrinsic_index = self . extrinsic_index ( ) ;
691722 let entry = self . changes . top . entry ( key) . or_default ( ) ;
692723 entry. set_with_extrinsic ( self . changes . history . as_slice ( ) , val, extrinsic_index) ;
@@ -696,6 +727,7 @@ impl OverlayedChanges {
696727 ///
697728 /// `None` can be used to delete a value specified by the given key.
698729 pub ( crate ) fn set_child_storage ( & mut self , storage_key : Vec < u8 > , key : Vec < u8 > , val : Option < Vec < u8 > > ) {
730+ self . add_cost_op ( & val) ;
699731 let extrinsic_index = self . extrinsic_index ( ) ;
700732 let map_entry = self . changes . children . entry ( storage_key) . or_default ( ) ;
701733 let entry = map_entry. entry ( key) . or_default ( ) ;
@@ -713,6 +745,7 @@ impl OverlayedChanges {
713745 let history = self . changes . history . as_slice ( ) ;
714746 let map_entry = self . changes . children . entry ( storage_key. to_vec ( ) ) . or_default ( ) ;
715747
748+ self . operation_from_last_gc += map_entry. len ( ) ;
716749 map_entry. values_mut ( ) . for_each ( |e| e. set_with_extrinsic ( history, None , extrinsic_index) ) ;
717750 }
718751
@@ -725,32 +758,52 @@ impl OverlayedChanges {
725758 pub ( crate ) fn clear_prefix ( & mut self , prefix : & [ u8 ] ) {
726759 let extrinsic_index = self . extrinsic_index ( ) ;
727760
761+ let mut nb_remove = 0 ;
728762 for ( key, entry) in self . changes . top . iter_mut ( ) {
729763 if key. starts_with ( prefix) {
764+ nb_remove += 1 ;
730765 entry. set_with_extrinsic ( self . changes . history . as_slice ( ) , None , extrinsic_index) ;
731766 }
732767 }
768+
769+ self . operation_from_last_gc += nb_remove;
733770 }
734771
735772 /// Discard prospective changes to state.
736773 pub fn discard_prospective ( & mut self ) {
737774 self . changes . discard_prospective ( ) ;
775+ if self . operation_from_last_gc > TRIGGER_COMMIT_GC {
776+ self . operation_from_last_gc = 0 ;
777+ self . gc ( true ) ;
778+ }
738779 }
739780
740781 /// Commit prospective changes to state.
741782 pub fn commit_prospective ( & mut self ) {
742783 self . changes . commit_prospective ( ) ;
784+ if self . operation_from_last_gc > TRIGGER_COMMIT_GC {
785+ self . operation_from_last_gc = 0 ;
786+ self . gc ( true ) ;
787+ }
743788 }
744789
745790 /// Create a new transactional layer.
746791 pub fn start_transaction ( & mut self ) {
747792 self . changes . start_transaction ( ) ;
793+ if self . operation_from_last_gc > TRIGGER_TRANSACTION_GC {
794+ self . operation_from_last_gc = 0 ;
795+ self . gc ( true ) ;
796+ }
748797 }
749798
750799 /// Discard a transactional layer.
751800 /// A transaction is always running (history always end with pending).
752801 pub fn discard_transaction ( & mut self ) {
753802 self . changes . discard_transaction ( ) ;
803+ if self . operation_from_last_gc > TRIGGER_TRANSACTION_GC {
804+ self . operation_from_last_gc = 0 ;
805+ self . gc ( true ) ;
806+ }
754807 }
755808
756809 /// Commit a transactional layer.
@@ -803,7 +856,11 @@ impl OverlayedChanges {
803856 changes_trie_config : Option < ChangesTrieConfig > ,
804857 ) -> Self {
805858 let changes = OverlayedChangeSet :: default ( ) ;
806- let mut result = OverlayedChanges { changes, changes_trie_config } ;
859+ let mut result = OverlayedChanges {
860+ changes,
861+ changes_trie_config,
862+ operation_from_last_gc : 0 ,
863+ } ;
807864 committed. into_iter ( ) . for_each ( |( k, v) | result. set_storage ( k, v) ) ;
808865 result. changes . commit_prospective ( ) ;
809866 prospective. into_iter ( ) . for_each ( |( k, v) | result. set_storage ( k, v) ) ;
0 commit comments