github.com/PikeEcosystem/tendermint@v0.0.4/state/txindex/indexer_service.go (about) 1 package txindex 2 3 import ( 4 "context" 5 6 abci "github.com/tendermint/tendermint/abci/types" 7 8 tmabci "github.com/PikeEcosystem/tendermint/abci/types" 9 "github.com/PikeEcosystem/tendermint/libs/service" 10 "github.com/PikeEcosystem/tendermint/state/indexer" 11 "github.com/PikeEcosystem/tendermint/types" 12 ) 13 14 // XXX/TODO: These types should be moved to the indexer package. 15 16 const ( 17 subscriber = "IndexerService" 18 ) 19 20 // IndexerService connects event bus, transaction and block indexers together in 21 // order to index transactions and blocks coming from the event bus. 22 type IndexerService struct { 23 service.BaseService 24 25 txIdxr TxIndexer 26 blockIdxr indexer.BlockIndexer 27 eventBus *types.EventBus 28 } 29 30 // NewIndexerService returns a new service instance. 31 func NewIndexerService( 32 txIdxr TxIndexer, 33 blockIdxr indexer.BlockIndexer, 34 eventBus *types.EventBus, 35 ) *IndexerService { 36 37 is := &IndexerService{txIdxr: txIdxr, blockIdxr: blockIdxr, eventBus: eventBus} 38 is.BaseService = *service.NewBaseService(nil, "IndexerService", is) 39 return is 40 } 41 42 // OnStart implements service.Service by subscribing for all transactions 43 // and indexing them by events. 44 func (is *IndexerService) OnStart() error { 45 // Use SubscribeUnbuffered here to ensure both subscriptions does not get 46 // canceled due to not pulling messages fast enough. Cause this might 47 // sometimes happen when there are no other subscribers. 48 blockHeadersSub, err := is.eventBus.SubscribeUnbuffered( 49 context.Background(), 50 subscriber, 51 types.EventQueryNewBlockHeader) 52 if err != nil { 53 return err 54 } 55 56 txsSub, err := is.eventBus.SubscribeUnbuffered(context.Background(), subscriber, types.EventQueryTx) 57 if err != nil { 58 return err 59 } 60 61 go func() { 62 for { 63 msg := <-blockHeadersSub.Out() 64 eventDataHeader := msg.Data().(types.EventDataNewBlockHeader) 65 height := eventDataHeader.Header.Height 66 batch := NewBatch(eventDataHeader.NumTxs) 67 68 for i := int64(0); i < eventDataHeader.NumTxs; i++ { 69 msg2 := <-txsSub.Out() 70 txResult := msg2.Data().(types.EventDataTx).TxResult 71 72 if err = batch.Add(&txResult); err != nil { 73 is.Logger.Error( 74 "failed to add tx to batch", 75 "height", height, 76 "index", txResult.Index, 77 "err", err, 78 ) 79 } 80 } 81 82 if err := is.blockIdxr.Index(eventDataHeader); err != nil { 83 is.Logger.Error("failed to index block", "height", height, "err", err) 84 } else { 85 is.Logger.Info("indexed block", "height", height) 86 } 87 88 batch.Ops, err = DeduplicateBatch(batch.Ops, is.txIdxr) 89 if err != nil { 90 is.Logger.Error("deduplicate batch", "height", height) 91 } 92 93 if err = is.txIdxr.AddBatch(batch); err != nil { 94 is.Logger.Error("failed to index block txs", "height", height, "err", err) 95 } else { 96 is.Logger.Debug("indexed block txs", "height", height, "num_txs", eventDataHeader.NumTxs) 97 } 98 } 99 }() 100 return nil 101 } 102 103 // OnStop implements service.Service by unsubscribing from all transactions. 104 func (is *IndexerService) OnStop() { 105 if is.eventBus.IsRunning() { 106 _ = is.eventBus.UnsubscribeAll(context.Background(), subscriber) 107 } 108 } 109 110 // DeduplicateBatch consider the case of duplicate txs. 111 // if the current one under investigation is NOT OK, then we need to check 112 // whether there's a previously indexed tx. 113 // SKIP the current tx if the previously indexed record is found and successful. 114 func DeduplicateBatch(ops []*abci.TxResult, txIdxr TxIndexer) ([]*abci.TxResult, error) { 115 result := make([]*abci.TxResult, 0, len(ops)) 116 117 // keep track of successful txs in this block in order to suppress latter ones being indexed. 118 var successfulTxsInThisBlock = make(map[string]struct{}) 119 120 for _, txResult := range ops { 121 hash := types.Tx(txResult.Tx).Hash() 122 123 if txResult.Result.IsOK() { 124 successfulTxsInThisBlock[string(hash)] = struct{}{} 125 } else { 126 // if it already appeared in current block and was successful, skip. 127 if _, found := successfulTxsInThisBlock[string(hash)]; found { 128 continue 129 } 130 131 // check if this tx hash is already indexed 132 old, err := txIdxr.Get(hash) 133 134 // if db op errored 135 // Not found is not an error 136 if err != nil { 137 return nil, err 138 } 139 140 // if it's already indexed in an older block and was successful, skip. 141 if old != nil && old.Result.Code == tmabci.CodeTypeOK { 142 continue 143 } 144 } 145 146 result = append(result, txResult) 147 } 148 149 return result, nil 150 }