17
17
package filtermaps
18
18
19
19
import (
20
+ "errors"
20
21
"math"
21
22
"time"
22
23
@@ -49,18 +50,15 @@ func (f *FilterMaps) indexerLoop() {
49
50
continue
50
51
}
51
52
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
56
55
return
57
56
}
58
57
}
59
58
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
64
62
}
65
63
} else {
66
64
if f .finalBlock != f .lastFinal {
@@ -69,13 +67,36 @@ func (f *FilterMaps) indexerLoop() {
69
67
}
70
68
f .lastFinal = f .finalBlock
71
69
}
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
74
81
}
82
+ // tail indexing/unindexing is done; if head is also indexed then
83
+ // wait here until there is a new head
84
+ f .waitForNewHead ()
75
85
}
76
86
}
77
87
}
78
88
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
+
79
100
type targetUpdate struct {
80
101
targetView * ChainView
81
102
historyCutoff , finalBlock uint64
@@ -116,14 +137,15 @@ func (f *FilterMaps) SetBlockProcessing(blockProcessing bool) {
116
137
// WaitIdle blocks until the indexer is in an idle state while synced up to the
117
138
// latest targetView.
118
139
func (f * FilterMaps ) WaitIdle () {
119
- if f .disabled {
120
- f .closeWg .Wait ()
121
- return
122
- }
123
140
for {
124
141
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 ()
127
149
return
128
150
}
129
151
}
@@ -196,16 +218,16 @@ func (f *FilterMaps) setTarget(target targetUpdate) {
196
218
f .finalBlock = target .finalBlock
197
219
}
198
220
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 {
202
225
headRenderer , err := f .renderMapsBefore (math .MaxUint32 )
203
226
if err != nil {
204
- log .Error ("Error creating log index head renderer" , "error" , err )
205
- return false
227
+ return err
206
228
}
207
229
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
209
231
}
210
232
if ! f .startedHeadIndex {
211
233
f .lastLogHeadIndex = time .Now ()
@@ -230,8 +252,7 @@ func (f *FilterMaps) tryIndexHead() bool {
230
252
f .lastLogHeadIndex = time .Now ()
231
253
}
232
254
}); err != nil {
233
- log .Error ("Log index head rendering failed" , "error" , err )
234
- return false
255
+ return err
235
256
}
236
257
if f .loggedHeadIndex && f .indexedRange .hasIndexedBlocks () {
237
258
log .Info ("Log index head rendering finished" ,
@@ -240,23 +261,23 @@ func (f *FilterMaps) tryIndexHead() bool {
240
261
"elapsed" , common .PrettyDuration (time .Since (f .startedHeadIndexAt )))
241
262
}
242
263
f .loggedHeadIndex , f .startedHeadIndex = false , false
243
- return true
264
+ return nil
244
265
}
245
266
246
267
// tryIndexTail tries to render tail epochs until the tail target block is
247
268
// indexed and returns true if successful.
248
269
// Note that tail indexing is only started if the log index head is fully
249
270
// rendered according to targetView and is suspended as soon as the targetView
250
271
// is changed.
251
- func (f * FilterMaps ) tryIndexTail () bool {
272
+ func (f * FilterMaps ) tryIndexTail () ( bool , error ) {
252
273
for {
253
274
firstEpoch := f .indexedRange .maps .First () >> f .logMapsPerEpoch
254
275
if firstEpoch == 0 || ! f .needTailEpoch (firstEpoch - 1 ) {
255
276
break
256
277
}
257
278
f .processEvents ()
258
279
if f .stop || ! f .targetHeadIndexed () {
259
- return false
280
+ return false , nil
260
281
}
261
282
// resume process if tail rendering was interrupted because of head rendering
262
283
tailRenderer := f .tailRenderer
@@ -268,8 +289,7 @@ func (f *FilterMaps) tryIndexTail() bool {
268
289
var err error
269
290
tailRenderer , err = f .renderMapsBefore (f .indexedRange .maps .First ())
270
291
if err != nil {
271
- log .Error ("Error creating log index tail renderer" , "error" , err )
272
- return false
292
+ return false , err
273
293
}
274
294
}
275
295
if tailRenderer == nil {
@@ -302,13 +322,16 @@ func (f *FilterMaps) tryIndexTail() bool {
302
322
f .lastLogTailIndex = time .Now ()
303
323
}
304
324
})
305
- if err != nil && f .needTailEpoch (firstEpoch - 1 ) {
325
+ if err != nil && ! f .needTailEpoch (firstEpoch - 1 ) {
306
326
// 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
308
331
}
309
332
if ! done {
310
333
f .tailRenderer = tailRenderer // only keep tail renderer if interrupted by stopCb
311
- return false
334
+ return false , nil
312
335
}
313
336
}
314
337
if f .loggedTailIndex && f .indexedRange .hasIndexedBlocks () {
@@ -318,22 +341,22 @@ func (f *FilterMaps) tryIndexTail() bool {
318
341
"elapsed" , common .PrettyDuration (time .Since (f .startedTailIndexAt )))
319
342
f .loggedTailIndex = false
320
343
}
321
- return true
344
+ return true , nil
322
345
}
323
346
324
347
// tryUnindexTail removes entire epochs of log index data as long as the first
325
348
// fully indexed block is at least as old as the tail target.
326
349
// Note that unindexing is very quick as it only removes continuous ranges of
327
350
// 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 ) {
329
352
for {
330
353
firstEpoch := (f .indexedRange .maps .First () - f .indexedRange .tailPartialEpoch ) >> f .logMapsPerEpoch
331
354
if f .needTailEpoch (firstEpoch ) {
332
355
break
333
356
}
334
357
f .processEvents ()
335
358
if f .stop {
336
- return false
359
+ return false , nil
337
360
}
338
361
if ! f .startedTailUnindex {
339
362
f .startedTailUnindexAt = time .Now ()
@@ -343,7 +366,7 @@ func (f *FilterMaps) tryUnindexTail() bool {
343
366
}
344
367
if err := f .deleteTailEpoch (firstEpoch ); err != nil {
345
368
log .Error ("Log index tail epoch unindexing failed" , "error" , err )
346
- return false
369
+ return false , err
347
370
}
348
371
}
349
372
if f .startedTailUnindex && f .indexedRange .hasIndexedBlocks () {
@@ -354,7 +377,7 @@ func (f *FilterMaps) tryUnindexTail() bool {
354
377
"elapsed" , common .PrettyDuration (time .Since (f .startedTailUnindexAt )))
355
378
f .startedTailUnindex = false
356
379
}
357
- return true
380
+ return true , nil
358
381
}
359
382
360
383
// needTailEpoch returns true if the given tail epoch needs to be kept
0 commit comments