core/filtermaps: revert to unindexed mode in case of indexing error

This commit is contained in:
Zsolt Felfoldi 2025-03-26 12:58:08 +01:00
parent c49aadc4b6
commit 2d469a4ace
2 changed files with 53 additions and 28 deletions

View file

@ -83,7 +83,11 @@ func (cv *ChainView) getReceipts(number uint64) types.Receipts {
if number > cv.headNumber { if number > cv.headNumber {
panic("invalid block number") panic("invalid block number")
} }
return cv.chain.GetReceiptsByHash(cv.blockHash(number)) blockHash := cv.blockHash(number)
if blockHash == (common.Hash{}) {
log.Error("Chain view: block hash unavailable", "number", number, "head", cv.headNumber)
}
return cv.chain.GetReceiptsByHash(blockHash)
} }
// limitedView returns a new chain view that is a truncated version of the parent view. // limitedView returns a new chain view that is a truncated version of the parent view.

View file

@ -17,6 +17,7 @@
package filtermaps package filtermaps
import ( import (
"errors"
"math" "math"
"time" "time"
@ -57,10 +58,13 @@ func (f *FilterMaps) indexerLoop() {
} }
} }
if !f.targetHeadIndexed() { if !f.targetHeadIndexed() {
if !f.tryIndexHead() { if err := f.tryIndexHead(); err != nil {
// either shutdown or unexpected error; in the latter case ensure log.Error("Log index head rendering failed; reverting to unindexed mode", "error", err)
// that proper shutdown is still possible. // disable indexer without resetting database; maybe a client
f.processSingleEvent(true) // update can fix the issue without reindexing
f.disabled = true
close(f.disabledCh)
return
} }
} else { } else {
if f.finalBlock != f.lastFinal { if f.finalBlock != f.lastFinal {
@ -69,9 +73,25 @@ func (f *FilterMaps) indexerLoop() {
} }
f.lastFinal = f.finalBlock f.lastFinal = f.finalBlock
} }
if f.tryIndexTail() && f.tryUnindexTail() { if done, err := f.tryIndexTail(); err != nil {
f.waitForNewHead() log.Error("Log index tail rendering failed; reverting to unindexed mode", "error", err)
f.disabled = true
close(f.disabledCh)
return
} else if !done {
continue
} }
if done, err := f.tryUnindexTail(); err != nil {
log.Error("Log index tail unindexing failed; reverting to unindexed mode", "error", err)
f.disabled = true
close(f.disabledCh)
return
} else if !done {
continue
}
// tail indexing/unindexing is done; if head is also indexed then
// wait here until there is a new head
f.waitForNewHead()
} }
} }
} }
@ -196,16 +216,16 @@ func (f *FilterMaps) setTarget(target targetUpdate) {
f.finalBlock = target.finalBlock f.finalBlock = target.finalBlock
} }
// tryIndexHead tries to render head maps according to the current targetView // tryIndexHead tries to render head maps according to the current targetView.
// and returns true if successful. // Should be called when targetHeadIndexed returns false. If this function
func (f *FilterMaps) tryIndexHead() bool { // returns no error then either stop is true or head indexing is finished.
func (f *FilterMaps) tryIndexHead() error {
headRenderer, err := f.renderMapsBefore(math.MaxUint32) headRenderer, err := f.renderMapsBefore(math.MaxUint32)
if err != nil { if err != nil {
log.Error("Error creating log index head renderer", "error", err) return err
return false
} }
if headRenderer == nil { if headRenderer == nil {
return true return errors.New("head indexer has nothing to do") // tryIndexHead should be called when head is not indexed
} }
if !f.startedHeadIndex { if !f.startedHeadIndex {
f.lastLogHeadIndex = time.Now() f.lastLogHeadIndex = time.Now()
@ -230,8 +250,7 @@ func (f *FilterMaps) tryIndexHead() bool {
f.lastLogHeadIndex = time.Now() f.lastLogHeadIndex = time.Now()
} }
}); err != nil { }); err != nil {
log.Error("Log index head rendering failed", "error", err) return err
return false
} }
if f.loggedHeadIndex && f.indexedRange.hasIndexedBlocks() { if f.loggedHeadIndex && f.indexedRange.hasIndexedBlocks() {
log.Info("Log index head rendering finished", log.Info("Log index head rendering finished",
@ -240,7 +259,7 @@ func (f *FilterMaps) tryIndexHead() bool {
"elapsed", common.PrettyDuration(time.Since(f.startedHeadIndexAt))) "elapsed", common.PrettyDuration(time.Since(f.startedHeadIndexAt)))
} }
f.loggedHeadIndex, f.startedHeadIndex = false, false f.loggedHeadIndex, f.startedHeadIndex = false, false
return true return nil
} }
// tryIndexTail tries to render tail epochs until the tail target block is // tryIndexTail tries to render tail epochs until the tail target block is
@ -248,7 +267,7 @@ func (f *FilterMaps) tryIndexHead() bool {
// Note that tail indexing is only started if the log index head is fully // Note that tail indexing is only started if the log index head is fully
// rendered according to targetView and is suspended as soon as the targetView // rendered according to targetView and is suspended as soon as the targetView
// is changed. // is changed.
func (f *FilterMaps) tryIndexTail() bool { func (f *FilterMaps) tryIndexTail() (bool, error) {
for { for {
firstEpoch := f.indexedRange.maps.First() >> f.logMapsPerEpoch firstEpoch := f.indexedRange.maps.First() >> f.logMapsPerEpoch
if firstEpoch == 0 || !f.needTailEpoch(firstEpoch-1) { if firstEpoch == 0 || !f.needTailEpoch(firstEpoch-1) {
@ -256,7 +275,7 @@ func (f *FilterMaps) tryIndexTail() bool {
} }
f.processEvents() f.processEvents()
if f.stop || !f.targetHeadIndexed() { if f.stop || !f.targetHeadIndexed() {
return false return false, nil
} }
// resume process if tail rendering was interrupted because of head rendering // resume process if tail rendering was interrupted because of head rendering
tailRenderer := f.tailRenderer tailRenderer := f.tailRenderer
@ -268,8 +287,7 @@ func (f *FilterMaps) tryIndexTail() bool {
var err error var err error
tailRenderer, err = f.renderMapsBefore(f.indexedRange.maps.First()) tailRenderer, err = f.renderMapsBefore(f.indexedRange.maps.First())
if err != nil { if err != nil {
log.Error("Error creating log index tail renderer", "error", err) return false, err
return false
} }
} }
if tailRenderer == nil { if tailRenderer == nil {
@ -302,13 +320,16 @@ func (f *FilterMaps) tryIndexTail() bool {
f.lastLogTailIndex = time.Now() f.lastLogTailIndex = time.Now()
} }
}) })
if err != nil && f.needTailEpoch(firstEpoch-1) { if err != nil && !f.needTailEpoch(firstEpoch-1) {
// stop silently if cutoff point has move beyond epoch boundary while rendering // stop silently if cutoff point has move beyond epoch boundary while rendering
log.Error("Log index tail rendering failed", "error", err) return true, nil
}
if err != nil {
return false, err
} }
if !done { if !done {
f.tailRenderer = tailRenderer // only keep tail renderer if interrupted by stopCb f.tailRenderer = tailRenderer // only keep tail renderer if interrupted by stopCb
return false return false, nil
} }
} }
if f.loggedTailIndex && f.indexedRange.hasIndexedBlocks() { if f.loggedTailIndex && f.indexedRange.hasIndexedBlocks() {
@ -318,14 +339,14 @@ func (f *FilterMaps) tryIndexTail() bool {
"elapsed", common.PrettyDuration(time.Since(f.startedTailIndexAt))) "elapsed", common.PrettyDuration(time.Since(f.startedTailIndexAt)))
f.loggedTailIndex = false f.loggedTailIndex = false
} }
return true return true, nil
} }
// tryUnindexTail removes entire epochs of log index data as long as the first // tryUnindexTail removes entire epochs of log index data as long as the first
// fully indexed block is at least as old as the tail target. // fully indexed block is at least as old as the tail target.
// Note that unindexing is very quick as it only removes continuous ranges of // Note that unindexing is very quick as it only removes continuous ranges of
// data from the database and is also called while running head indexing. // data from the database and is also called while running head indexing.
func (f *FilterMaps) tryUnindexTail() bool { func (f *FilterMaps) tryUnindexTail() (bool, error) {
for { for {
firstEpoch := (f.indexedRange.maps.First() - f.indexedRange.tailPartialEpoch) >> f.logMapsPerEpoch firstEpoch := (f.indexedRange.maps.First() - f.indexedRange.tailPartialEpoch) >> f.logMapsPerEpoch
if f.needTailEpoch(firstEpoch) { if f.needTailEpoch(firstEpoch) {
@ -333,7 +354,7 @@ func (f *FilterMaps) tryUnindexTail() bool {
} }
f.processEvents() f.processEvents()
if f.stop { if f.stop {
return false return false, nil
} }
if !f.startedTailUnindex { if !f.startedTailUnindex {
f.startedTailUnindexAt = time.Now() f.startedTailUnindexAt = time.Now()
@ -343,7 +364,7 @@ func (f *FilterMaps) tryUnindexTail() bool {
} }
if err := f.deleteTailEpoch(firstEpoch); err != nil { if err := f.deleteTailEpoch(firstEpoch); err != nil {
log.Error("Log index tail epoch unindexing failed", "error", err) log.Error("Log index tail epoch unindexing failed", "error", err)
return false return false, err
} }
} }
if f.startedTailUnindex && f.indexedRange.hasIndexedBlocks() { if f.startedTailUnindex && f.indexedRange.hasIndexedBlocks() {
@ -354,7 +375,7 @@ func (f *FilterMaps) tryUnindexTail() bool {
"elapsed", common.PrettyDuration(time.Since(f.startedTailUnindexAt))) "elapsed", common.PrettyDuration(time.Since(f.startedTailUnindexAt)))
f.startedTailUnindex = false f.startedTailUnindex = false
} }
return true return true, nil
} }
// needTailEpoch returns true if the given tail epoch needs to be kept // needTailEpoch returns true if the given tail epoch needs to be kept