core/filtermaps: un-embedded matcher processing

This commit is contained in:
Zsolt Felfoldi 2025-03-14 10:41:26 +01:00
parent 8f2f37da67
commit 25b51f2940
2 changed files with 102 additions and 78 deletions

View file

@ -737,7 +737,6 @@ func (l *logIterator) next() error {
l.skipToBoundary = false l.skipToBoundary = false
} }
return nil return nil
} }
if l.finished { if l.finished {
return nil return nil

View file

@ -75,7 +75,6 @@ type SyncRange struct {
// Also note that the returned list may contain false positives. // Also note that the returned list may contain false positives.
func GetPotentialMatches(ctx context.Context, backend MatcherBackend, firstBlock, lastBlock uint64, addresses []common.Address, topics [][]common.Hash) ([]*types.Log, error) { func GetPotentialMatches(ctx context.Context, backend MatcherBackend, firstBlock, lastBlock uint64, addresses []common.Address, topics [][]common.Hash) ([]*types.Log, error) {
params := backend.GetParams() params := backend.GetParams()
var getLogStats runtimeStats
// find the log value index range to search // find the log value index range to search
firstIndex, err := backend.GetBlockLvPointer(ctx, firstBlock) firstIndex, err := backend.GetBlockLvPointer(ctx, firstBlock)
if err != nil { if err != nil {
@ -88,8 +87,6 @@ func GetPotentialMatches(ctx context.Context, backend MatcherBackend, firstBlock
if lastIndex > 0 { if lastIndex > 0 {
lastIndex-- lastIndex--
} }
firstMap, lastMap := uint32(firstIndex>>params.logValuesPerMap), uint32(lastIndex>>params.logValuesPerMap)
firstEpoch, lastEpoch := firstMap>>params.logMapsPerEpoch, lastMap>>params.logMapsPerEpoch
// build matcher according to the given filter criteria // build matcher according to the given filter criteria
matchers := make([]matcher, len(topics)+1) matchers := make([]matcher, len(topics)+1)
@ -117,45 +114,45 @@ func GetPotentialMatches(ctx context.Context, backend MatcherBackend, firstBlock
// matchers signal a match for consecutive log value indices. // matchers signal a match for consecutive log value indices.
matcher := newMatchSequence(params, matchers) matcher := newMatchSequence(params, matchers)
// processEpoch returns the potentially matching logs from the given epoch. m := &matcherEnv{
processEpoch := func(epochIndex uint32) ([]*types.Log, error) { ctx: ctx,
var logs []*types.Log backend: backend,
// create a list of map indices to process params: params,
fm, lm := epochIndex<<params.logMapsPerEpoch, (epochIndex+1)<<params.logMapsPerEpoch-1 matcher: matcher,
if fm < firstMap { firstIndex: firstIndex,
fm = firstMap lastIndex: lastIndex,
} firstMap: uint32(firstIndex >> params.logValuesPerMap),
if lm > lastMap { lastMap: uint32(lastIndex >> params.logValuesPerMap),
lm = lastMap
}
//
mapIndices := make([]uint32, lm+1-fm)
for i := range mapIndices {
mapIndices[i] = fm + uint32(i)
}
// find potential matches
matches, err := getAllMatches(ctx, matcher, mapIndices)
if err != nil {
return logs, err
}
// get the actual logs located at the matching log value indices
var st int
getLogStats.setState(&st, stGetLog)
defer getLogStats.setState(&st, stNone)
for _, m := range matches {
if m == nil {
return nil, ErrMatchAll
}
mlogs, err := getLogsFromMatches(ctx, backend, firstIndex, lastIndex, m)
if err != nil {
return logs, err
}
logs = append(logs, mlogs...)
}
getLogStats.addAmount(st, int64(len(logs)))
return logs, nil
} }
start := time.Now()
res, err := m.process()
if doRuntimeStats {
log.Info("Log search finished", "elapsed", time.Since(start))
for i, ma := range matchers {
for j, m := range ma.(matchAny) {
log.Info("Single matcher stats", "matchSequence", i, "matchAny", j)
m.(*singleMatcher).stats.print()
}
}
log.Info("Get log stats")
m.getLogStats.print()
}
return res, err
}
type matcherEnv struct {
ctx context.Context
backend MatcherBackend
params *Params
matcher matcher
getLogStats runtimeStats
firstIndex, lastIndex uint64
firstMap, lastMap uint32
}
func (m *matcherEnv) process() ([]*types.Log, error) {
type task struct { type task struct {
epochIndex uint32 epochIndex uint32
logs []*types.Log logs []*types.Log
@ -175,18 +172,18 @@ func GetPotentialMatches(ctx context.Context, backend MatcherBackend, firstBlock
if task == nil { if task == nil {
break break
} }
task.logs, task.err = processEpoch(task.epochIndex) task.logs, task.err = m.processEpoch(task.epochIndex)
close(task.done) close(task.done)
} }
wg.Done() wg.Done()
} }
start := time.Now()
for i := 0; i < 4; i++ { for i := 0; i < 4; i++ {
wg.Add(1) wg.Add(1)
go worker() go worker()
} }
firstEpoch, lastEpoch := m.firstMap>>m.params.logMapsPerEpoch, m.lastMap>>m.params.logMapsPerEpoch
var logs []*types.Log var logs []*types.Log
// startEpoch is the next task to send whenever a worker can accept it. // startEpoch is the next task to send whenever a worker can accept it.
// waitEpoch is the next task we are waiting for to finish in order to append // waitEpoch is the next task we are waiting for to finish in order to append
@ -220,30 +217,58 @@ func GetPotentialMatches(ctx context.Context, backend MatcherBackend, firstBlock
} }
} }
} }
if doRuntimeStats { return logs, nil
log.Info("Log search finished", "elapsed", time.Since(start))
for i, ma := range matchers {
for j, m := range ma.(matchAny) {
log.Info("Single matcher stats", "matchSequence", i, "matchAny", j)
m.(*singleMatcher).stats.print()
} }
// processEpoch returns the potentially matching logs from the given epoch.
func (m *matcherEnv) processEpoch(epochIndex uint32) ([]*types.Log, error) {
var logs []*types.Log
// create a list of map indices to process
fm, lm := epochIndex<<m.params.logMapsPerEpoch, (epochIndex+1)<<m.params.logMapsPerEpoch-1
if fm < m.firstMap {
fm = m.firstMap
} }
log.Info("Get log stats") if lm > m.lastMap {
getLogStats.print() lm = m.lastMap
} }
//
mapIndices := make([]uint32, lm+1-fm)
for i := range mapIndices {
mapIndices[i] = fm + uint32(i)
}
// find potential matches
matches, err := m.getAllMatches(mapIndices)
if err != nil {
return logs, err
}
// get the actual logs located at the matching log value indices
var st int
m.getLogStats.setState(&st, stGetLog)
defer m.getLogStats.setState(&st, stNone)
for _, match := range matches {
if match == nil {
return nil, ErrMatchAll
}
mlogs, err := m.getLogsFromMatches(match)
if err != nil {
return logs, err
}
logs = append(logs, mlogs...)
}
m.getLogStats.addAmount(st, int64(len(logs)))
return logs, nil return logs, nil
} }
// getLogsFromMatches returns the list of potentially matching logs located at // getLogsFromMatches returns the list of potentially matching logs located at
// the given list of matching log indices. Matches outside the firstIndex to // the given list of matching log indices. Matches outside the firstIndex to
// lastIndex range are not returned. // lastIndex range are not returned.
func getLogsFromMatches(ctx context.Context, backend MatcherBackend, firstIndex, lastIndex uint64, matches potentialMatches) ([]*types.Log, error) { func (m *matcherEnv) getLogsFromMatches(matches potentialMatches) ([]*types.Log, error) {
var logs []*types.Log var logs []*types.Log
for _, match := range matches { for _, match := range matches {
if match < firstIndex || match > lastIndex { if match < m.firstIndex || match > m.lastIndex {
continue continue
} }
log, err := backend.GetLogByLvIndex(ctx, match) log, err := m.backend.GetLogByLvIndex(m.ctx, match)
if err != nil { if err != nil {
return logs, fmt.Errorf("failed to retrieve log at index %d: %v", match, err) return logs, fmt.Errorf("failed to retrieve log at index %d: %v", match, err)
} }
@ -254,6 +279,28 @@ func getLogsFromMatches(ctx context.Context, backend MatcherBackend, firstIndex,
return logs, nil return logs, nil
} }
// getAllMatches creates an instance for a given matcher and set of map indices,
// iterates through mapping layers and collects all results, then returns all
// results in the same order as the map indices were specified.
func (m *matcherEnv) getAllMatches(mapIndices []uint32) ([]potentialMatches, error) {
instance := m.matcher.newInstance(mapIndices)
resultsMap := make(map[uint32]potentialMatches)
for layerIndex := uint32(0); len(resultsMap) < len(mapIndices); layerIndex++ {
results, err := instance.getMatchesForLayer(m.ctx, layerIndex)
if err != nil {
return nil, err
}
for _, result := range results {
resultsMap[result.mapIndex] = result.matches
}
}
matches := make([]potentialMatches, len(mapIndices))
for i, mapIndex := range mapIndices {
matches[i] = resultsMap[mapIndex]
}
return matches, nil
}
// matcher defines a general abstraction for any matcher configuration that // matcher defines a general abstraction for any matcher configuration that
// can instantiate a matcherInstance. // can instantiate a matcherInstance.
type matcher interface { type matcher interface {
@ -281,28 +328,6 @@ type matcherResult struct {
matches potentialMatches matches potentialMatches
} }
// getAllMatches creates an instance for a given matcher and set of map indices,
// iterates through mapping layers and collects all results, then returns all
// results in the same order as the map indices were specified.
func getAllMatches(ctx context.Context, matcher matcher, mapIndices []uint32) ([]potentialMatches, error) {
instance := matcher.newInstance(mapIndices)
resultsMap := make(map[uint32]potentialMatches)
for layerIndex := uint32(0); len(resultsMap) < len(mapIndices); layerIndex++ {
results, err := instance.getMatchesForLayer(ctx, layerIndex)
if err != nil {
return nil, err
}
for _, result := range results {
resultsMap[result.mapIndex] = result.matches
}
}
matches := make([]potentialMatches, len(mapIndices))
for i, mapIndex := range mapIndices {
matches[i] = resultsMap[mapIndex]
}
return matches, nil
}
// singleMatcher implements matcher by returning matches for a single log value hash. // singleMatcher implements matcher by returning matches for a single log value hash.
type singleMatcher struct { type singleMatcher struct {
backend MatcherBackend backend MatcherBackend