-
Notifications
You must be signed in to change notification settings - Fork 38.6k
rpc: Faster getblock API #25232
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
rpc: Faster getblock API #25232
Conversation
vincenzopalazzo
left a comment
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 is some way to ensure that this is a speed improvement that justify this refactoring?
Yes, I'm running a benchmark right now and will share the results. A good way to test is also trying to sync Fulcrum, an Electrum server for a more real-world scenario where getblock is used a lot. |
dc29e19 to
c46d051
Compare
|
I haven't found a good way to benchmark it except trying to sync Fulcrum and viewing the logs. Without this patch, it often exceeded the RPC work queue depth and was very slow. With this patch, it was much faster and didn't do that. |
|
Concept ACK on doing the block read from disk without locking, when pruning is not enabled. This seems quite low-hanging fruit for optimization. Assuming, there can't be a race condition between the block being written to disk and reading it? |
|
I don't think there is, because the check if the block exists ( |
|
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ReviewsSee the guideline for information on the review process.
If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
aureleoules
left a comment
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.
ACK c46d05144d1440889251cb01e6ae8361ce4381a8.
I'm not sure how to benchmark this either so I used the poor's man benchmark tool time:
time seq 10000 | xargs -i -P 15 ./src/bitcoin-cli -testnet getblock 000000000000001b3a9aeab5659c3e13dbc32d771a8b4f86aeac529d51a12039
This executes 10,000 getblock requests in parallel batches of 15 on testnet.
Master
46.48s user 21.84s system 395% cpu 17.277 total
46.44s user 21.75s system 419% cpu 16.254 total
PR
45.73s user 21.27s system 425% cpu 15.740 total
45.74s user 21.40s system 422% cpu 15.894 total
The results are slightly faster than master but they're consistent.
src/rpc/blockchain.cpp
Outdated
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.
nit: i think it's clearer
| static void EnsureNotPruned(BlockManager& blockman, const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) | |
| static void EnsureBlockNotPruned(BlockManager& blockman, const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) |
|
You'll need to squash the new commit https://github.com/bitcoin/bitcoin/blob/master/CONTRIBUTING.md#squashing-commits |
fbb889a to
599f260
Compare
aureleoules
left a comment
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.
ACK 599f260
|
What if pruning occurs for the first time, after the lock is released, and before it is read? IMO a better approach would be to use a temporary prune lock. This would extend the benefit to pruned nodes, and could possibly (in another PR) be used to safely re-fetch a pruned block from another peer. |
|
Sorry I wasn't aware of this PR until drahtbot alerted me to conflicts. You can bench with ApacheBench. Run where and the
If the file has not yet been opened, it will fail and throw an RPC error similar to if it is pruned. If the file has already been opened, then it should succeed and remove the file once the file is closed on linux. On Windows it could cause the files to not be pruned https://en.cppreference.com/w/cpp/io/c/remove. See #26316. |
| havePruned = chainman.m_blockman.m_have_pruned; | ||
| if(havePruned) { | ||
| EnsureBlockNotPruned(chainman.m_blockman, pblockindex); | ||
| block = GetBlockChecked(pblockindex); |
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.
Is there any value in keeping the cs_main lock here. In the very rare case where the block could be deleted after EnsureBlockNotPruned but before GetBlockChecked without the lock, it seems fine to just error out as well. The only difference would be the error message.
This is done in #26308
|
🐙 This pull request conflicts with the target branch and needs rebase. |
|
@AaronDewes I think #26308 accomplishes this. I syncing Fulcrum on master faster now? |
|
Yes, it seems a lot faster, thank you! |
Previously,
cs_mainhad to be locked duringGetBlockCheckedin thegetblockrpc call. This made parallelgetblockcalls slower, for example in Fulcrum.By moving the pruned check out of
GetBlockCheckedinto a separate function,cs_mainis no longer locked duringReadBlockFromDisk.It still locks
cs_mainif pruning is enabled to prevent a race condition (#13903), so the performance improvement will only be noticeable without pruning.