-
Notifications
You must be signed in to change notification settings - Fork 37.7k
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
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. |
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 c46d051.
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
@@ -565,14 +565,18 @@ static RPCHelpMan getblockheader() | |||
}; | |||
} | |||
|
|||
static CBlock GetBlockChecked(BlockManager& blockman, const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) | |||
static void EnsureNotPruned(BlockManager& blockman, const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) |
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
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_main
had to be locked duringGetBlockChecked
in thegetblock
rpc call. This made parallelgetblock
calls slower, for example in Fulcrum.By moving the pruned check out of
GetBlockChecked
into a separate function,cs_main
is no longer locked duringReadBlockFromDisk
.It still locks
cs_main
if pruning is enabled to prevent a race condition (#13903), so the performance improvement will only be noticeable without pruning.