bitbucket.org/number571/tendermint@v0.8.14/state/indexer/indexer_service.go (about) 1 package indexer 2 3 import ( 4 "context" 5 6 "bitbucket.org/number571/tendermint/libs/service" 7 "bitbucket.org/number571/tendermint/types" 8 ) 9 10 // XXX/TODO: These types should be moved to the indexer package. 11 12 const ( 13 subscriber = "IndexerService" 14 ) 15 16 // Service connects event bus, transaction and block indexers together in 17 // order to index transactions and blocks coming from the event bus. 18 type Service struct { 19 service.BaseService 20 21 eventSinks []EventSink 22 eventBus *types.EventBus 23 } 24 25 // NewIndexerService returns a new service instance. 26 func NewIndexerService(es []EventSink, eventBus *types.EventBus) *Service { 27 28 is := &Service{eventSinks: es, eventBus: eventBus} 29 is.BaseService = *service.NewBaseService(nil, "IndexerService", is) 30 return is 31 } 32 33 // OnStart implements service.Service by subscribing for all transactions 34 // and indexing them by events. 35 func (is *Service) OnStart() error { 36 // Use SubscribeUnbuffered here to ensure both subscriptions does not get 37 // canceled due to not pulling messages fast enough. Cause this might 38 // sometimes happen when there are no other subscribers. 39 blockHeadersSub, err := is.eventBus.SubscribeUnbuffered( 40 context.Background(), 41 subscriber, 42 types.EventQueryNewBlockHeader) 43 if err != nil { 44 return err 45 } 46 47 txsSub, err := is.eventBus.SubscribeUnbuffered(context.Background(), subscriber, types.EventQueryTx) 48 if err != nil { 49 return err 50 } 51 52 go func() { 53 for { 54 msg := <-blockHeadersSub.Out() 55 56 eventDataHeader := msg.Data().(types.EventDataNewBlockHeader) 57 height := eventDataHeader.Header.Height 58 batch := NewBatch(eventDataHeader.NumTxs) 59 60 for i := int64(0); i < eventDataHeader.NumTxs; i++ { 61 msg2 := <-txsSub.Out() 62 txResult := msg2.Data().(types.EventDataTx).TxResult 63 64 if err = batch.Add(&txResult); err != nil { 65 is.Logger.Error( 66 "failed to add tx to batch", 67 "height", height, 68 "index", txResult.Index, 69 "err", err, 70 ) 71 } 72 } 73 74 if !IndexingEnabled(is.eventSinks) { 75 continue 76 } 77 78 for _, sink := range is.eventSinks { 79 if err := sink.IndexBlockEvents(eventDataHeader); err != nil { 80 is.Logger.Error("failed to index block", "height", height, "err", err) 81 } else { 82 is.Logger.Debug("indexed block", "height", height, "sink", sink.Type()) 83 } 84 85 if len(batch.Ops) > 0 { 86 err := sink.IndexTxEvents(batch.Ops) 87 if err != nil { 88 is.Logger.Error("failed to index block txs", "height", height, "err", err) 89 } else { 90 is.Logger.Debug("indexed txs", "height", height, "sink", sink.Type()) 91 } 92 } 93 } 94 } 95 }() 96 return nil 97 } 98 99 // OnStop implements service.Service by unsubscribing from all transactions and 100 // close the eventsink. 101 func (is *Service) OnStop() { 102 if is.eventBus.IsRunning() { 103 _ = is.eventBus.UnsubscribeAll(context.Background(), subscriber) 104 } 105 106 for _, sink := range is.eventSinks { 107 if err := sink.Stop(); err != nil { 108 is.Logger.Error("failed to close eventsink", "eventsink", sink.Type(), "err", err) 109 } 110 } 111 } 112 113 // KVSinkEnabled returns the given eventSinks is containing KVEventSink. 114 func KVSinkEnabled(sinks []EventSink) bool { 115 for _, sink := range sinks { 116 if sink.Type() == KV { 117 return true 118 } 119 } 120 121 return false 122 } 123 124 // IndexingEnabled returns the given eventSinks is supporting the indexing services. 125 func IndexingEnabled(sinks []EventSink) bool { 126 for _, sink := range sinks { 127 if sink.Type() == KV || sink.Type() == PSQL { 128 return true 129 } 130 } 131 132 return false 133 }