github.com/vipernet-xyz/tm@v0.34.24/cmd/tendermint/commands/reindex_event.go (about)

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