Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Add offset support to MemoryFreezer #1965
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
base: develop
Are you sure you want to change the base?
Add offset support to MemoryFreezer #1965
Changes from 7 commits
3b32cf66097d13f74d4ac9716b100cd3033907e9aad917c1e11439caFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
There are no files selected for viewing
Check failure on line 276 in core/rawdb/freezer_memory.go
AncientBytes left non-offset-aware while Ancient/AncientRange were updated
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.
🔴 MemoryFreezer.AncientBytes (core/rawdb/freezer_memory.go:467-490) was not updated to be offset-aware, while this PR adds offset translation to Ancient and AncientRange. With offset > 0, AncientBytes silently disagrees with Ancient/AncientRange: e.g. AncientBytes("test", 12, 0, n) on a freezer with offset=10 and items at absolute ids 10..12 hits the t.items<=start guard and returns errOutOfBounds, while AncientBytes("test", 2, 0, n) — below the absolute start — would silently return data for what is actually item 12. Fix: subtract f.offset.Load() from id with the same underflow guard the PR adds for Ancient/AncientRange/TruncateHead/TruncateTail, and add a TestMemoryFreezerOffset case for AncientBytes.
Extended reasoning...
The bug
This PR makes
MemoryFreezeroffset-aware, updatingAncient,AncientRange,TruncateHead,TruncateTail,ModifyAncients,Resetand the write-batch initializer to translate between absolute item numbers and table-relative indices viaf.offset.Load(). However, the parallel readerMemoryFreezer.AncientBytes(core/rawdb/freezer_memory.go:467-490) — which is not in the diff — was not updated. It still callstable.retrieve(id, 1, 0)with the absolute id.After this PR,
AncientBytesis the onlyAncientReaderOpmethod left non-offset-aware, so aMemoryFreezerconstructed withoffset > 0produces inconsistent results across its reader methods.Step-by-step proof
Construct
f := NewMemoryFreezer(false, 10, {"test": …})and append three items at absolute ids 10, 11, 12 (matching the newTestMemoryFreezerOffset). After commit, the underlyingmemoryTablehast.items = 3andt.offset = 0(thememoryTable.itemsfield is incremented only bycommitand starts at 0 — unlike the freezer-levelf.itemswhich now includes the offset).f.Ancient("test", 12)→ updated path:number=12,offset=10, callst.retrieve(12-10, 1, 0) = t.retrieve(2, 1, 0)→ returns"twelve". ✓f.AncientBytes("test", 12, 0, n)→ unchanged path: callst.retrieve(12, 1, 0)→ trips thet.items <= startguard (3 <= 12) → returnserrOutOfBounds. ✗ (disagrees withAncient)f.AncientBytes("test", 2, 0, n)(an id below the freezer's absolute start of 10) → callst.retrieve(2, 1, 0)→ silently returns"twelve"(the entry at relative index 2, i.e. absolute id 12). ✗ (worse: wrong data, not even an error)Why existing code does not prevent it
The new
TestMemoryFreezerOffsetexercisesAncient,AncientRange,TruncateHead,TruncateTail, andResetagainst offset=10, but does not callAncientBytes, so this gap is not caught.chainFreezer.AncientBytes(core/rawdb/chain_freezer.go:451) is a direct pass-through, andnewChainFreezernow wires a non-zero offset intoNewMemoryFreezerfor the in-memory chain freezer case, so the inconsistency is reachable through the publicethdb.AncientStoreinterface even if no current caller ofAncientByteshappens to use an offset != 0 today (state/trienode constructors still pass 0; chain reads currently route throughAncient, notAncientBytes).Impact
Latent today (no current production caller triggers it), but a real correctness/consistency bug introduced by this PR's offset refactor. Any future caller — or any future code path that routes chain-table reads through
AncientByteswhile the chain freezer is configured with a pruning offset — would either silently read the wrong item (whenid < offset) or get a spuriouserrOutOfBounds(whenid >= offset). The contract that all reader methods agree on absolute ids is broken on the same struct.Suggested fix
Apply the same translation and underflow guard already used for
AncientandAncientRange:And extend
TestMemoryFreezerOffsetwithAncientBytes("test", 12, …)succeeding andAncientBytes("test", 9, …)returningerrOutOfBounds, mirroring the existingAncient/AncientRangeassertions.Check failure on line 51 in core/rawdb/freezer_memory_test.go
Refactor this method to reduce its Cognitive Complexity from 36 to the 15 allowed.
Uh oh!
There was an error while loading. Please reload this page.