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

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

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
@@ -434,6 +434,7 @@ func DeleteBlockLvPointers(db ethdb.KeyValueStore, blocks common.Range[uint64],
// FilterMapsRange is a storage representation of the block range covered by the
// filter maps structure and the corresponting log value index range.
type FilterMapsRange struct {
Version uint32
Copy link
Member

Choose a reason for hiding this comment

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

This will invalidate the previously stored entry and reindex the stuff in next launch, just want to mention

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is intentional as also mentioned in the commit title, see also the description of #31642. The index has to be regenerated as it might be corrupted in a some cases.

@@ -125,7 +128,7 @@ func (fm *FilterMapsMatcherBackend) synced() {
indexedBlocks.SetAfterLast(indexedBlocks.Last()) // remove partially indexed last block
}
fm.syncCh <- SyncRange{
HeadNumber: fm.f.targetView.headNumber,
IndexedView: fm.f.indexedView,
Copy link
Member

Choose a reason for hiding this comment

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

Generally I feel it's very wrong.

Theoretically whenever reorg occurs, the affected data should be discarded, either from the uncommitted memory or persisted state. And then resume.

For the ongoing matching, also, the affected matched results should be discarded and then resume.

The whole reorg management feels very wonky to me.

@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.

@@ -257,11 +256,14 @@ func (f *FilterMaps) loadHeadSnapshot() error {

// makeSnapshot creates a snapshot of the current state of the rendered map.
func (r *mapRenderer) makeSnapshot() {
r.f.renderSnapshots.Add(r.iterator.blockNumber, &renderedMap{
if r.iterator.blockNumber != r.currentMap.lastBlock {
panic("iterator state inconsistent with last block")
Copy link
Member

Choose a reason for hiding this comment

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

Probably discard the snapshot instead of panic here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'd prefer to panic here because it is something that absolutely should not and will not happen; currentMap and iterator are both needed here and should be in a consistent state which is currently trivially ensured by the caller. If a future change ever breaks this then I think the louder and harder it breaks, the better.

@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
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