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  }