1717package filtermaps
1818
1919import (
20+ "errors"
2021 "math"
2122 "time"
2223
@@ -49,18 +50,15 @@ func (f *FilterMaps) indexerLoop() {
4950 continue
5051 }
5152 if err := f .init (); err != nil {
52- log .Error ("Error initializing log index; reverting to unindexed mode" , "error" , err )
53- f .reset ()
54- f .disabled = true
55- close (f .disabledCh )
53+ f .disableForError ("initialization" , err )
54+ f .reset () // remove broken index from DB
5655 return
5756 }
5857 }
5958 if ! f .targetHeadIndexed () {
60- if ! f .tryIndexHead () {
61- // either shutdown or unexpected error; in the latter case ensure
62- // that proper shutdown is still possible.
63- f .processSingleEvent (true )
59+ if err := f .tryIndexHead (); err != nil && err != errChainUpdate {
60+ f .disableForError ("head rendering" , err )
61+ return
6462 }
6563 } else {
6664 if f .finalBlock != f .lastFinal {
@@ -69,13 +67,36 @@ func (f *FilterMaps) indexerLoop() {
6967 }
7068 f .lastFinal = f .finalBlock
7169 }
72- if f .tryIndexTail () && f .tryUnindexTail () {
73- f .waitForNewHead ()
70+ if done , err := f .tryIndexTail (); err != nil {
71+ f .disableForError ("tail rendering" , err )
72+ return
73+ } else if ! done {
74+ continue
75+ }
76+ if done , err := f .tryUnindexTail (); err != nil {
77+ f .disableForError ("tail unindexing" , err )
78+ return
79+ } else if ! done {
80+ continue
7481 }
82+ // tail indexing/unindexing is done; if head is also indexed then
83+ // wait here until there is a new head
84+ f .waitForNewHead ()
7585 }
7686 }
7787}
7888
89+ // disableForError is called when the indexer encounters a database error, for example a
90+ // missing receipt. We can't continue operating when the database is broken, so the
91+ // indexer goes into disabled state.
92+ // Note that the partial index is left in disk; maybe a client update can fix the
93+ // issue without reindexing.
94+ func (f * FilterMaps ) disableForError (op string , err error ) {
95+ log .Error ("Log index " + op + " failed, reverting to unindexed mode" , "error" , err )
96+ f .disabled = true
97+ close (f .disabledCh )
98+ }
99+
79100type targetUpdate struct {
80101 targetView * ChainView
81102 historyCutoff , finalBlock uint64
@@ -116,14 +137,15 @@ func (f *FilterMaps) SetBlockProcessing(blockProcessing bool) {
116137// WaitIdle blocks until the indexer is in an idle state while synced up to the
117138// latest targetView.
118139func (f * FilterMaps ) WaitIdle () {
119- if f .disabled {
120- f .closeWg .Wait ()
121- return
122- }
123140 for {
124141 ch := make (chan bool )
125- f .waitIdleCh <- ch
126- if <- ch {
142+ select {
143+ case f .waitIdleCh <- ch :
144+ if <- ch {
145+ return
146+ }
147+ case <- f .disabledCh :
148+ f .closeWg .Wait ()
127149 return
128150 }
129151 }
@@ -196,16 +218,16 @@ func (f *FilterMaps) setTarget(target targetUpdate) {
196218 f .finalBlock = target .finalBlock
197219}
198220
199- // tryIndexHead tries to render head maps according to the current targetView
200- // and returns true if successful.
201- func (f * FilterMaps ) tryIndexHead () bool {
221+ // tryIndexHead tries to render head maps according to the current targetView.
222+ // Should be called when targetHeadIndexed returns false. If this function
223+ // returns no error then either stop is true or head indexing is finished.
224+ func (f * FilterMaps ) tryIndexHead () error {
202225 headRenderer , err := f .renderMapsBefore (math .MaxUint32 )
203226 if err != nil {
204- log .Error ("Error creating log index head renderer" , "error" , err )
205- return false
227+ return err
206228 }
207229 if headRenderer == nil {
208- return true
230+ return errors . New ( "head indexer has nothing to do" ) // tryIndexHead should be called when head is not indexed
209231 }
210232 if ! f .startedHeadIndex {
211233 f .lastLogHeadIndex = time .Now ()
@@ -230,8 +252,7 @@ func (f *FilterMaps) tryIndexHead() bool {
230252 f .lastLogHeadIndex = time .Now ()
231253 }
232254 }); err != nil {
233- log .Error ("Log index head rendering failed" , "error" , err )
234- return false
255+ return err
235256 }
236257 if f .loggedHeadIndex && f .indexedRange .hasIndexedBlocks () {
237258 log .Info ("Log index head rendering finished" ,
@@ -240,23 +261,23 @@ func (f *FilterMaps) tryIndexHead() bool {
240261 "elapsed" , common .PrettyDuration (time .Since (f .startedHeadIndexAt )))
241262 }
242263 f .loggedHeadIndex , f .startedHeadIndex = false , false
243- return true
264+ return nil
244265}
245266
246267// tryIndexTail tries to render tail epochs until the tail target block is
247268// indexed and returns true if successful.
248269// Note that tail indexing is only started if the log index head is fully
249270// rendered according to targetView and is suspended as soon as the targetView
250271// is changed.
251- func (f * FilterMaps ) tryIndexTail () bool {
272+ func (f * FilterMaps ) tryIndexTail () ( bool , error ) {
252273 for {
253274 firstEpoch := f .indexedRange .maps .First () >> f .logMapsPerEpoch
254275 if firstEpoch == 0 || ! f .needTailEpoch (firstEpoch - 1 ) {
255276 break
256277 }
257278 f .processEvents ()
258279 if f .stop || ! f .targetHeadIndexed () {
259- return false
280+ return false , nil
260281 }
261282 // resume process if tail rendering was interrupted because of head rendering
262283 tailRenderer := f .tailRenderer
@@ -268,8 +289,7 @@ func (f *FilterMaps) tryIndexTail() bool {
268289 var err error
269290 tailRenderer , err = f .renderMapsBefore (f .indexedRange .maps .First ())
270291 if err != nil {
271- log .Error ("Error creating log index tail renderer" , "error" , err )
272- return false
292+ return false , err
273293 }
274294 }
275295 if tailRenderer == nil {
@@ -302,13 +322,16 @@ func (f *FilterMaps) tryIndexTail() bool {
302322 f .lastLogTailIndex = time .Now ()
303323 }
304324 })
305- if err != nil && f .needTailEpoch (firstEpoch - 1 ) {
325+ if err != nil && ! f .needTailEpoch (firstEpoch - 1 ) {
306326 // stop silently if cutoff point has move beyond epoch boundary while rendering
307- log .Error ("Log index tail rendering failed" , "error" , err )
327+ return true , nil
328+ }
329+ if err != nil {
330+ return false , err
308331 }
309332 if ! done {
310333 f .tailRenderer = tailRenderer // only keep tail renderer if interrupted by stopCb
311- return false
334+ return false , nil
312335 }
313336 }
314337 if f .loggedTailIndex && f .indexedRange .hasIndexedBlocks () {
@@ -318,22 +341,22 @@ func (f *FilterMaps) tryIndexTail() bool {
318341 "elapsed" , common .PrettyDuration (time .Since (f .startedTailIndexAt )))
319342 f .loggedTailIndex = false
320343 }
321- return true
344+ return true , nil
322345}
323346
324347// tryUnindexTail removes entire epochs of log index data as long as the first
325348// fully indexed block is at least as old as the tail target.
326349// Note that unindexing is very quick as it only removes continuous ranges of
327350// data from the database and is also called while running head indexing.
328- func (f * FilterMaps ) tryUnindexTail () bool {
351+ func (f * FilterMaps ) tryUnindexTail () ( bool , error ) {
329352 for {
330353 firstEpoch := (f .indexedRange .maps .First () - f .indexedRange .tailPartialEpoch ) >> f .logMapsPerEpoch
331354 if f .needTailEpoch (firstEpoch ) {
332355 break
333356 }
334357 f .processEvents ()
335358 if f .stop {
336- return false
359+ return false , nil
337360 }
338361 if ! f .startedTailUnindex {
339362 f .startedTailUnindexAt = time .Now ()
@@ -343,7 +366,7 @@ func (f *FilterMaps) tryUnindexTail() bool {
343366 }
344367 if err := f .deleteTailEpoch (firstEpoch ); err != nil {
345368 log .Error ("Log index tail epoch unindexing failed" , "error" , err )
346- return false
369+ return false , err
347370 }
348371 }
349372 if f .startedTailUnindex && f .indexedRange .hasIndexedBlocks () {
@@ -354,7 +377,7 @@ func (f *FilterMaps) tryUnindexTail() bool {
354377 "elapsed" , common .PrettyDuration (time .Since (f .startedTailUnindexAt )))
355378 f .startedTailUnindex = false
356379 }
357- return true
380+ return true , nil
358381}
359382
360383// needTailEpoch returns true if the given tail epoch needs to be kept
0 commit comments