-
Notifications
You must be signed in to change notification settings - Fork 75
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
refactor(sequencer): generate SequencerBlock
after transaction execution in proposal phase
#1562
Conversation
…ock can be constructed during proposal
// cache of results of executing of transactions in `process_proposal`. | ||
// cleared at the end of the block. | ||
finalize_block: Option<abci::response::FinalizeBlock>, |
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.
Should we utilize the ephemeral storage within cnidarium vs adding to state on the app? I know @SuperFluffy has been trying to decrease our global state bloat.
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 is functionally the same - it's an object stored in memory and cleared at the start of each block. the ephemeral store is also only in memory and is cleared at the end of each block. is the suggestion to have less fields within the App
struct?
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.
I agree that we should employ the ephemeral store more rather than adding fields to app.
There is a tendency to add more and more fields to App
that need to be actively set/unset, which is not clean (and I believe with which we had issues in the past).
For a reader it's also better to have a single source of truth of effects that happen during block building, rather than finding some in the ephemeral store and some in the "global" app state.
I should note that this applies to all the other fields as well.
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.
moved the fields to the ephemeral object store here: 591fd94
this is nice because the fields are automatically cleared when update_state_for_new_round
is called, however having to get a StateDelta
for writing to it is bit more cumbersome in parts (putting the execution_results
in prepare_proposal
)
@@ -458,6 +482,16 @@ impl App { | |||
); | |||
|
|||
self.executed_proposal_hash = process_proposal.hash; | |||
self.post_execute_transactions( |
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.
Why not pass the entire process_proposal
to this method?
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 is also called in finalize_block
potentially, so it might not have process_proposal
available
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 test is so complex, I don't know how to evaluate this change to 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.
yeah :/ i didn't write the test, just modified it/added some asserts for sanity when updating it
pub(crate) async fn finalize_block( | ||
/// this must be called after a block's transactions are executed. | ||
#[instrument(name = "App::post_execute_transactions", skip_all)] | ||
async fn post_execute_transactions( | ||
&mut self, |
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.
Hard to follow all the side effects in this function.
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 function is basically everything that was in finalize_block
after txs were executed (minus mempool cleaning, as that needs to be done only when the block is actually finalized)
// use a dummy app hash here - the actual app hash will be filled out in finalize_block. | ||
let dummy_app_hash = AppHash::default(); | ||
|
||
let finalize_block = abci::response::FinalizeBlock { |
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.
It's surprising that this method changes the internal state of self.finalize_block
, but the finalize_block
takes a finalize_block
argument and then mutates the finalize_block
field that some other part of the stack changed.
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.
i could change this to be some intermediate type that doesn't have the app hash yet, which is turned into finalize_block
response only in finalize_block
. would that be easier to follow?
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.
I suppose it's fine. We should probably sit down and refactor of all of this. It looks like we have a bit of code duplication going on.
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.
I have a hard time following the changes, which might be an artifact of how app execution is structured.
I am also surprised that making sequencer blocks available during an earlier phase leads to changes in mempool tests.
the changes are because |
06ea6d2
to
591fd94
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.
There's one mention of self.execution_results
left behind in the doc comments for execute_transactions_prepare_proposal
which would be good to have updated.
I also think we should look to abstract the usage of the ephemeral store behind our own API provided by the extension traits in app/state_ext.rs
(similar to how cached block deposits are handled in the bridge
extension traits). We'd also then keep the keys for these in app::state_ext
along with the others. But I can handle changing to that as part of the ongoing storage updates.
Neither of these are blocking though, so approving as is.
@@ -629,15 +662,16 @@ impl App { | |||
self.metrics | |||
.set_transactions_in_mempool_total(self.mempool.len().await); | |||
|
|||
self.execution_results = Some(execution_results); | |||
let mut state_tx = Arc::try_begin_transaction(&mut self.state) |
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 looks complex. Can you add comment above explaining why it's necessary? Seems like a hack around cnidarium not providing us with something we need.
I.e. // HACK: <reason>
or // XXX: <reason>
Summary
refactor the sequencer app to generate and store the resulting
SequencerBlock
after transaction execution even in the proposal phase.Background
previously, we were only generating the
SequencerBlock
infinalize_block
, however with the upcoming builder APIs (#1519) we require the (proposed)SequencerBlock
to be available after execution in the proposal phase.Changes
post_execute_transactions
method and move the after-execution logic that generates theSequencerBlock
fromfinalize_block
to there.process_proposal
.prepare_proposal
,post_execute_transactions
is still called inprocess_proposal
, as the block hash is not available inprepare_proposal
.Testing
existing unit tests pass, app logic was not changed, just refactored
Related Issues
related to #1322