github.com/onflow/flow-go@v0.33.17/engine/execution/storehouse/block_end_snapshot.go (about) 1 package storehouse 2 3 import ( 4 "errors" 5 "sync" 6 7 "github.com/onflow/flow-go/engine/execution" 8 "github.com/onflow/flow-go/fvm/storage/snapshot" 9 "github.com/onflow/flow-go/model/flow" 10 "github.com/onflow/flow-go/storage" 11 ) 12 13 var _ snapshot.StorageSnapshot = (*BlockEndStateSnapshot)(nil) 14 15 // BlockEndStateSnapshot represents the storage at the end of a block. 16 type BlockEndStateSnapshot struct { 17 storage execution.RegisterStore 18 19 blockID flow.Identifier 20 height uint64 21 22 mutex sync.RWMutex 23 readCache map[flow.RegisterID]flow.RegisterValue // cache the reads from storage at baseBlock 24 } 25 26 // the caller must ensure the block height is for the given block 27 func NewBlockEndStateSnapshot( 28 storage execution.RegisterStore, 29 blockID flow.Identifier, 30 height uint64, 31 ) *BlockEndStateSnapshot { 32 return &BlockEndStateSnapshot{ 33 storage: storage, 34 blockID: blockID, 35 height: height, 36 readCache: make(map[flow.RegisterID]flow.RegisterValue), 37 } 38 } 39 40 // Get returns the value of the register with the given register ID. 41 // It returns: 42 // - (value, nil) if the register exists 43 // - (nil, nil) if the register does not exist 44 // - (nil, storage.ErrHeightNotIndexed) if the height is below the first height that is indexed. 45 // - (nil, storehouse.ErrNotExecuted) if the block is not executed yet 46 // - (nil, storehouse.ErrNotExecuted) if the block is conflicting with finalized block 47 // - (nil, err) for any other exceptions 48 func (s *BlockEndStateSnapshot) Get(id flow.RegisterID) (flow.RegisterValue, error) { 49 value, ok := s.getFromCache(id) 50 if ok { 51 return value, nil 52 } 53 54 value, err := s.getFromStorage(id) 55 if err != nil { 56 return nil, err 57 } 58 59 s.mutex.Lock() 60 defer s.mutex.Unlock() 61 62 // TODO: consider adding a limit/eviction policy for the cache 63 s.readCache[id] = value 64 return value, err 65 } 66 67 func (s *BlockEndStateSnapshot) getFromCache(id flow.RegisterID) (flow.RegisterValue, bool) { 68 s.mutex.RLock() 69 defer s.mutex.RUnlock() 70 71 value, ok := s.readCache[id] 72 return value, ok 73 } 74 75 func (s *BlockEndStateSnapshot) getFromStorage(id flow.RegisterID) (flow.RegisterValue, error) { 76 value, err := s.storage.GetRegister(s.height, s.blockID, id) 77 if err != nil { 78 if errors.Is(err, storage.ErrNotFound) { 79 // if the error is not found, we return a nil RegisterValue, 80 // in this case, the nil value can be cached, because the storage will not change it 81 return nil, nil 82 } 83 // if the error is not ErrNotFound, such as storage.ErrHeightNotIndexed, storehouse.ErrNotExecuted 84 // we return the error without caching 85 return nil, err 86 } 87 return value, nil 88 }