github.com/iotexproject/iotex-core@v1.14.1-rc1/blockindex/sync_indexers.go (about)

     1  // Copyright (c) 2023 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  	"context"
    10  
    11  	"github.com/iotexproject/iotex-core/blockchain/block"
    12  	"github.com/iotexproject/iotex-core/blockchain/blockdao"
    13  )
    14  
    15  // SyncIndexers is a special index that includes multiple indexes,
    16  // which stay in sync when blocks are added.
    17  type SyncIndexers struct {
    18  	indexers       []blockdao.BlockIndexer
    19  	startHeights   []uint64 // start height of each indexer, which will be determined when the indexer is started
    20  	minStartHeight uint64   // minimum start height of all indexers
    21  }
    22  
    23  // NewSyncIndexers creates a new SyncIndexers
    24  // each indexer will PutBlock one by one in the order of the indexers
    25  func NewSyncIndexers(indexers ...blockdao.BlockIndexer) *SyncIndexers {
    26  	return &SyncIndexers{indexers: indexers}
    27  }
    28  
    29  // Start starts the indexer group
    30  func (ig *SyncIndexers) Start(ctx context.Context) error {
    31  	for _, indexer := range ig.indexers {
    32  		if err := indexer.Start(ctx); err != nil {
    33  			return err
    34  		}
    35  	}
    36  	return ig.initStartHeight()
    37  }
    38  
    39  // Stop stops the indexer group
    40  func (ig *SyncIndexers) Stop(ctx context.Context) error {
    41  	for _, indexer := range ig.indexers {
    42  		if err := indexer.Stop(ctx); err != nil {
    43  			return err
    44  		}
    45  	}
    46  	return nil
    47  }
    48  
    49  // PutBlock puts a block into the indexers in the group
    50  func (ig *SyncIndexers) PutBlock(ctx context.Context, blk *block.Block) error {
    51  	for i, indexer := range ig.indexers {
    52  		// check if the block is higher than the indexer's start height
    53  		if blk.Height() < ig.startHeights[i] {
    54  			continue
    55  		}
    56  		// check if the block is higher than the indexer's height
    57  		height, err := indexer.Height()
    58  		if err != nil {
    59  			return err
    60  		}
    61  		if blk.Height() <= height {
    62  			continue
    63  		}
    64  		// put block
    65  		if err := indexer.PutBlock(ctx, blk); err != nil {
    66  			return err
    67  		}
    68  	}
    69  	return nil
    70  }
    71  
    72  // DeleteTipBlock deletes the tip block from the indexers in the group
    73  func (ig *SyncIndexers) DeleteTipBlock(ctx context.Context, blk *block.Block) error {
    74  	for _, indexer := range ig.indexers {
    75  		if err := indexer.DeleteTipBlock(ctx, blk); err != nil {
    76  			return err
    77  		}
    78  	}
    79  	return nil
    80  }
    81  
    82  // StartHeight returns the minimum start height of the indexers in the group
    83  func (ig *SyncIndexers) StartHeight() uint64 {
    84  	return ig.minStartHeight
    85  }
    86  
    87  // Height returns the minimum height of the indexers in the group
    88  func (ig *SyncIndexers) Height() (uint64, error) {
    89  	var height uint64
    90  	for i, indexer := range ig.indexers {
    91  		h, err := indexer.Height()
    92  		if err != nil {
    93  			return 0, err
    94  		}
    95  		if i == 0 || h < height {
    96  			height = h
    97  		}
    98  	}
    99  	return height, nil
   100  }
   101  
   102  // initStartHeight initializes the start height of the indexers in the group
   103  // for every indexer, the start height is the maximum of tipheight+1 and startheight
   104  func (ig *SyncIndexers) initStartHeight() error {
   105  	ig.minStartHeight = 0
   106  	ig.startHeights = make([]uint64, len(ig.indexers))
   107  	for i, indexer := range ig.indexers {
   108  		tipHeight, err := indexer.Height()
   109  		if err != nil {
   110  			return err
   111  		}
   112  		indexStartHeight := tipHeight + 1
   113  		if indexerWithStart, ok := indexer.(blockdao.BlockIndexerWithStart); ok {
   114  			startHeight := indexerWithStart.StartHeight()
   115  			if startHeight > indexStartHeight {
   116  				indexStartHeight = startHeight
   117  			}
   118  		}
   119  		ig.startHeights[i] = indexStartHeight
   120  		if i == 0 || indexStartHeight < ig.minStartHeight {
   121  			ig.minStartHeight = indexStartHeight
   122  		}
   123  	}
   124  	return nil
   125  }