github.com/iotexproject/iotex-core@v1.14.1-rc1/blockchain/blockdao/blockdao.go (about) 1 // Copyright (c) 2019 IoTeX Foundation 2 // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability 3 // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed. 4 // This source code is governed by Apache License 2.0 that can be found in the LICENSE file. 5 6 package blockdao 7 8 import ( 9 "context" 10 "sync/atomic" 11 12 "github.com/pkg/errors" 13 "github.com/prometheus/client_golang/prometheus" 14 "go.uber.org/zap" 15 16 "github.com/iotexproject/go-pkgs/cache" 17 "github.com/iotexproject/go-pkgs/hash" 18 "github.com/iotexproject/iotex-proto/golang/iotextypes" 19 20 "github.com/iotexproject/iotex-core/action" 21 "github.com/iotexproject/iotex-core/blockchain/block" 22 "github.com/iotexproject/iotex-core/pkg/lifecycle" 23 "github.com/iotexproject/iotex-core/pkg/log" 24 "github.com/iotexproject/iotex-core/pkg/prometheustimer" 25 ) 26 27 // vars 28 var ( 29 _cacheMtc = prometheus.NewCounterVec( 30 prometheus.CounterOpts{ 31 Name: "iotex_blockdao_cache", 32 Help: "IoTeX blockdao cache counter.", 33 }, 34 []string{"result"}, 35 ) 36 ) 37 38 type ( 39 // BlockDAO represents the block data access object 40 BlockDAO interface { 41 Start(ctx context.Context) error 42 Stop(ctx context.Context) error 43 Height() (uint64, error) 44 GetBlockHash(uint64) (hash.Hash256, error) 45 GetBlockHeight(hash.Hash256) (uint64, error) 46 GetBlock(hash.Hash256) (*block.Block, error) 47 GetBlockByHeight(uint64) (*block.Block, error) 48 GetReceipts(uint64) ([]*action.Receipt, error) 49 ContainsTransactionLog() bool 50 TransactionLogs(uint64) (*iotextypes.TransactionLogs, error) 51 PutBlock(context.Context, *block.Block) error 52 Header(hash.Hash256) (*block.Header, error) 53 HeaderByHeight(uint64) (*block.Header, error) 54 FooterByHeight(uint64) (*block.Footer, error) 55 } 56 57 blockDAO struct { 58 blockStore BlockDAO 59 indexers []BlockIndexer 60 timerFactory *prometheustimer.TimerFactory 61 lifecycle lifecycle.Lifecycle 62 headerCache cache.LRUCache 63 bodyCache cache.LRUCache 64 footerCache cache.LRUCache 65 tipHeight uint64 66 } 67 ) 68 69 // NewBlockDAOWithIndexersAndCache returns a BlockDAO with indexers which will consume blocks appended, and 70 // caches which will speed up reading 71 func NewBlockDAOWithIndexersAndCache(blkStore BlockDAO, indexers []BlockIndexer, cacheSize int) BlockDAO { 72 if blkStore == nil { 73 return nil 74 } 75 76 blockDAO := &blockDAO{ 77 blockStore: blkStore, 78 indexers: indexers, 79 } 80 81 blockDAO.lifecycle.Add(blkStore) 82 for _, indexer := range indexers { 83 blockDAO.lifecycle.Add(indexer) 84 } 85 if cacheSize > 0 { 86 blockDAO.headerCache = cache.NewThreadSafeLruCache(cacheSize) 87 blockDAO.bodyCache = cache.NewThreadSafeLruCache(cacheSize) 88 blockDAO.footerCache = cache.NewThreadSafeLruCache(cacheSize) 89 } 90 timerFactory, err := prometheustimer.New( 91 "iotex_block_dao_perf", 92 "Performance of block DAO", 93 []string{"type"}, 94 []string{"default"}, 95 ) 96 if err != nil { 97 return nil 98 } 99 blockDAO.timerFactory = timerFactory 100 return blockDAO 101 } 102 103 // Start starts block DAO and initiates the top height if it doesn't exist 104 func (dao *blockDAO) Start(ctx context.Context) error { 105 err := dao.lifecycle.OnStart(ctx) 106 if err != nil { 107 return errors.Wrap(err, "failed to start child services") 108 } 109 110 tipHeight, err := dao.blockStore.Height() 111 if err != nil { 112 return err 113 } 114 atomic.StoreUint64(&dao.tipHeight, tipHeight) 115 return dao.checkIndexers(ctx) 116 } 117 118 func (dao *blockDAO) checkIndexers(ctx context.Context) error { 119 checker := NewBlockIndexerChecker(dao) 120 for i, indexer := range dao.indexers { 121 if err := checker.CheckIndexer(ctx, indexer, 0, func(height uint64) { 122 if height%5000 == 0 { 123 log.L().Info( 124 "indexer is catching up.", 125 zap.Int("indexer", i), 126 zap.Uint64("height", height), 127 ) 128 } 129 }); err != nil { 130 return err 131 } 132 log.L().Info( 133 "indexer is up to date.", 134 zap.Int("indexer", i), 135 ) 136 } 137 return nil 138 } 139 140 func (dao *blockDAO) Stop(ctx context.Context) error { return dao.lifecycle.OnStop(ctx) } 141 142 func (dao *blockDAO) GetBlockHash(height uint64) (hash.Hash256, error) { 143 timer := dao.timerFactory.NewTimer("get_block_hash") 144 defer timer.End() 145 return dao.blockStore.GetBlockHash(height) 146 } 147 148 func (dao *blockDAO) GetBlockHeight(hash hash.Hash256) (uint64, error) { 149 timer := dao.timerFactory.NewTimer("get_block_height") 150 defer timer.End() 151 return dao.blockStore.GetBlockHeight(hash) 152 } 153 154 func (dao *blockDAO) GetBlock(hash hash.Hash256) (*block.Block, error) { 155 timer := dao.timerFactory.NewTimer("get_block") 156 defer timer.End() 157 return dao.blockStore.GetBlock(hash) 158 } 159 160 func (dao *blockDAO) GetBlockByHeight(height uint64) (*block.Block, error) { 161 timer := dao.timerFactory.NewTimer("get_block_byheight") 162 defer timer.End() 163 return dao.blockStore.GetBlockByHeight(height) 164 } 165 166 func (dao *blockDAO) HeaderByHeight(height uint64) (*block.Header, error) { 167 if v, ok := lruCacheGet(dao.headerCache, height); ok { 168 _cacheMtc.WithLabelValues("hit_header").Inc() 169 return v.(*block.Header), nil 170 } 171 _cacheMtc.WithLabelValues("miss_header").Inc() 172 173 header, err := dao.blockStore.HeaderByHeight(height) 174 if err != nil { 175 return nil, err 176 } 177 178 lruCachePut(dao.headerCache, height, header) 179 return header, nil 180 } 181 182 func (dao *blockDAO) FooterByHeight(height uint64) (*block.Footer, error) { 183 if v, ok := lruCacheGet(dao.footerCache, height); ok { 184 _cacheMtc.WithLabelValues("hit_footer").Inc() 185 return v.(*block.Footer), nil 186 } 187 _cacheMtc.WithLabelValues("miss_footer").Inc() 188 189 footer, err := dao.blockStore.FooterByHeight(height) 190 if err != nil { 191 return nil, err 192 } 193 194 lruCachePut(dao.footerCache, height, footer) 195 return footer, nil 196 } 197 198 func (dao *blockDAO) Height() (uint64, error) { 199 return dao.blockStore.Height() 200 } 201 202 func (dao *blockDAO) Header(h hash.Hash256) (*block.Header, error) { 203 if header, ok := lruCacheGet(dao.headerCache, h); ok { 204 _cacheMtc.WithLabelValues("hit_header").Inc() 205 return header.(*block.Header), nil 206 } 207 _cacheMtc.WithLabelValues("miss_header").Inc() 208 209 header, err := dao.blockStore.Header(h) 210 if err != nil { 211 return nil, err 212 } 213 214 lruCachePut(dao.headerCache, h, header) 215 return header, nil 216 } 217 218 func (dao *blockDAO) GetReceipts(height uint64) ([]*action.Receipt, error) { 219 timer := dao.timerFactory.NewTimer("get_receipt") 220 defer timer.End() 221 return dao.blockStore.GetReceipts(height) 222 } 223 224 func (dao *blockDAO) ContainsTransactionLog() bool { 225 return dao.blockStore.ContainsTransactionLog() 226 } 227 228 func (dao *blockDAO) TransactionLogs(height uint64) (*iotextypes.TransactionLogs, error) { 229 timer := dao.timerFactory.NewTimer("get_transactionlog") 230 defer timer.End() 231 return dao.blockStore.TransactionLogs(height) 232 } 233 234 func (dao *blockDAO) PutBlock(ctx context.Context, blk *block.Block) error { 235 timer := dao.timerFactory.NewTimer("put_block") 236 if err := dao.blockStore.PutBlock(ctx, blk); err != nil { 237 timer.End() 238 return err 239 } 240 atomic.StoreUint64(&dao.tipHeight, blk.Height()) 241 header := blk.Header 242 lruCachePut(dao.headerCache, blk.Height(), &header) 243 lruCachePut(dao.headerCache, header.HashHeader(), &header) 244 timer.End() 245 246 // index the block if there's indexer 247 timer = dao.timerFactory.NewTimer("index_block") 248 defer timer.End() 249 for _, indexer := range dao.indexers { 250 if err := indexer.PutBlock(ctx, blk); err != nil { 251 return err 252 } 253 } 254 return nil 255 } 256 257 func lruCacheGet(c cache.LRUCache, key interface{}) (interface{}, bool) { 258 if c != nil { 259 return c.Get(key) 260 } 261 return nil, false 262 } 263 264 func lruCachePut(c cache.LRUCache, k, v interface{}) { 265 if c != nil { 266 c.Add(k, v) 267 } 268 }