github.com/iotexproject/iotex-core@v1.14.1-rc1/blockindex/indexbuilder.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 blockindex 7 8 import ( 9 "strconv" 10 11 "github.com/pkg/errors" 12 "github.com/prometheus/client_golang/prometheus" 13 "go.uber.org/zap" 14 "golang.org/x/net/context" 15 16 "github.com/iotexproject/iotex-core/blockchain/block" 17 "github.com/iotexproject/iotex-core/blockchain/blockdao" 18 "github.com/iotexproject/iotex-core/blockchain/genesis" 19 "github.com/iotexproject/iotex-core/pkg/log" 20 "github.com/iotexproject/iotex-core/pkg/prometheustimer" 21 ) 22 23 var batchSizeMtc = prometheus.NewGaugeVec( 24 prometheus.GaugeOpts{ 25 Name: "iotex_indexer_batch_size", 26 Help: "Indexer batch size", 27 }, 28 []string{}, 29 ) 30 31 func init() { 32 prometheus.MustRegister(batchSizeMtc) 33 } 34 35 // these NS belong to old DB before migrating to separate index 36 // they are left here only for record 37 // do NOT use them in the future to avoid potential conflict 38 const ( 39 _blockActionBlockMappingNS = "a2b" 40 _blockAddressActionMappingNS = "a2a" 41 _blockAddressActionCountMappingNS = "a2c" 42 _blockActionReceiptMappingNS = "a2r" 43 _numActionsNS = "nac" 44 _transferAmountNS = "tfa" 45 ) 46 47 // IndexBuilder defines the index builder 48 type IndexBuilder struct { 49 timerFactory *prometheustimer.TimerFactory 50 dao blockdao.BlockDAO 51 indexer Indexer 52 genesis genesis.Genesis 53 } 54 55 // NewIndexBuilder instantiates an index builder 56 func NewIndexBuilder(chainID uint32, g genesis.Genesis, dao blockdao.BlockDAO, indexer Indexer) (*IndexBuilder, error) { 57 timerFactory, err := prometheustimer.New( 58 "iotex_indexer_batch_time", 59 "Indexer batch time", 60 []string{"topic", "chainID"}, 61 []string{"default", strconv.FormatUint(uint64(chainID), 10)}, 62 ) 63 if err != nil { 64 return nil, err 65 } 66 return &IndexBuilder{ 67 timerFactory: timerFactory, 68 dao: dao, 69 indexer: indexer, 70 genesis: g, 71 }, nil 72 } 73 74 // Start starts the index builder 75 func (ib *IndexBuilder) Start(ctx context.Context) error { 76 if err := ib.indexer.Start(ctx); err != nil { 77 return err 78 } 79 if err := ib.init(ctx); err != nil { 80 return err 81 } 82 // start handler to index incoming new block 83 return nil 84 } 85 86 // Stop stops the index builder 87 func (ib *IndexBuilder) Stop(ctx context.Context) error { 88 return ib.indexer.Stop(ctx) 89 } 90 91 // Indexer returns the indexer 92 func (ib *IndexBuilder) Indexer() Indexer { 93 return ib.indexer 94 } 95 96 // ReceiveBlock handles the block and create the indices for the actions and receipts in it 97 func (ib *IndexBuilder) ReceiveBlock(blk *block.Block) error { 98 timer := ib.timerFactory.NewTimer("indexBlock") 99 if err := ib.indexer.PutBlock(genesis.WithGenesisContext(context.Background(), ib.genesis), blk); err != nil { 100 log.L().Error( 101 "Error when indexing the block", 102 zap.Uint64("height", blk.Height()), 103 zap.Error(err), 104 ) 105 return err 106 } 107 timer.End() 108 if blk.Height()%100 == 0 { 109 log.L().Info("indexing new block", zap.Uint64("height", blk.Height())) 110 } 111 return nil 112 } 113 114 func (ib *IndexBuilder) init(ctx context.Context) error { 115 startHeight, err := ib.indexer.Height() 116 if err != nil { 117 return err 118 } 119 tipHeight, err := ib.dao.Height() 120 if err != nil { 121 return err 122 } 123 if startHeight == tipHeight { 124 // indexer height consistent with dao height 125 zap.L().Info("Consistent DB", zap.Uint64("height", startHeight)) 126 return nil 127 } 128 if startHeight > tipHeight { 129 // indexer height > dao height 130 // this shouldn't happen unless blocks are deliberately removed from dao w/o removing index 131 // in this case we revert the extra block index, but nothing we can do to revert action index 132 err := errors.Errorf("Inconsistent DB: indexer height %d > blockDAO height %d", startHeight, tipHeight) 133 zap.L().Error(err.Error()) 134 return err 135 } 136 // update index to latest block 137 var ( 138 gCtx = genesis.WithGenesisContext(ctx, ib.genesis) 139 blks = make([]*block.Block, 0, 5000) 140 ) 141 for startHeight++; startHeight <= tipHeight; startHeight++ { 142 blk, err := ib.dao.GetBlockByHeight(startHeight) 143 if err != nil { 144 return err 145 } 146 blks = append(blks, blk) 147 // commit once every 5000 blocks 148 if startHeight%5000 == 0 || startHeight == tipHeight { 149 if err := ib.indexer.PutBlocks(gCtx, blks); err != nil { 150 return err 151 } 152 blks = blks[:0] 153 zap.L().Info("Finished indexing blocks up to", zap.Uint64("height", startHeight)) 154 } 155 } 156 if startHeight >= tipHeight { 157 // successfully migrated to latest block 158 zap.L().Info("Finished migrating DB", zap.Uint64("height", startHeight)) 159 } 160 return nil 161 }