Skip to content

Commit b55c629

Browse files
committed
core/filtermaps: properly handle history cutoff
1 parent 91a85de commit b55c629

File tree

3 files changed

+50
-17
lines changed

3 files changed

+50
-17
lines changed

core/filtermaps/filtermaps.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ const (
4141
cachedRenderSnapshots = 8 // saved map renderer data at block boundaries
4242
)
4343

44+
var errHistoryCutoff = errors.New("cannot start indexing before history cutoff point")
45+
4446
// FilterMaps is the in-memory representation of the log index structure that is
4547
// responsible for building and updating the index according to the canonical
4648
// chain.
@@ -278,8 +280,13 @@ func (f *FilterMaps) initChainView(chainView *ChainView) *ChainView {
278280
}
279281

280282
// reset un-initializes the FilterMaps structure and removes all related data from
281-
// the database. The function returns true if everything was successfully removed.
282-
func (f *FilterMaps) reset() bool {
283+
// the database.
284+
// Note that in case of leveldb database the fallback implementation of DeleteRange
285+
// might take a long time to finish and deleting the entire database may be
286+
// interrupted by a shutdown. Deleting the filterMapsRange entry first does
287+
// guarantee though that the next init() will not return successfully until the
288+
// entire database has been cleaned.
289+
func (f *FilterMaps) reset() {
283290
f.indexLock.Lock()
284291
f.indexedRange = filterMapsRange{}
285292
f.indexedView = nil
@@ -292,11 +299,16 @@ func (f *FilterMaps) reset() bool {
292299
// deleting the range first ensures that resetDb will be called again at next
293300
// startup and any leftover data will be removed even if it cannot finish now.
294301
rawdb.DeleteFilterMapsRange(f.db)
295-
return f.safeDeleteRange(rawdb.DeleteFilterMapsDb, "Resetting log index database")
302+
f.safeDeleteRange(rawdb.DeleteFilterMapsDb, "Resetting log index database")
296303
}
297304

298305
// init initializes an empty log index according to the current targetView.
299306
func (f *FilterMaps) init() error {
307+
// ensure that there is no remaining data in the filter maps key range
308+
if !f.safeDeleteRange(rawdb.DeleteFilterMapsDb, "Resetting log index database") {
309+
return errors.New("could not reset log index database")
310+
}
311+
300312
f.indexLock.Lock()
301313
defer f.indexLock.Unlock()
302314

@@ -317,6 +329,13 @@ func (f *FilterMaps) init() error {
317329
bestIdx, bestLen = idx, max
318330
}
319331
}
332+
var initBlockNumber uint64
333+
if bestLen > 0 {
334+
initBlockNumber = checkpoints[bestIdx][bestLen-1].BlockNumber
335+
}
336+
if initBlockNumber < f.historyCutoff {
337+
return errHistoryCutoff
338+
}
320339
batch := f.db.NewBatch()
321340
for epoch := range bestLen {
322341
cp := checkpoints[bestIdx][epoch]

core/filtermaps/indexer.go

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ func (f *FilterMaps) indexerLoop() {
4848
continue
4949
}
5050
if err := f.init(); err != nil {
51+
if err == errHistoryCutoff {
52+
log.Warn("History cutoff point beyond latest checkpoint; log index disabled")
53+
f.disabled = true
54+
f.reset()
55+
return
56+
}
5157
log.Error("Error initializing log index", "error", err)
5258
// unexpected error; there is not a lot we can do here, maybe it
5359
// recovers, maybe not. Calling event processing here ensures
@@ -57,10 +63,16 @@ func (f *FilterMaps) indexerLoop() {
5763
}
5864
}
5965
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)
66+
if _, err := f.tryIndexHead(); err != nil {
67+
switch err {
68+
case errHistoryCutoff:
69+
log.Warn("History cutoff point beyond rendered index head; resetting log index")
70+
f.reset() // still attempt re-initializing; maybe there are more recent checkpoints
71+
default:
72+
// unexpected error; ensure that we can still properly
73+
// shutdown in case of an infinite loop.
74+
f.processSingleEvent(true)
75+
}
6476
}
6577
} else {
6678
if f.finalBlock != f.lastFinal {
@@ -198,22 +210,22 @@ func (f *FilterMaps) setTarget(target targetUpdate) {
198210

199211
// tryIndexHead tries to render head maps according to the current targetView
200212
// and returns true if successful.
201-
func (f *FilterMaps) tryIndexHead() bool {
213+
func (f *FilterMaps) tryIndexHead() (bool, error) {
202214
headRenderer, err := f.renderMapsBefore(math.MaxUint32)
203215
if err != nil {
204216
log.Error("Error creating log index head renderer", "error", err)
205-
return false
217+
return false, err
206218
}
207219
if headRenderer == nil {
208-
return true
220+
return true, nil
209221
}
210222
if !f.startedHeadIndex {
211223
f.lastLogHeadIndex = time.Now()
212224
f.startedHeadIndexAt = f.lastLogHeadIndex
213225
f.startedHeadIndex = true
214226
f.ptrHeadIndex = f.indexedRange.blocks.AfterLast()
215227
}
216-
if _, err := headRenderer.run(func() bool {
228+
if done, err := headRenderer.run(func() bool {
217229
f.processEvents()
218230
return f.stop
219231
}, func() {
@@ -229,9 +241,11 @@ func (f *FilterMaps) tryIndexHead() bool {
229241
f.loggedHeadIndex = true
230242
f.lastLogHeadIndex = time.Now()
231243
}
232-
}); err != nil {
233-
log.Error("Log index head rendering failed", "error", err)
234-
return false
244+
}); !done {
245+
if err != nil {
246+
log.Error("Log index head rendering failed", "error", err)
247+
}
248+
return false, err
235249
}
236250
if f.loggedHeadIndex && f.indexedRange.hasIndexedBlocks() {
237251
log.Info("Log index head rendering finished",
@@ -240,7 +254,7 @@ func (f *FilterMaps) tryIndexHead() bool {
240254
"elapsed", common.PrettyDuration(time.Since(f.startedHeadIndexAt)))
241255
}
242256
f.loggedHeadIndex, f.startedHeadIndex = false, false
243-
return true
257+
return true, nil
244258
}
245259

246260
// tryIndexTail tries to render tail epochs until the tail target block is

core/filtermaps/map_renderer.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -681,7 +681,7 @@ func (f *FilterMaps) newLogIteratorFromMapBoundary(mapIndex uint32, startBlock,
681681
// get block receipts
682682
receipts := f.targetView.getReceipts(startBlock)
683683
if receipts == nil {
684-
return nil, fmt.Errorf("receipts not found for start block %d", startBlock)
684+
return nil, errHistoryCutoff
685685
}
686686
// initialize iterator at block start
687687
l := &logIterator{
@@ -748,7 +748,7 @@ func (l *logIterator) next() error {
748748
l.blockNumber++
749749
l.receipts = l.chainView.getReceipts(l.blockNumber)
750750
if l.receipts == nil {
751-
return fmt.Errorf("receipts not found for block %d", l.blockNumber)
751+
return errHistoryCutoff
752752
}
753753
l.txIndex, l.logIndex, l.topicIndex, l.blockStart = 0, 0, 0, true
754754
} else {

0 commit comments

Comments
 (0)