Skip to content
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

feat(state-keeper): Parallel l2 block sealing #1801

Open
wants to merge 20 commits into
base: main
Choose a base branch
from

Conversation

perekopskiy
Copy link
Contributor

@perekopskiy perekopskiy commented Apr 25, 2024

What ❔

L2 block data (tx results, storage logs, events, l2 to l1 logs, factory deps, tokens) is sealed in parallel using concurrent DB connections. L2 block header is inserted after all l2 block data is inserted and serves as a marker that l2 block is sealed. DB queries are adjusted so L2 block is not accessed before block is sealed. Note, that some of the queries are only queried for resolved block range, i.e. caller never tries to access "pending" data. I haven't adjusted such queries on purpose to keep their performance the same.

Why ❔

Improves L2 block sealing performance

Checklist

  • PR title corresponds to the body of PR (we generate changelog entries from PRs).
  • Tests for the changes have been added / updated.
  • Documentation comments have been added / updated.
  • Code has been formatted via zk fmt and zk lint.
  • Spellcheck has been run via zk spellcheck.
  • Linkcheck has been run via zk linkcheck.

last_in_batch: block.last_batch_miniblock == Some(block.number),
last_in_batch: block.tx_count == 0,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fictive l2 block sealing and L1 batch sealing are not happening in the same transaction anymore, so the only way to determine if l2 block is the last in batch is to check if it has 0 txs.

if this.pre_insert_txs {
let mut connection = pool.connection_tagged("state_keeper").await?;
let progress = L2_BLOCK_METRICS.start(L2BlockSealStage::PreInsertTxs, is_fictive);
this.insert_transactions(&mut connection)
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'm going to merge pre_insert_txs and mark_txs_as_executed_in_l2_block in a separate PR.

@perekopskiy perekopskiy marked this pull request as ready for review April 26, 2024 07:49
Copy link
Contributor

@slowli slowli left a comment

Choose a reason for hiding this comment

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

Looking at all additional filtering needed to beI wonder whether a potential alternative with primitive L2 block lifecycle would be easier. That is:

  • Insert an L2 block header before other data (meaning that foreign keys don't need to be removed)
  • ...but mark the L2 block as incomplete until all other data is inserted. Having a new miniblocks field looks like an overkill / impractical; another way is to record the latest finished L2 block in a singleton table.

On the one hand, this would allow retaining FK constraints and make filtering out incomplete L2 blocks more explicit. OTOH, it'd require filtering out throughout all queries touching L2 blocks, which is easy to screw up.

core/lib/dal/src/blocks_web3_dal.rs Outdated Show resolved Hide resolved
core/lib/dal/src/storage_logs_dal.rs Outdated Show resolved Hide resolved
core/lib/dal/src/tokens_dal.rs Show resolved Hide resolved
core/lib/dal/src/storage_web3_dal.rs Outdated Show resolved Hide resolved
core/lib/zksync_core/src/sync_layer/external_io.rs Outdated Show resolved Hide resolved
@RomanBrodetski
Copy link
Collaborator

Didn't have a deep loop but wanted to respond to @slowli 's comment on

Insert an L2 block header before other data (meaning that foreign keys don't need to be removed)

IMO it might be even beneficial to remote foreign keys - this will make insertion even faster.

@perekopskiy
Copy link
Contributor Author

My thoughts on alternative approach with inserting header first:
IMO foreign keys don't add much value. It's more important to keep data flow more convenient to use. As @slowli said, if we insert header first then we would need to add filters to (almost?) any query that reads from miniblocks. So more queries should be adjusted in addition to those touched in this PR

Copy link
Contributor

github-actions bot commented May 9, 2024

No performance difference detected (anymore)

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.

None yet

4 participants