11package mtrie
22
33import (
4- "errors"
54 "fmt"
65
7- lru "github.com/hashicorp/golang-lru"
8-
96 "github.com/onflow/flow-go/ledger"
107 "github.com/onflow/flow-go/ledger/common/hash"
118 "github.com/onflow/flow-go/ledger/complete/mtrie/trie"
@@ -27,7 +24,7 @@ type Forest struct {
2724 // tries stores all MTries in the forest. It is NOT a CACHE in the conventional sense:
2825 // there is no mechanism to load a trie from disk in case of a cache miss. Missing a
2926 // needed trie in the forest might cause a fatal application logic error.
30- tries * lru. Cache
27+ tries * TrieCache
3128 forestCapacity int
3229 onTreeEvicted func (tree * trie.MTrie )
3330 metrics module.LedgerMetrics
@@ -36,38 +33,19 @@ type Forest struct {
3633// NewForest returns a new instance of memory forest.
3734//
3835// CAUTION on forestCapacity: the specified capacity MUST be SUFFICIENT to store all needed MTries in the forest.
39- // If more tries are added than the capacity, the Least Recently Used trie is removed (evicted) from the Forest.
40- // THIS IS A ROUGH HEURISTIC as it might evict tries that are still needed.
36+ // If more tries are added than the capacity, the Least Recently Added trie is removed (evicted) from the Forest (FIFO queue).
4137// Make sure you chose a sufficiently large forestCapacity, such that, when reaching the capacity, the
42- // Least Recently Used trie will never be needed again.
38+ // Least Recently Added trie will never be needed again.
4339func NewForest (forestCapacity int , metrics module.LedgerMetrics , onTreeEvicted func (tree * trie.MTrie )) (* Forest , error ) {
44- // init LRU cache as a SHORTCUT for a usage-related storage eviction policy
45- var cache * lru.Cache
46- var err error
47- if onTreeEvicted != nil {
48- cache , err = lru .NewWithEvict (forestCapacity , func (key interface {}, value interface {}) {
49- trie , ok := value .(* trie.MTrie )
50- if ! ok {
51- panic (fmt .Sprintf ("cache contains item of type %T" , value ))
52- }
53- onTreeEvicted (trie )
54- })
55- } else {
56- cache , err = lru .New (forestCapacity )
57- }
58- if err != nil {
59- return nil , fmt .Errorf ("cannot create forest cache: %w" , err )
60- }
61-
62- forest := & Forest {tries : cache ,
40+ forest := & Forest {tries : NewTrieCache (uint (forestCapacity ), onTreeEvicted ),
6341 forestCapacity : forestCapacity ,
6442 onTreeEvicted : onTreeEvicted ,
6543 metrics : metrics ,
6644 }
6745
6846 // add trie with no allocated registers
6947 emptyTrie := trie .NewEmptyMTrie ()
70- err = forest .AddTrie (emptyTrie )
48+ err : = forest .AddTrie (emptyTrie )
7149 if err != nil {
7250 return nil , fmt .Errorf ("adding empty trie to forest failed: %w" , err )
7351 }
@@ -333,33 +311,15 @@ func (f *Forest) HasTrie(rootHash ledger.RootHash) bool {
333311// warning, use this function for read-only operation
334312func (f * Forest ) GetTrie (rootHash ledger.RootHash ) (* trie.MTrie , error ) {
335313 // if in memory
336- if ent , found := f .tries .Get (rootHash ); found {
337- trie , ok := ent .(* trie.MTrie )
338- if ! ok {
339- return nil , fmt .Errorf ("forest contains an element of a wrong type" )
340- }
314+ if trie , found := f .tries .Get (rootHash ); found {
341315 return trie , nil
342316 }
343317 return nil , fmt .Errorf ("trie with the given rootHash %s not found" , rootHash )
344318}
345319
346320// GetTries returns list of currently cached tree root hashes
347321func (f * Forest ) GetTries () ([]* trie.MTrie , error ) {
348- // ToDo needs concurrency safety
349- keys := f .tries .Keys ()
350- tries := make ([]* trie.MTrie , len (keys ))
351- for i , key := range keys {
352- t , ok := f .tries .Get (key )
353- if ! ok {
354- return nil , errors .New ("concurrent Forest modification" )
355- }
356- trie , ok := t .(* trie.MTrie )
357- if ! ok {
358- return nil , errors .New ("forest contains an element of a wrong type" )
359- }
360- tries [i ] = trie
361- }
362- return tries , nil
322+ return f .tries .Tries (), nil
363323}
364324
365325// AddTries adds a trie to the forest
@@ -381,44 +341,42 @@ func (f *Forest) AddTrie(newTrie *trie.MTrie) error {
381341
382342 // TODO: check Thread safety
383343 rootHash := newTrie .RootHash ()
384- if storedTrie , found := f .tries .Get (rootHash ); found {
385- trie , ok := storedTrie .(* trie.MTrie )
386- if ! ok {
387- return fmt .Errorf ("forest contains an element of a wrong type" )
388- }
389- if trie .Equals (newTrie ) {
390- return nil
391- }
392- return fmt .Errorf ("forest already contains a tree with same root hash but other properties" )
344+ if _ , found := f .tries .Get (rootHash ); found {
345+ // do no op
346+ return nil
393347 }
394- f .tries .Add ( rootHash , newTrie )
395- f .metrics .ForestNumberOfTrees (uint64 (f .tries .Len ()))
348+ f .tries .Push ( newTrie )
349+ f .metrics .ForestNumberOfTrees (uint64 (f .tries .Count ()))
396350
397351 return nil
398352}
399353
400- // RemoveTrie removes a trie to the forest
401- func (f * Forest ) RemoveTrie (rootHash ledger.RootHash ) {
402- // TODO remove from the file as well
403- f .tries .Remove (rootHash )
404- f .metrics .ForestNumberOfTrees (uint64 (f .tries .Len ()))
405- }
406-
407354// GetEmptyRootHash returns the rootHash of empty Trie
408355func (f * Forest ) GetEmptyRootHash () ledger.RootHash {
409356 return trie .EmptyTrieRootHash ()
410357}
411358
412359// MostRecentTouchedRootHash returns the rootHash of the most recently touched trie
413360func (f * Forest ) MostRecentTouchedRootHash () (ledger.RootHash , error ) {
414- keys := f .tries .Keys ()
415- if len ( keys ) > 0 {
416- return keys [ len ( keys ) - 1 ].(ledger. RootHash ), nil
361+ trie := f .tries .LastAddedTrie ()
362+ if trie != nil {
363+ return trie . RootHash ( ), nil
417364 }
418365 return ledger .RootHash (hash .DummyHash ), fmt .Errorf ("no trie is stored in the forest" )
419366}
420367
368+ // PurgeCacheExcept removes all tries in the memory except the one with the given root hash
369+ func (f * Forest ) PurgeCacheExcept (rootHash ledger.RootHash ) error {
370+ trie , found := f .tries .Get (rootHash )
371+ if ! found {
372+ return fmt .Errorf ("trie with the given root hash not found" )
373+ }
374+ f .tries .Purge ()
375+ f .tries .Push (trie )
376+ return nil
377+ }
378+
421379// Size returns the number of active tries in this store
422380func (f * Forest ) Size () int {
423- return f .tries .Len ()
381+ return f .tries .Count ()
424382}
0 commit comments