github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/cmd/tendermint/commands/reindex_event.go (about) 1 package commands 2 3 import ( 4 "errors" 5 "fmt" 6 "path/filepath" 7 "strings" 8 9 "github.com/spf13/cobra" 10 dbm "github.com/tendermint/tm-db" 11 12 abcitypes "github.com/ari-anchor/sei-tendermint/abci/types" 13 tmcfg "github.com/ari-anchor/sei-tendermint/config" 14 "github.com/ari-anchor/sei-tendermint/internal/libs/progressbar" 15 "github.com/ari-anchor/sei-tendermint/internal/state" 16 "github.com/ari-anchor/sei-tendermint/internal/state/indexer" 17 "github.com/ari-anchor/sei-tendermint/internal/state/indexer/sink/kv" 18 "github.com/ari-anchor/sei-tendermint/internal/state/indexer/sink/psql" 19 "github.com/ari-anchor/sei-tendermint/internal/store" 20 "github.com/ari-anchor/sei-tendermint/libs/log" 21 "github.com/ari-anchor/sei-tendermint/libs/os" 22 "github.com/ari-anchor/sei-tendermint/rpc/coretypes" 23 "github.com/ari-anchor/sei-tendermint/types" 24 ) 25 26 const ( 27 reindexFailed = "event re-index failed: " 28 ) 29 30 // MakeReindexEventCommand constructs a command to re-index events in a block height interval. 31 func MakeReindexEventCommand(conf *tmcfg.Config, logger log.Logger) *cobra.Command { 32 var ( 33 startHeight int64 34 endHeight int64 35 ) 36 37 cmd := &cobra.Command{ 38 Use: "reindex-event", 39 Short: "reindex events to the event store backends", 40 Long: ` 41 reindex-event is an offline tooling to re-index block and tx events to the eventsinks, 42 you can run this command when the event store backend dropped/disconnected or you want to 43 replace the backend. The default start-height is 0, meaning the tooling will start 44 reindex from the base block height(inclusive); and the default end-height is 0, meaning 45 the tooling will reindex until the latest block height(inclusive). User can omit 46 either or both arguments. 47 `, 48 Example: ` 49 tendermint reindex-event 50 tendermint reindex-event --start-height 2 51 tendermint reindex-event --end-height 10 52 tendermint reindex-event --start-height 2 --end-height 10 53 `, 54 RunE: func(cmd *cobra.Command, args []string) error { 55 bs, ss, err := loadStateAndBlockStore(conf) 56 if err != nil { 57 return fmt.Errorf("%s: %w", reindexFailed, err) 58 } 59 60 cvhArgs := checkValidHeightArgs{ 61 startHeight: startHeight, 62 endHeight: endHeight, 63 } 64 if err := checkValidHeight(bs, cvhArgs); err != nil { 65 return fmt.Errorf("%s: %w", reindexFailed, err) 66 } 67 68 es, err := loadEventSinks(conf) 69 if err != nil { 70 return fmt.Errorf("%s: %w", reindexFailed, err) 71 } 72 73 riArgs := eventReIndexArgs{ 74 startHeight: startHeight, 75 endHeight: endHeight, 76 sinks: es, 77 blockStore: bs, 78 stateStore: ss, 79 } 80 if err := eventReIndex(cmd, riArgs); err != nil { 81 return fmt.Errorf("%s: %w", reindexFailed, err) 82 } 83 84 logger.Info("event re-index finished") 85 return nil 86 }, 87 } 88 89 cmd.Flags().Int64Var(&startHeight, "start-height", 0, "the block height would like to start for re-index") 90 cmd.Flags().Int64Var(&endHeight, "end-height", 0, "the block height would like to finish for re-index") 91 return cmd 92 } 93 94 func loadEventSinks(cfg *tmcfg.Config) ([]indexer.EventSink, error) { 95 // Check duplicated sinks. 96 sinks := map[string]bool{} 97 for _, s := range cfg.TxIndex.Indexer { 98 sl := strings.ToLower(s) 99 if sinks[sl] { 100 return nil, errors.New("found duplicated sinks, please check the tx-index section in the config.toml") 101 } 102 sinks[sl] = true 103 } 104 105 eventSinks := []indexer.EventSink{} 106 107 for k := range sinks { 108 switch k { 109 case string(indexer.NULL): 110 return nil, errors.New("found null event sink, please check the tx-index section in the config.toml") 111 case string(indexer.KV): 112 store, err := tmcfg.DefaultDBProvider(&tmcfg.DBContext{ID: "tx_index", Config: cfg}) 113 if err != nil { 114 return nil, err 115 } 116 eventSinks = append(eventSinks, kv.NewEventSink(store)) 117 case string(indexer.PSQL): 118 conn := cfg.TxIndex.PsqlConn 119 if conn == "" { 120 return nil, errors.New("the psql connection settings cannot be empty") 121 } 122 es, err := psql.NewEventSink(conn, cfg.ChainID()) 123 if err != nil { 124 return nil, err 125 } 126 eventSinks = append(eventSinks, es) 127 default: 128 return nil, errors.New("unsupported event sink type") 129 } 130 } 131 132 if len(eventSinks) == 0 { 133 return nil, errors.New("no proper event sink can do event re-indexing," + 134 " please check the tx-index section in the config.toml") 135 } 136 137 if !indexer.IndexingEnabled(eventSinks) { 138 return nil, fmt.Errorf("no event sink has been enabled") 139 } 140 141 return eventSinks, nil 142 } 143 144 func loadStateAndBlockStore(cfg *tmcfg.Config) (*store.BlockStore, state.Store, error) { 145 dbType := dbm.BackendType(cfg.DBBackend) 146 147 if !os.FileExists(filepath.Join(cfg.DBDir(), "blockstore.db")) { 148 return nil, nil, fmt.Errorf("no blockstore found in %v", cfg.DBDir()) 149 } 150 151 // Get BlockStore 152 blockStoreDB, err := dbm.NewDB("blockstore", dbType, cfg.DBDir()) 153 if err != nil { 154 return nil, nil, err 155 } 156 blockStore := store.NewBlockStore(blockStoreDB) 157 158 if !os.FileExists(filepath.Join(cfg.DBDir(), "state.db")) { 159 return nil, nil, fmt.Errorf("no blockstore found in %v", cfg.DBDir()) 160 } 161 162 // Get StateStore 163 stateDB, err := dbm.NewDB("state", dbType, cfg.DBDir()) 164 if err != nil { 165 return nil, nil, err 166 } 167 stateStore := state.NewStore(stateDB) 168 169 return blockStore, stateStore, nil 170 } 171 172 type eventReIndexArgs struct { 173 startHeight int64 174 endHeight int64 175 sinks []indexer.EventSink 176 blockStore state.BlockStore 177 stateStore state.Store 178 } 179 180 func eventReIndex(cmd *cobra.Command, args eventReIndexArgs) error { 181 var bar progressbar.Bar 182 bar.NewOption(args.startHeight-1, args.endHeight) 183 184 fmt.Println("start re-indexing events:") 185 defer bar.Finish() 186 for i := args.startHeight; i <= args.endHeight; i++ { 187 select { 188 case <-cmd.Context().Done(): 189 return fmt.Errorf("event re-index terminated at height %d: %w", i, cmd.Context().Err()) 190 default: 191 b := args.blockStore.LoadBlock(i) 192 if b == nil { 193 return fmt.Errorf("not able to load block at height %d from the blockstore", i) 194 } 195 196 r, err := args.stateStore.LoadFinalizeBlockResponses(i) 197 if err != nil { 198 return fmt.Errorf("not able to load ABCI Response at height %d from the statestore", i) 199 } 200 201 e := types.EventDataNewBlockHeader{ 202 Header: b.Header, 203 NumTxs: int64(len(b.Txs)), 204 ResultFinalizeBlock: *r, 205 } 206 207 var batch *indexer.Batch 208 if e.NumTxs > 0 { 209 batch = indexer.NewBatch(e.NumTxs) 210 211 for i := range b.Data.Txs { 212 tr := abcitypes.TxResult{ 213 Height: b.Height, 214 Index: uint32(i), 215 Tx: b.Data.Txs[i], 216 Result: *(r.TxResults[i]), 217 } 218 219 _ = batch.Add(&tr) 220 } 221 } 222 223 for _, sink := range args.sinks { 224 if err := sink.IndexBlockEvents(e); err != nil { 225 return fmt.Errorf("block event re-index at height %d failed: %w", i, err) 226 } 227 228 if batch != nil { 229 if err := sink.IndexTxEvents(batch.Ops); err != nil { 230 return fmt.Errorf("tx event re-index at height %d failed: %w", i, err) 231 } 232 } 233 } 234 } 235 236 bar.Play(i) 237 } 238 239 return nil 240 } 241 242 type checkValidHeightArgs struct { 243 startHeight int64 244 endHeight int64 245 } 246 247 func checkValidHeight(bs state.BlockStore, args checkValidHeightArgs) error { 248 base := bs.Base() 249 250 if args.startHeight == 0 { 251 args.startHeight = base 252 fmt.Printf("set the start block height to the base height of the blockstore %d \n", base) 253 } 254 255 if args.startHeight < base { 256 return fmt.Errorf("%s (requested start height: %d, base height: %d)", 257 coretypes.ErrHeightNotAvailable, args.startHeight, base) 258 } 259 260 height := bs.Height() 261 262 if args.startHeight > height { 263 return fmt.Errorf( 264 "%s (requested start height: %d, store height: %d)", coretypes.ErrHeightNotAvailable, args.startHeight, height) 265 } 266 267 if args.endHeight == 0 || args.endHeight > height { 268 args.endHeight = height 269 fmt.Printf("set the end block height to the latest height of the blockstore %d \n", height) 270 } 271 272 if args.endHeight < base { 273 return fmt.Errorf( 274 "%s (requested end height: %d, base height: %d)", coretypes.ErrHeightNotAvailable, args.endHeight, base) 275 } 276 277 if args.endHeight < args.startHeight { 278 return fmt.Errorf( 279 "%s (requested the end height: %d is less than the start height: %d)", 280 coretypes.ErrInvalidRequest, args.startHeight, args.endHeight) 281 } 282 283 return nil 284 }