github.com/badrootd/nibiru-cometbft@v0.37.5-0.20240307173500-2a75559eee9b/cmd/cometbft/commands/reindex_event.go (about)

     1  package commands
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"github.com/spf13/cobra"
     9  
    10  	dbm "github.com/badrootd/nibiru-db"
    11  
    12  	abcitypes "github.com/badrootd/nibiru-cometbft/abci/types"
    13  	cmtcfg "github.com/badrootd/nibiru-cometbft/config"
    14  	"github.com/badrootd/nibiru-cometbft/libs/progressbar"
    15  	"github.com/badrootd/nibiru-cometbft/state"
    16  	"github.com/badrootd/nibiru-cometbft/state/indexer"
    17  	blockidxkv "github.com/badrootd/nibiru-cometbft/state/indexer/block/kv"
    18  	"github.com/badrootd/nibiru-cometbft/state/indexer/sink/psql"
    19  	"github.com/badrootd/nibiru-cometbft/state/txindex"
    20  	"github.com/badrootd/nibiru-cometbft/state/txindex/kv"
    21  	"github.com/badrootd/nibiru-cometbft/types"
    22  )
    23  
    24  const (
    25  	reindexFailed = "event re-index failed: "
    26  )
    27  
    28  var (
    29  	ErrHeightNotAvailable = errors.New("height is not available")
    30  	ErrInvalidRequest     = errors.New("invalid request")
    31  )
    32  
    33  // ReIndexEventCmd constructs a command to re-index events in a block height interval.
    34  var ReIndexEventCmd = &cobra.Command{
    35  	Use:     "reindex-event",
    36  	Aliases: []string{"reindex_event"},
    37  	Short:   "reindex events to the event store backends",
    38  	Long: `
    39  reindex-event is an offline tooling to re-index block and tx events to the eventsinks,
    40  you can run this command when the event store backend dropped/disconnected or you want to
    41  replace the backend. The default start-height is 0, meaning the tooling will start
    42  reindex from the base block height(inclusive); and the default end-height is 0, meaning
    43  the tooling will reindex until the latest block height(inclusive). User can omit
    44  either or both arguments.
    45  
    46  Note: This operation requires ABCI Responses. Do not set DiscardABCIResponses to true if you
    47  want to use this command.
    48  	`,
    49  	Example: `
    50  	cometbft reindex-event
    51  	cometbft reindex-event --start-height 2
    52  	cometbft reindex-event --end-height 10
    53  	cometbft reindex-event --start-height 2 --end-height 10
    54  	`,
    55  	Run: func(cmd *cobra.Command, args []string) {
    56  		bs, ss, err := loadStateAndBlockStore(config)
    57  		if err != nil {
    58  			fmt.Println(reindexFailed, err)
    59  			return
    60  		}
    61  
    62  		if err := checkValidHeight(bs); err != nil {
    63  			fmt.Println(reindexFailed, err)
    64  			return
    65  		}
    66  
    67  		bi, ti, err := loadEventSinks(config)
    68  		if err != nil {
    69  			fmt.Println(reindexFailed, err)
    70  			return
    71  		}
    72  
    73  		riArgs := eventReIndexArgs{
    74  			startHeight:  startHeight,
    75  			endHeight:    endHeight,
    76  			blockIndexer: bi,
    77  			txIndexer:    ti,
    78  			blockStore:   bs,
    79  			stateStore:   ss,
    80  		}
    81  		if err := eventReIndex(cmd, riArgs); err != nil {
    82  			panic(fmt.Errorf("%s: %w", reindexFailed, err))
    83  		}
    84  
    85  		fmt.Println("event re-index finished")
    86  	},
    87  }
    88  
    89  var (
    90  	startHeight int64
    91  	endHeight   int64
    92  )
    93  
    94  func init() {
    95  	ReIndexEventCmd.Flags().Int64Var(&startHeight, "start-height", 0, "the block height would like to start for re-index")
    96  	ReIndexEventCmd.Flags().Int64Var(&endHeight, "end-height", 0, "the block height would like to finish for re-index")
    97  }
    98  
    99  func loadEventSinks(cfg *cmtcfg.Config) (indexer.BlockIndexer, txindex.TxIndexer, error) {
   100  	switch strings.ToLower(cfg.TxIndex.Indexer) {
   101  	case "null":
   102  		return nil, nil, errors.New("found null event sink, please check the tx-index section in the config.toml")
   103  	case "psql":
   104  		conn := cfg.TxIndex.PsqlConn
   105  		if conn == "" {
   106  			return nil, nil, errors.New("the psql connection settings cannot be empty")
   107  		}
   108  		es, err := psql.NewEventSink(conn, cfg.ChainID())
   109  		if err != nil {
   110  			return nil, nil, err
   111  		}
   112  		return es.BlockIndexer(), es.TxIndexer(), nil
   113  	case "kv":
   114  		store, err := dbm.NewDB("tx_index", dbm.BackendType(cfg.DBBackend), cfg.DBDir())
   115  		if err != nil {
   116  			return nil, nil, err
   117  		}
   118  
   119  		txIndexer := kv.NewTxIndex(store)
   120  		blockIndexer := blockidxkv.New(dbm.NewPrefixDB(store, []byte("block_events")))
   121  		return blockIndexer, txIndexer, nil
   122  	default:
   123  		return nil, nil, fmt.Errorf("unsupported event sink type: %s", cfg.TxIndex.Indexer)
   124  	}
   125  }
   126  
   127  type eventReIndexArgs struct {
   128  	startHeight  int64
   129  	endHeight    int64
   130  	blockIndexer indexer.BlockIndexer
   131  	txIndexer    txindex.TxIndexer
   132  	blockStore   state.BlockStore
   133  	stateStore   state.Store
   134  }
   135  
   136  func eventReIndex(cmd *cobra.Command, args eventReIndexArgs) error {
   137  	var bar progressbar.Bar
   138  	bar.NewOption(args.startHeight-1, args.endHeight)
   139  
   140  	fmt.Println("start re-indexing events:")
   141  	defer bar.Finish()
   142  	for i := args.startHeight; i <= args.endHeight; i++ {
   143  		select {
   144  		case <-cmd.Context().Done():
   145  			return fmt.Errorf("event re-index terminated at height %d: %w", i, cmd.Context().Err())
   146  		default:
   147  			b := args.blockStore.LoadBlock(i)
   148  			if b == nil {
   149  				return fmt.Errorf("not able to load block at height %d from the blockstore", i)
   150  			}
   151  
   152  			r, err := args.stateStore.LoadABCIResponses(i)
   153  			if err != nil {
   154  				return fmt.Errorf("not able to load ABCI Response at height %d from the statestore", i)
   155  			}
   156  
   157  			e := types.EventDataNewBlockHeader{
   158  				Header:           b.Header,
   159  				NumTxs:           int64(len(b.Txs)),
   160  				ResultBeginBlock: *r.BeginBlock,
   161  				ResultEndBlock:   *r.EndBlock,
   162  			}
   163  
   164  			var batch *txindex.Batch
   165  			if e.NumTxs > 0 {
   166  				batch = txindex.NewBatch(e.NumTxs)
   167  
   168  				for i := range b.Data.Txs {
   169  					tr := abcitypes.TxResult{
   170  						Height: b.Height,
   171  						Index:  uint32(i),
   172  						Tx:     b.Data.Txs[i],
   173  						Result: *(r.DeliverTxs[i]),
   174  					}
   175  
   176  					if err = batch.Add(&tr); err != nil {
   177  						return fmt.Errorf("adding tx to batch: %w", err)
   178  					}
   179  				}
   180  
   181  				if err := args.txIndexer.AddBatch(batch); err != nil {
   182  					return fmt.Errorf("tx event re-index at height %d failed: %w", i, err)
   183  				}
   184  			}
   185  
   186  			if err := args.blockIndexer.Index(e); err != nil {
   187  				return fmt.Errorf("block event re-index at height %d failed: %w", i, err)
   188  			}
   189  		}
   190  
   191  		bar.Play(i)
   192  	}
   193  
   194  	return nil
   195  }
   196  
   197  func checkValidHeight(bs state.BlockStore) error {
   198  	base := bs.Base()
   199  
   200  	if startHeight == 0 {
   201  		startHeight = base
   202  		fmt.Printf("set the start block height to the base height of the blockstore %d \n", base)
   203  	}
   204  
   205  	if startHeight < base {
   206  		return fmt.Errorf("%s (requested start height: %d, base height: %d)",
   207  			ErrHeightNotAvailable, startHeight, base)
   208  	}
   209  
   210  	height := bs.Height()
   211  
   212  	if startHeight > height {
   213  		return fmt.Errorf(
   214  			"%s (requested start height: %d, store height: %d)", ErrHeightNotAvailable, startHeight, height)
   215  	}
   216  
   217  	if endHeight == 0 || endHeight > height {
   218  		endHeight = height
   219  		fmt.Printf("set the end block height to the latest height of the blockstore %d \n", height)
   220  	}
   221  
   222  	if endHeight < base {
   223  		return fmt.Errorf(
   224  			"%s (requested end height: %d, base height: %d)", ErrHeightNotAvailable, endHeight, base)
   225  	}
   226  
   227  	if endHeight < startHeight {
   228  		return fmt.Errorf(
   229  			"%s (requested the end height: %d is less than the start height: %d)",
   230  			ErrInvalidRequest, startHeight, endHeight)
   231  	}
   232  
   233  	return nil
   234  }