Skip to content

eth/filters, core/filtermaps: safe chain view update #31590

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Apr 20, 2025

Conversation

zsfelfoldi
Copy link
Contributor

@zsfelfoldi zsfelfoldi commented Apr 8, 2025

This PR changes the chain view update mechanism of the log filter. Previously the head updates were all wired through the indexer, even in unindexed mode. This was both a bit weird and also unsafe as the indexer's chain view was updates asynchronously with some delay, making some log related tests flaky. Also, the reorg safety of the indexed search was integrated with unindexed search in a weird way, relying on syncRange.ValidBlocks in the unindexed case too, with a special condition added to only consider the head of the valid range but not the tail in the unindexed case.

In this PR the current chain view is directly accessible through the filter backend and unindexed search is also chain view based, making it inherently safe. The matcher sync mechanism is now only used for indexed search as originally intended, removing a few ugly special conditions.

The PR is currently based on top of #31642
Together they fix #31518 and replace #31542

@zsfelfoldi zsfelfoldi changed the title eth/filters: safe chain view update (WIP) eth/filters: safe chain view update Apr 10, 2025
@zsfelfoldi zsfelfoldi changed the title eth/filters: safe chain view update eth/filters: safe chain view update (WIP) Apr 14, 2025
@zsfelfoldi zsfelfoldi force-pushed the filter-head-update branch 2 times, most recently from 888367a to 844e795 Compare April 15, 2025 01:03
@zsfelfoldi zsfelfoldi changed the title eth/filters: safe chain view update (WIP) eth/filters, core/filtermaps: safe head updates and reorgs Apr 15, 2025
@rjl493456442
Copy link
Member

My general feeling is that the chain view management was
implemented incorrectly from the beginning.

Whenever a potential reorg occurs, the current implementation
updates the new chain view in the FilterMaps without explicitly
notifying components like (a) the indexer and (b) the matcher
synchronously.

Instead, the new chain view is updated asynchronously, and
any potentially invalid indexed data or matched results are
removed afterward.

This approach may introduce a number of issues while offering
little to no real benefit.

Ideally, it should be implemented that: once reorg occurs, immediately
terminate the ongoing indexer/matcher if they are affected, discarding
the affected data and resume.

@zsfelfoldi zsfelfoldi changed the title eth/filters, core/filtermaps: safe head updates and reorgs eth/filters, core/filtermaps: safe chain view update Apr 15, 2025
@zsfelfoldi zsfelfoldi force-pushed the filter-head-update branch 3 times, most recently from 8cf5ffa to 9bd1f9a Compare April 18, 2025 12:38
@zsfelfoldi
Copy link
Contributor Author

My general feeling is that the chain view management was implemented incorrectly from the beginning.

Whenever a potential reorg occurs, the current implementation updates the new chain view in the FilterMaps without explicitly notifying components like (a) the indexer and (b) the matcher synchronously.

Blocking SetTarget would have its own dangers. It's always a bit tricky with any kind of extra chain processing as even blocking a chain subscription can disrupt the entire client operation.
The indexer does pretty much what you expect though, it checks for target updates on the channel frequently and discards the map rendering immediately if the iterator is already past the point where the new target forks off from the previous one.
The matching could be done differently for sure but the thing is that for efficiency reasons the entire matching process is epoch based and one epoch contains tens of thousands of blocks so canceling the processing of the latest epoch just because the last 1-2 blocks have been reorged might not be the most efficient.

eth/filters: discard cached matching result if the error occurs
s.filter.rangeLogsTestHook <- rangeLogsTestEvent{rangeLogsTestSynced, s.syncRange.ValidBlocks}
}
// discard everything that might be invalid
trimRange := s.syncRange.ValidBlocks.Intersection(s.chainView.SharedRange(s.syncRange.IndexedView))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I just confirm that

s.syncRange.ValidBlocks is always consistent with the result returned by s.filter.indexedLogs right?

Copy link
Member

@rjl493456442 rjl493456442 Apr 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, probably not.

e.g., the indexed view was A->B, then A->B->C->D; then it's changed to A->B'->C'->D';

The match backend was initialized with at A->B; the s.syncRange.ValidBlocks returned is A (B was removed due to indexed view reorg). s.syncRange.ValidBlocks is aligned with the indexed view.

However, the indexed view might be forked with the current block chain (e.g. chain is rewound to A'->B''->C''->D'') and this view hasn't be propagated into the indexer yet.
In this case, the s.syncRange.ValidBlocks will be trimmed by the intersection of indexed view and chain view.

This subset should be totally trusted and canonical.

@zsfelfoldi is it correct?

Copy link
Contributor Author

@zsfelfoldi zsfelfoldi Apr 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's correct. The block number range in syncRange.ValidBlocks always refers to syncRange.IndexedView which is usually the same as the local reference view s.chainView but has this small delay because of async indexing so in case they are different the guaranteed correct range is trimmed further so that s.matchRange and s.matches always stay consistent with s.chainView.
The indexer's event handler will check for new heads before the new matcher sync iteration so in the next sync the two views will hopefully match and the results of the next search iteration will all be considered safe and the search can be successfully finished (unless of course there is a next reorg happening very quickly).

rjl493456442
rjl493456442 previously approved these changes Apr 19, 2025
Copy link
Member

@rjl493456442 rjl493456442 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’ve tried my best to review this PR, and it looks good to me.

However, my understanding of this package isn’t very comprehensive, so I can’t guarantee that the new filtermap implementation is completely issue free.

But the flaky test appears to be resolved, which is a good sign.

@zsfelfoldi zsfelfoldi merged commit 7f57437 into ethereum:master Apr 20, 2025
4 checks passed
sivaratrisrinivas pushed a commit to sivaratrisrinivas/go-ethereum that referenced this pull request Apr 21, 2025
This PR changes the chain view update mechanism of the log filter.
Previously the head updates were all wired through the indexer, even in
unindexed mode. This was both a bit weird and also unsafe as the
indexer's chain view was updates asynchronously with some delay, making
some log related tests flaky. Also, the reorg safety of the indexed
search was integrated with unindexed search in a weird way, relying on
`syncRange.ValidBlocks` in the unindexed case too, with a special
condition added to only consider the head of the valid range but not the
tail in the unindexed case.

In this PR the current chain view is directly accessible through the
filter backend and unindexed search is also chain view based, making it
inherently safe. The matcher sync mechanism is now only used for indexed
search as originally intended, removing a few ugly special conditions.

The PR is currently based on top of
ethereum#31642
Together they fix ethereum#31518
and replace ethereum#31542

---------

Co-authored-by: Gary Rong <[email protected]>
0g-wh pushed a commit to 0glabs/0g-geth that referenced this pull request Apr 22, 2025
This PR changes the chain view update mechanism of the log filter.
Previously the head updates were all wired through the indexer, even in
unindexed mode. This was both a bit weird and also unsafe as the
indexer's chain view was updates asynchronously with some delay, making
some log related tests flaky. Also, the reorg safety of the indexed
search was integrated with unindexed search in a weird way, relying on
`syncRange.ValidBlocks` in the unindexed case too, with a special
condition added to only consider the head of the valid range but not the
tail in the unindexed case.

In this PR the current chain view is directly accessible through the
filter backend and unindexed search is also chain view based, making it
inherently safe. The matcher sync mechanism is now only used for indexed
search as originally intended, removing a few ugly special conditions.

The PR is currently based on top of
ethereum#31642
Together they fix ethereum#31518
and replace ethereum#31542

---------

Co-authored-by: Gary Rong <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Log events are not found
2 participants