-
Notifications
You must be signed in to change notification settings - Fork 20.2k
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
core/state: set-based journalling #30660
base: master
Are you sure you want to change the base?
Conversation
dcbf781
to
696edfa
Compare
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.
Random nits for now
6ca0931
to
32cd8a4
Compare
936622f
to
e896dac
Compare
Lifting this to top level
So, now I have made (actually, still working on it) the api cleaner, journal:
StateDB
This had quite a large fallout, and also made for some large changes (simplifications) inside the existing linear journal. The result is that after these changes, I am not 100% confident that it won't blow up in some corner-case where the journal is empty / reset, and something calls revert or discard. I intentionally did not check for out of bounds, because I want to locate the source of such flaws. One such source is when the state processor applies a tx: it either becomes finalized, in which case the journal will be reset, since cross-tx reverting is not allowed. However, if it fails, then the caller (outside) needs to revert it. So that's one point where, although the API is symmetric, the call pattern is not. So, my plan now: let the tests run, then put it on benchmarkers for a spin. |
e896dac
to
e6f3517
Compare
I tried to depoy this on
|
Deploying now, so
|
I was running Gary’s pr
…On Thu, 7 Nov 2024 at 13:31, Martin HS ***@***.***> wrote:
Deploying now, so
- bench05: this PR, but using linear_journal
- bench06: this PR, but using setjournal
—
Reply to this email directly, view it on GitHub
<#30660 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAA7UGN6TTGHA7JCOLAD2IDZ7NMRRAVCNFSM6AAAAABQOC4ON6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDINRSGEYTSMRTGI>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Has been running fine for ~5 days. Nothing noteworthy with regards to differences in charts on the two machines. |
Yes, the db version is 9, so it's confirmed! |
e6f3517
to
f5d8372
Compare
core/state: add handling for DiscardSnapshot core/state: use new journal core/state, genesis: fix flaw re discard/commit. In case the state is committed, the journal is reset, thus it is not correct to Discard/Revert snapshots at that point. core/state: fix nil defer in merge core/state: fix bugs in setjournal core/state: journal api changes core/state: bugfixes in sparse journal core/state: journal tests core/state: improve post-state check in journal-fuzzing test core/state: post-rebase fixups miner: remove discard-snapshot call, it's not needed since journal will be reset in Finalize core/state: fix tests core/state: lint core/state: supply origin-value when reverting storage change Update core/genesis.go core/state: fix erroneous comments core/state: review-nits regarding the journal
f5d8372
to
79b00d1
Compare
// This is fine | ||
return | ||
} | ||
j.revisions = j.revisions[:id] |
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.
This can panic with runtime error: slice bounds out of range [:-1]
if you try to discard on an empty journal. I think this is was also the behavior before the change, but it was made explicitly then, while this is not so easy to spot. Maybe we should be more explicit about it here
|
||
// revertSnapshot reverts all state changes made since the last call to snapshot(). | ||
func (j *linearJournal) revertSnapshot(s *StateDB) { | ||
id := len(j.revisions) - 1 |
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.
Same here, this will panic if there was no previous call to snapshot()
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.
Interestingly enough the set based journaling will not panic on revertSnapshot or discardSnapshot
|
||
// revertSnapshot reverts all state changes made since the last call to snapshot(). | ||
func (j *sparseJournal) revertSnapshot(s *StateDB) { | ||
id := len(j.entries) - 1 |
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.
If you call revertSnapshot on the top-most snapshot, you will end up with a broken journal, since the initial (hidden) snapshot will be reverted. I don't think its intended behavior, since all subsequent calls will panics
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.
A fix would be to call s.snapshot()
if id == 0
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.
That looks to me like a fix which just fixes it because it somehow works, but I don't think it makes sense semantically. Why not just no-op if caller tries to revert when there's nothing to revert?
That is, return
if len(j.entries) == 0
?
I built a little fuzzer for differential fuzzing the two journals against each other here: https://github.com/MariusVanDerWijden/go-ethereum/tree/journal_on_heapfix_fuzz, its in the first commit, I also added some things on top, so that the fuzzer doesn't panic |
Nice! We could add it to this PR, but please make it so that it's deterministic from the fuzzer input. So don't use |
This is a second attempt at #30500 .
This PR introduces set-based journalling, where the journalling-events are basically stored in per-scoped maps.
Whenever we enter a new call scope, we create a new scoped journal. Changes within the same scoped journal overwrite eachother. For example: if a scope updates a balance to from 6 to 5 then 4, then 3, there will only be one preimage in the journal. As opposed to the old journal (linear_journal), which would have multiple entries: [ prev: 6, prev: 5, prev:4].
The linear / appending journal is wasteful on memory, and also slow on rollbacks, since each change is rolled back individually.
@karalabe reminded me of the burntpix benchmark, on which this PR excels, so I thought it was worth another chance.
master
:This PR:
Allocations,
915K
->30K
,Allocated bytes:
175M
->30M