-
Notifications
You must be signed in to change notification settings - Fork 20.9k
core/filtermaps: fix map renderer reorg issue #31642
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
Changes from 5 commits
d40607c
dbf620d
756a6f8
11e654e
cdaa79a
5004973
9d35412
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -84,7 +84,7 @@ func (f *FilterMaps) renderMapsBefore(renderBefore uint32) (*mapRenderer, error) | |
if err != nil { | ||
return nil, err | ||
} | ||
if snapshot := f.lastCanonicalSnapshotBefore(renderBefore); snapshot != nil && snapshot.mapIndex >= nextMap { | ||
if snapshot := f.lastCanonicalSnapshotOfMap(nextMap); snapshot != nil { | ||
return f.renderMapsFromSnapshot(snapshot) | ||
} | ||
if nextMap >= renderBefore { | ||
|
@@ -97,14 +97,14 @@ func (f *FilterMaps) renderMapsBefore(renderBefore uint32) (*mapRenderer, error) | |
// snapshot made at a block boundary. | ||
func (f *FilterMaps) renderMapsFromSnapshot(cp *renderedMap) (*mapRenderer, error) { | ||
f.testSnapshotUsed = true | ||
iter, err := f.newLogIteratorFromBlockDelimiter(cp.lastBlock) | ||
iter, err := f.newLogIteratorFromBlockDelimiter(cp.lastBlock, cp.headDelimiter) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to create log iterator from block delimiter %d: %v", cp.lastBlock, err) | ||
} | ||
return &mapRenderer{ | ||
f: f, | ||
currentMap: &renderedMap{ | ||
filterMap: cp.filterMap.copy(), | ||
filterMap: cp.filterMap.fullCopy(), | ||
mapIndex: cp.mapIndex, | ||
lastBlock: cp.lastBlock, | ||
blockLvPtrs: cp.blockLvPtrs, | ||
|
@@ -137,14 +137,14 @@ func (f *FilterMaps) renderMapsFromMapBoundary(firstMap, renderBefore uint32, st | |
}, nil | ||
} | ||
|
||
// lastCanonicalSnapshotBefore returns the latest cached snapshot that matches | ||
// the current targetView. | ||
func (f *FilterMaps) lastCanonicalSnapshotBefore(renderBefore uint32) *renderedMap { | ||
// lastCanonicalSnapshotOfMap returns the latest cached snapshot of the given map | ||
// that is also consistent with the current targetView. | ||
func (f *FilterMaps) lastCanonicalSnapshotOfMap(mapIndex uint32) *renderedMap { | ||
var best *renderedMap | ||
for _, blockNumber := range f.renderSnapshots.Keys() { | ||
if cp, _ := f.renderSnapshots.Get(blockNumber); cp != nil && blockNumber < f.indexedRange.blocks.AfterLast() && | ||
blockNumber <= f.targetView.headNumber && f.targetView.getBlockId(blockNumber) == cp.lastBlockId && | ||
cp.mapIndex < renderBefore && (best == nil || blockNumber > best.lastBlock) { | ||
cp.mapIndex == mapIndex && (best == nil || blockNumber > best.lastBlock) { | ||
best = cp | ||
} | ||
} | ||
|
@@ -171,10 +171,9 @@ func (f *FilterMaps) lastCanonicalMapBoundaryBefore(renderBefore uint32) (nextMa | |
if err != nil { | ||
return 0, 0, 0, fmt.Errorf("failed to retrieve last block of reverse iterated map %d: %v", mapIndex, err) | ||
} | ||
if lastBlock >= f.indexedView.headNumber || lastBlock >= f.targetView.headNumber || | ||
lastBlockId != f.targetView.getBlockId(lastBlock) { | ||
// map is not full or inconsistent with targetView; roll back | ||
continue | ||
if (f.indexedRange.headIndexed && mapIndex >= f.indexedRange.maps.Last()) || | ||
lastBlock >= f.targetView.headNumber || lastBlockId != f.targetView.getBlockId(lastBlock) { | ||
continue // map is not full or inconsistent with targetView; roll back | ||
} | ||
lvPtr, err := f.getBlockLvPointer(lastBlock) | ||
if err != nil { | ||
|
@@ -257,10 +256,13 @@ 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{ | ||
filterMap: r.currentMap.filterMap.copy(), | ||
if r.iterator.blockNumber != r.currentMap.lastBlock { | ||
panic("iterator state inconsistent with last block") | ||
} | ||
r.f.renderSnapshots.Add(r.currentMap.lastBlock, &renderedMap{ | ||
filterMap: r.currentMap.filterMap.fastCopy(), | ||
mapIndex: r.currentMap.mapIndex, | ||
lastBlock: r.iterator.blockNumber, | ||
lastBlock: r.currentMap.lastBlock, | ||
lastBlockId: r.f.targetView.getBlockId(r.currentMap.lastBlock), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Honestly it's a bit weird to get the block id with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are right, it does look better. It is an assumption that the two should always be the same, guaranteed by |
||
blockLvPtrs: r.currentMap.blockLvPtrs, | ||
finished: true, | ||
|
@@ -661,24 +663,13 @@ var errUnindexedRange = errors.New("unindexed range") | |
// newLogIteratorFromBlockDelimiter creates a logIterator starting at the | ||
// given block's first log value entry (the block delimiter), according to the | ||
// current targetView. | ||
func (f *FilterMaps) newLogIteratorFromBlockDelimiter(blockNumber uint64) (*logIterator, error) { | ||
func (f *FilterMaps) newLogIteratorFromBlockDelimiter(blockNumber, lvIndex uint64) (*logIterator, error) { | ||
if blockNumber > f.targetView.headNumber { | ||
return nil, fmt.Errorf("iterator entry point %d after target chain head block %d", blockNumber, f.targetView.headNumber) | ||
} | ||
if !f.indexedRange.blocks.Includes(blockNumber) { | ||
return nil, errUnindexedRange | ||
} | ||
var lvIndex uint64 | ||
if f.indexedRange.headIndexed && blockNumber+1 == f.indexedRange.blocks.AfterLast() { | ||
lvIndex = f.indexedRange.headDelimiter | ||
} else { | ||
var err error | ||
lvIndex, err = f.getBlockLvPointer(blockNumber + 1) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to retrieve log value pointer of block %d after delimiter: %v", blockNumber+1, err) | ||
} | ||
lvIndex-- | ||
} | ||
finished := blockNumber == f.targetView.headNumber | ||
l := &logIterator{ | ||
chainView: f.targetView, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modification to the fast copy will affect the original map;
Appending to the fast copy won't affect the original map, isn't it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically true but the point is you should not append to both as they share the underlying array and would corrupt each other. In my use case the original keeps being appended as it is the current rendered map and snapsnots are made during the process. So I defined the semantics of
fastCopy
so that the original can be appended to and the copy should be used as read only. This rule guarantees safety.