github.com/ava-labs/avalanchego@v1.11.11/vms/proposervm/state/block_state.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package state 5 6 import ( 7 "errors" 8 9 "github.com/prometheus/client_golang/prometheus" 10 11 "github.com/ava-labs/avalanchego/cache" 12 "github.com/ava-labs/avalanchego/cache/metercacher" 13 "github.com/ava-labs/avalanchego/database" 14 "github.com/ava-labs/avalanchego/ids" 15 "github.com/ava-labs/avalanchego/snow/choices" 16 "github.com/ava-labs/avalanchego/utils/constants" 17 "github.com/ava-labs/avalanchego/utils/metric" 18 "github.com/ava-labs/avalanchego/utils/units" 19 "github.com/ava-labs/avalanchego/utils/wrappers" 20 "github.com/ava-labs/avalanchego/vms/proposervm/block" 21 ) 22 23 const blockCacheSize = 64 * units.MiB 24 25 var ( 26 errBlockWrongVersion = errors.New("wrong version") 27 28 _ BlockState = (*blockState)(nil) 29 ) 30 31 type BlockState interface { 32 GetBlock(blkID ids.ID) (block.Block, error) 33 PutBlock(blk block.Block) error 34 DeleteBlock(blkID ids.ID) error 35 } 36 37 type blockState struct { 38 // Caches BlockID -> Block. If the Block is nil, that means the block is not 39 // in storage. 40 blkCache cache.Cacher[ids.ID, *blockWrapper] 41 42 db database.Database 43 } 44 45 type blockWrapper struct { 46 Block []byte `serialize:"true"` 47 Status choices.Status `serialize:"true"` 48 49 block block.Block 50 } 51 52 func cachedBlockSize(_ ids.ID, bw *blockWrapper) int { 53 if bw == nil { 54 return ids.IDLen + constants.PointerOverhead 55 } 56 return ids.IDLen + len(bw.Block) + wrappers.IntLen + 2*constants.PointerOverhead 57 } 58 59 func NewBlockState(db database.Database) BlockState { 60 return &blockState{ 61 blkCache: cache.NewSizedLRU[ids.ID, *blockWrapper]( 62 blockCacheSize, 63 cachedBlockSize, 64 ), 65 db: db, 66 } 67 } 68 69 func NewMeteredBlockState(db database.Database, namespace string, metrics prometheus.Registerer) (BlockState, error) { 70 blkCache, err := metercacher.New[ids.ID, *blockWrapper]( 71 metric.AppendNamespace(namespace, "block_cache"), 72 metrics, 73 cache.NewSizedLRU[ids.ID, *blockWrapper]( 74 blockCacheSize, 75 cachedBlockSize, 76 ), 77 ) 78 79 return &blockState{ 80 blkCache: blkCache, 81 db: db, 82 }, err 83 } 84 85 func (s *blockState) GetBlock(blkID ids.ID) (block.Block, error) { 86 if blk, found := s.blkCache.Get(blkID); found { 87 if blk == nil { 88 return nil, database.ErrNotFound 89 } 90 return blk.block, nil 91 } 92 93 blkWrapperBytes, err := s.db.Get(blkID[:]) 94 if err == database.ErrNotFound { 95 s.blkCache.Put(blkID, nil) 96 return nil, database.ErrNotFound 97 } 98 if err != nil { 99 return nil, err 100 } 101 102 blkWrapper := blockWrapper{} 103 parsedVersion, err := Codec.Unmarshal(blkWrapperBytes, &blkWrapper) 104 if err != nil { 105 return nil, err 106 } 107 if parsedVersion != CodecVersion { 108 return nil, errBlockWrongVersion 109 } 110 111 // The key was in the database 112 blk, err := block.ParseWithoutVerification(blkWrapper.Block) 113 if err != nil { 114 return nil, err 115 } 116 blkWrapper.block = blk 117 118 s.blkCache.Put(blkID, &blkWrapper) 119 return blk, nil 120 } 121 122 func (s *blockState) PutBlock(blk block.Block) error { 123 blkWrapper := blockWrapper{ 124 Block: blk.Bytes(), 125 Status: choices.Accepted, 126 block: blk, 127 } 128 129 bytes, err := Codec.Marshal(CodecVersion, &blkWrapper) 130 if err != nil { 131 return err 132 } 133 134 blkID := blk.ID() 135 s.blkCache.Put(blkID, &blkWrapper) 136 return s.db.Put(blkID[:], bytes) 137 } 138 139 func (s *blockState) DeleteBlock(blkID ids.ID) error { 140 s.blkCache.Evict(blkID) 141 return s.db.Delete(blkID[:]) 142 }