github.com/eris-ltd/erisdb@v0.25.0/storage/immutable_forest.go (about) 1 package storage 2 3 import ( 4 "fmt" 5 6 "github.com/xlab/treeprint" 7 8 lru "github.com/hashicorp/golang-lru" 9 dbm "github.com/tendermint/tendermint/libs/db" 10 ) 11 12 type ImmutableForest struct { 13 // Store of tree prefix -> last commitID (version + hash) - serves as a set of all known trees and provides a global hash 14 commitsTree KVCallbackIterableReader 15 treeDB dbm.DB 16 // Cache for frequently used trees 17 treeCache *lru.Cache 18 // Cache size is used in multiple places - for the LRU cache and node cache for any trees created - it probably 19 // makes sense for them to be roughly the same size 20 cacheSize int 21 // Determines whether we use LoadVersionForOverwriting on underlying MutableTrees - since ImmutableForest is used 22 // by MutableForest in a writing context sometimes we do need to load a version destructively 23 overwriting bool 24 } 25 26 type ForestOption func(*ImmutableForest) 27 28 var WithOverwriting ForestOption = func(imf *ImmutableForest) { imf.overwriting = true } 29 30 func NewImmutableForest(commitsTree KVCallbackIterableReader, treeDB dbm.DB, cacheSize int, 31 options ...ForestOption) (*ImmutableForest, error) { 32 cache, err := lru.New(cacheSize) 33 if err != nil { 34 return nil, fmt.Errorf("NewImmutableForest() could not create cache: %v", err) 35 } 36 imf := &ImmutableForest{ 37 commitsTree: commitsTree, 38 treeDB: treeDB, 39 treeCache: cache, 40 cacheSize: cacheSize, 41 } 42 for _, opt := range options { 43 opt(imf) 44 } 45 return imf, nil 46 } 47 48 func (imf *ImmutableForest) Iterate(start, end []byte, ascending bool, fn func(prefix []byte, tree KVCallbackIterableReader) error) error { 49 return imf.commitsTree.Iterate(start, end, ascending, func(prefix []byte, _ []byte) error { 50 rwt, err := imf.tree(prefix) 51 if err != nil { 52 return err 53 } 54 return fn(prefix, rwt) 55 }) 56 } 57 58 func (imf *ImmutableForest) IterateRWTree(start, end []byte, ascending bool, fn func(prefix []byte, tree *RWTree) error) error { 59 return imf.commitsTree.Iterate(start, end, ascending, func(prefix []byte, _ []byte) error { 60 rwt, err := imf.tree(prefix) 61 if err != nil { 62 return err 63 } 64 return fn(prefix, rwt) 65 }) 66 } 67 68 // Get the tree at prefix for making reads 69 func (imf *ImmutableForest) Reader(prefix []byte) (KVCallbackIterableReader, error) { 70 return imf.tree(prefix) 71 } 72 73 // Lazy load tree 74 func (imf *ImmutableForest) tree(prefix []byte) (*RWTree, error) { 75 // Try cache 76 if value, ok := imf.treeCache.Get(string(prefix)); ok { 77 return value.(*RWTree), nil 78 } 79 // Not in caches but non-negative version - we should be able to load into memory 80 return imf.loadOrCreateTree(prefix) 81 } 82 83 func (imf *ImmutableForest) commitID(prefix []byte) (*CommitID, error) { 84 bs := imf.commitsTree.Get(prefix) 85 if bs == nil { 86 return new(CommitID), nil 87 } 88 commitID, err := UnmarshalCommitID(bs) 89 if err != nil { 90 return nil, fmt.Errorf("could not get commitID for prefix %X: %v", prefix, err) 91 } 92 return commitID, nil 93 } 94 95 func (imf *ImmutableForest) loadOrCreateTree(prefix []byte) (*RWTree, error) { 96 const errHeader = "ImmutableForest.loadOrCreateTree():" 97 tree := imf.newTree(prefix) 98 commitID, err := imf.commitID(prefix) 99 if err != nil { 100 return nil, fmt.Errorf("%s %v", errHeader, err) 101 } 102 if commitID.Version == 0 { 103 // This is the first time we have been asked to load this tree 104 return imf.newTree(prefix), nil 105 } 106 err = tree.Load(commitID.Version, imf.overwriting) 107 if err != nil { 108 return nil, fmt.Errorf("%s could not load tree: %v", errHeader, err) 109 } 110 return tree, nil 111 } 112 113 // Create a new in-memory IAVL tree 114 func (imf *ImmutableForest) newTree(prefix []byte) *RWTree { 115 p := string(prefix) 116 tree := NewRWTree(NewPrefixDB(imf.treeDB, p), imf.cacheSize) 117 imf.treeCache.Add(p, tree) 118 return tree 119 } 120 121 func (imf *ImmutableForest) Dump() string { 122 dump := treeprint.New() 123 AddTreePrintTree("Commits", dump, imf.commitsTree) 124 imf.Iterate(nil, nil, true, func(prefix []byte, tree KVCallbackIterableReader) error { 125 AddTreePrintTree(string(prefix), dump, tree) 126 return nil 127 }) 128 return dump.String() 129 }