-
Notifications
You must be signed in to change notification settings - Fork 37.7k
Avoid permanent cs_main/cs_wallet lock during RescanFromTime #11281
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
Conversation
Best reviewed with |
@jonasschnelli Which variables are guarded by I'll double-check against my lock annotations to make sure they are in sync. |
Awesome, will review. |
src/wallet/wallet.cpp
Outdated
LogPrintf("%s: Rescanning last %i blocks\n", __func__, startBlock ? chainActive.Height() - startBlock->nHeight + 1 : 0); | ||
CBlockIndex* startBlock = nullptr; | ||
{ | ||
LOCK2(cs_main, cs_wallet); |
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.
What needs cs_wallet here?
src/wallet/wallet.cpp
Outdated
double dProgressStart = 0; | ||
double dProgressTip = 0; | ||
{ | ||
LOCK2(cs_main, cs_wallet); |
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.
What needs cs_wallet here?
@@ -1582,13 +1588,17 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool f | |||
|
|||
CBlock block; | |||
if (ReadBlockFromDisk(block, pindex, Params().GetConsensus())) { | |||
LOCK2(cs_main, cs_wallet); |
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.
What needs cs_main here?
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.
chainActive.
src/wallet/wallet.cpp
Outdated
for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) { | ||
AddToWalletIfInvolvingMe(block.vtx[posInBlock], pindex, posInBlock, fUpdate); | ||
} | ||
} else { | ||
ret = pindex; | ||
} | ||
pindex = chainActive.Next(pindex); | ||
{ | ||
LOCK2(cs_main, cs_wallet); |
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.
What needs cs_wallet here?
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.
Fixed
4c1f082
to
1d367ef
Compare
Updated and reduced locks after @sipa's mentioning. |
utACK 1d367effa8e7a19303ae46a6b871084cbaaf5a0a |
src/wallet/wallet.cpp
Outdated
fAbortRescan = false; | ||
fScanningWallet = true; | ||
|
||
ShowProgress(_("Rescanning..."), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup | ||
double dProgressStart = GuessVerificationProgress(chainParams.TxData(), pindex); | ||
double dProgressTip = GuessVerificationProgress(chainParams.TxData(), chainActive.Tip()); | ||
double dProgressStart = 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.
Only chainActive.Tip()
needs the lock? So keep old code and:
CBlockIndex* tip = nullptr;
{
LOCK(cs_main);
tip = chainActive.Tip();
}
double dProgressStart = GuessVerificationProgress(chainParams.TxData(), pindex);
double dProgressTip = GuessVerificationProgress(chainParams.TxData(), tip);
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.
BTW, this patch allows the tip to be update so dProgressTip
should be inside the loop?
pindex = chainActive.Next(pindex); | ||
{ | ||
LOCK(cs_main); | ||
pindex = chainActive.Next(pindex); |
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.
What if there's a reorg between iterations?
Does anything weird happen if we (for ex): begin scanning, receive a new best block with our tx from p2p, then re-scan here?
Seems to me that we should take a copy of ChainActive, traverse, compare to progress to chainActive.Tip(), and repeat until they match. Something like (just a quick sketch, probably broken):
CBlockIndex* pindex = pindexStart;
CChain chain;
while (!fAbortRescan) {
{
LOCK(cs_main);
if (pindex == chainActive.Tip()) {
break;
}
chain.SetTip(chainActive.Tip());
}
if (pindex) {
// start where we left off.
const CBlockIndex* fork = chain.FindFork(pindex);
if (fork != pindex) {
// Need to undo the orphans.
}
pindex = chain.Next(fork);
}
while(!fAbortRescan && pindex) {
/*
do stuff
...
pindex = chain.Next(pindex);
*/
}
pindex = chain.Tip();
}
That almost entirely avoids locking cs_main, while ensuring that we're never jumping chains.
It could also be done with just the pindex and GetAncestor(), to skip the memory overhead of the second chain copy.
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'm not sure if this is really a problem. During rescans, IMO it won't hurt to scan an orphaned block nor does it hurt if you do not scan up to the tip in case you have connected new blocks during the time of re-scanning. This, because those new blocks must also have been scanned by the wallet logic (outside of ScanForWalletTransactions
).
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 a reorg happens during rescan, we want to continue rescanning using the current chain, not the outdated one.
This, because those new blocks must also have been scanned by the wallet logic (outside of ScanForWalletTransactions).
Indeed.
... I think it works out like this. Though @theuni does put a good point.
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.
Maybe break the rescan as soon as possible if the above tip is not in the current chain? Then add here:
if (!chainActive.Contains(tip)) break;
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.
In case of a reorg, wouldn't chainActive.Next(pindex);
return a nullptr
leading to stop the scan, which should be totally fine. During the reorg, the blocks of the new chain must have been scanned by the wallet via ConnectBlock.
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.
Suppose the following:
A B C D E F G H I J
- chain to rescan, tip = J(A) B C D E F G H I J
- scanning block AA (B) C D E F G H I J
- scanning block BA (B) C D E F G H K L M
- scanning block B, reorg after H
Should only rescan to H inclusive? Agree it's an edge case.
BTW, if the tip changes dProgressTip
could be updated?
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.
Yea, I think there is a real issue here. I (believe) it could be fixed by doing the AddToWalletIfInvolvingMe inside a cs_main (which is going to be at least mostly required since ReadBlockFromDisk requires cs_main) with a chainActive.Contains() check before doing the AddToWalletIfInvolvingMe. That should put us back to the original state of anything we add to the wallet is on the main chain and, thus, will either be Disconnected then Connected in the callbacks, only Connected, or not included in a callback at all.
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 still don't fully understand the concerns (please help me).
- If there is a reorg, all blocks of the new chain will be scanned by the wallet via ConnectBlock (no need to rescan again, we only need to rescan the stuff that doesn't come in via Connect/Disconnect block).
- If we scan blocks that are no longer in the main-chain, and we could find a transaction in those blocks that is relevant to our wallet, so be it. The wallet does display such transactions as unconfirmed... same as we would have a reorg outside of the rescan process.
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.
Heh, sorry, I didnt fully specify my concern because I thought the above discussion did, but I dont think the above comments are correct. As you point out, I do not believe there is a race where you simply miss transactions. However, there is a race where you may end up marking a transaction as in the wrong block:
A -> B -> ....
-> C -> ...
Both B and C have the transaction you're interested in.
You go to load block B from a rescan, but it has already been reorg'd and the notificatitons for disconnect(B) and connect(C) have already fired. Thus, you have the transaction marked as in block C, which is correct, but then you finish rescanning and mark the transaction as in block B, which now makes you think the transaction has been conflicted and has negative GetDepthInMainChain.
As I mentioned, this is most simply fixed by adding a "LOCK(cs_main); if (!chainActive.Contains(pindex)) break;" outside the AddToWalletIfInvolvingMe loop.
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.
Fixed
Sorry for the bad reference ☝️ ... |
1d367ef
to
4bc57dc
Compare
Fixed @promag's code folding/style issue |
4bc57dc
to
726fe69
Compare
Reviewers welcome (to make progress with GUI rescan abort #11200), best reviewed with ?w=1 |
(removed my utACK for now, this does not get reorgs right in case the order in which blocks are scanned matters, which I'm not sure about) |
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.
LGTM, will play a bit.
pindex = chainActive.Next(pindex); | ||
{ | ||
LOCK(cs_main); | ||
pindex = chainActive.Next(pindex); |
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.
Maybe break the rescan as soon as possible if the above tip is not in the current chain? Then add here:
if (!chainActive.Contains(tip)) break;
BTW, see question #11281 (comment). |
This also allows concurrent rescans correct? It will mess up the progress dialog no? |
Concurrent rescans on multiple wallets would be nice, multiple concurrent rescans on one wallet would be dangerous or at least counter-productive (so we might want to track that?). |
726fe69
to
0621022
Compare
Rebased the PR.
|
0621022
to
78049d9
Compare
Best reviewed with w=1. |
src/wallet/wallet.cpp
Outdated
double dProgressStart = GuessVerificationProgress(chainParams.TxData(), pindex); | ||
double dProgressTip = GuessVerificationProgress(chainParams.TxData(), chainActive.Tip()); | ||
double dProgressTip = GuessVerificationProgress(chainParams.TxData(), tip); |
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.
GuessVerificationProcess needs cs_main (probably best to lock cs_main in GuessVerificationProcess).
src/wallet/wallet.cpp
Outdated
@@ -1669,6 +1674,7 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock | |||
|
|||
CBlock block; | |||
if (ReadBlockFromDisk(block, pindex, Params().GetConsensus())) { | |||
LOCK(cs_wallet); |
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.
(one line up) ReadBlockFromDisk in CBlockIndex* form requires cs_main.
pindex = chainActive.Next(pindex); | ||
{ | ||
LOCK(cs_main); | ||
pindex = chainActive.Next(pindex); |
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.
Yea, I think there is a real issue here. I (believe) it could be fixed by doing the AddToWalletIfInvolvingMe inside a cs_main (which is going to be at least mostly required since ReadBlockFromDisk requires cs_main) with a chainActive.Contains() check before doing the AddToWalletIfInvolvingMe. That should put us back to the original state of anything we add to the wallet is on the main chain and, thus, will either be Disconnected then Connected in the callbacks, only Connected, or not included in a callback at all.
src/wallet/rpcdump.cpp
Outdated
@@ -428,6 +432,10 @@ UniValue importpubkey(const JSONRPCRequest& request) | |||
if (fRescan && fPruneMode) | |||
throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode"); | |||
|
|||
if (fRescan && pwallet->IsScanning()) { |
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 very race-y. You could check check this, have another thread call rescan, and then end up with two parallell rescans.
Add a unit test for LockDirectory, introduced in bitcoin#11281.
Add a unit test for LockDirectory, introduced in bitcoin#11281.
90ba2df Fix missing cs_main lock for GuessVerificationProgress() (Jonas Schnelli) Pull request description: `GuessVerificationProgress()` needs `cs_main` due to accessing the `pindex->nChainTx`. This adds a `AssertLockHeld` in `GuessVerificationProgress()` and adds the missing locks in... * `LoadChainTip()` * `ScanForWalletTransactions()` (got missed in #11281) * GUI, `ClientModel::getVerificationProgress()` <--- **this may have GUI performance impacts**, but could be relaxed later with a cache or something more efficient. Tree-SHA512: 13302946571422375f32af8e396b9d2c1180f2693ea363aeba9e98c8266ddec64fe7862bfdcbb5a93a4b12165a61eec1e51e4e7d7a8515fa50879095dc163412
Add a unit test for LockDirectory, introduced in bitcoin#11281. Github-Pull: bitcoin#12422 Rebased-From: 1d4cbd2 Tree-SHA512: 8186f4b22c65153e30ec1e0b68be20fd59d34e1fe565d99f3b5a302780125953b867fb14c37144de3fd7baf5751df94bf3901eebbb7b820491ca452706d4e205
Return more information about scan status from ScanForWalletTransactions and try to describe the return value more clearly. There is a slight change in behavior here where rescans that end early due to a reorg will no longer trigger errors. (I incorrectly suggested that that they should trigger errors in the PR where this behavior was introduced: bitcoin#11281 (comment))
…scanFromTime 7f81250 Mention that other RPC calls report keys as "imported" while txns are still missing (Jonas Schnelli) ccd8ef6 Reduce cs_main lock in ReadBlockFromDisk, only read GetBlockPos under the lock (Jonas Schnelli) bc356b4 Make sure WalletRescanReserver has successfully reserved the rescan (Jonas Schnelli) dbf8556 Add RAII wallet rescan reserver (Jonas Schnelli) 8d0b610 Avoid pemanent cs_main/cs_wallet lock during wallet rescans (Jonas Schnelli) Pull request description: Right now, we are holding `cs_main`/`cs_wallet` during the whole rescan process (which can take a couple of hours). This was probably only done because of laziness and it is an important show-stopper for bitcoin#11200 (GUI rescan abort). Tree-SHA512: 0fc3f82d0ee9b2f013e6bacba8d59f7334306660cd676cd64c47bb305c4cb7c7a36219d6a6f76023b74e5fe87f3ab9fc7fd2439e939f71aef653fddb0a1e23b1 Signed-off-by: Pasta <pasta@dashboost.org> # Conflicts: # src/wallet/rpcdump.cpp # src/wallet/test/wallet_tests.cpp # src/wallet/wallet.h
…escanFromTime (#3411) * Merge bitcoin#11281: Avoid permanent cs_main/cs_wallet lock during RescanFromTime 7f81250 Mention that other RPC calls report keys as "imported" while txns are still missing (Jonas Schnelli) ccd8ef6 Reduce cs_main lock in ReadBlockFromDisk, only read GetBlockPos under the lock (Jonas Schnelli) bc356b4 Make sure WalletRescanReserver has successfully reserved the rescan (Jonas Schnelli) dbf8556 Add RAII wallet rescan reserver (Jonas Schnelli) 8d0b610 Avoid pemanent cs_main/cs_wallet lock during wallet rescans (Jonas Schnelli) Pull request description: Right now, we are holding `cs_main`/`cs_wallet` during the whole rescan process (which can take a couple of hours). This was probably only done because of laziness and it is an important show-stopper for bitcoin#11200 (GUI rescan abort). Tree-SHA512: 0fc3f82d0ee9b2f013e6bacba8d59f7334306660cd676cd64c47bb305c4cb7c7a36219d6a6f76023b74e5fe87f3ab9fc7fd2439e939f71aef653fddb0a1e23b1 Signed-off-by: Pasta <pasta@dashboost.org> # Conflicts: # src/wallet/rpcdump.cpp # src/wallet/test/wallet_tests.cpp # src/wallet/wallet.h * remove "LearnAllRelatedScripts" Signed-off-by: Pasta <pasta@dashboost.org> * fix importelectrumwallet Signed-off-by: Pasta <pasta@dashboost.org> Co-authored-by: Wladimir J. van der Laan <laanwj@gmail.com>
…gress() 90ba2df Fix missing cs_main lock for GuessVerificationProgress() (Jonas Schnelli) Pull request description: `GuessVerificationProgress()` needs `cs_main` due to accessing the `pindex->nChainTx`. This adds a `AssertLockHeld` in `GuessVerificationProgress()` and adds the missing locks in... * `LoadChainTip()` * `ScanForWalletTransactions()` (got missed in bitcoin#11281) * GUI, `ClientModel::getVerificationProgress()` <--- **this may have GUI performance impacts**, but could be relaxed later with a cache or something more efficient. Tree-SHA512: 13302946571422375f32af8e396b9d2c1180f2693ea363aeba9e98c8266ddec64fe7862bfdcbb5a93a4b12165a61eec1e51e4e7d7a8515fa50879095dc163412
…gress() 90ba2df Fix missing cs_main lock for GuessVerificationProgress() (Jonas Schnelli) Pull request description: `GuessVerificationProgress()` needs `cs_main` due to accessing the `pindex->nChainTx`. This adds a `AssertLockHeld` in `GuessVerificationProgress()` and adds the missing locks in... * `LoadChainTip()` * `ScanForWalletTransactions()` (got missed in bitcoin#11281) * GUI, `ClientModel::getVerificationProgress()` <--- **this may have GUI performance impacts**, but could be relaxed later with a cache or something more efficient. Tree-SHA512: 13302946571422375f32af8e396b9d2c1180f2693ea363aeba9e98c8266ddec64fe7862bfdcbb5a93a4b12165a61eec1e51e4e7d7a8515fa50879095dc163412
…gress() 90ba2df Fix missing cs_main lock for GuessVerificationProgress() (Jonas Schnelli) Pull request description: `GuessVerificationProgress()` needs `cs_main` due to accessing the `pindex->nChainTx`. This adds a `AssertLockHeld` in `GuessVerificationProgress()` and adds the missing locks in... * `LoadChainTip()` * `ScanForWalletTransactions()` (got missed in bitcoin#11281) * GUI, `ClientModel::getVerificationProgress()` <--- **this may have GUI performance impacts**, but could be relaxed later with a cache or something more efficient. Tree-SHA512: 13302946571422375f32af8e396b9d2c1180f2693ea363aeba9e98c8266ddec64fe7862bfdcbb5a93a4b12165a61eec1e51e4e7d7a8515fa50879095dc163412
…gress() 90ba2df Fix missing cs_main lock for GuessVerificationProgress() (Jonas Schnelli) Pull request description: `GuessVerificationProgress()` needs `cs_main` due to accessing the `pindex->nChainTx`. This adds a `AssertLockHeld` in `GuessVerificationProgress()` and adds the missing locks in... * `LoadChainTip()` * `ScanForWalletTransactions()` (got missed in bitcoin#11281) * GUI, `ClientModel::getVerificationProgress()` <--- **this may have GUI performance impacts**, but could be relaxed later with a cache or something more efficient. Tree-SHA512: 13302946571422375f32af8e396b9d2c1180f2693ea363aeba9e98c8266ddec64fe7862bfdcbb5a93a4b12165a61eec1e51e4e7d7a8515fa50879095dc163412
…gress() 90ba2df Fix missing cs_main lock for GuessVerificationProgress() (Jonas Schnelli) Pull request description: `GuessVerificationProgress()` needs `cs_main` due to accessing the `pindex->nChainTx`. This adds a `AssertLockHeld` in `GuessVerificationProgress()` and adds the missing locks in... * `LoadChainTip()` * `ScanForWalletTransactions()` (got missed in bitcoin#11281) * GUI, `ClientModel::getVerificationProgress()` <--- **this may have GUI performance impacts**, but could be relaxed later with a cache or something more efficient. Tree-SHA512: 13302946571422375f32af8e396b9d2c1180f2693ea363aeba9e98c8266ddec64fe7862bfdcbb5a93a4b12165a61eec1e51e4e7d7a8515fa50879095dc163412
…gress() 90ba2df Fix missing cs_main lock for GuessVerificationProgress() (Jonas Schnelli) Pull request description: `GuessVerificationProgress()` needs `cs_main` due to accessing the `pindex->nChainTx`. This adds a `AssertLockHeld` in `GuessVerificationProgress()` and adds the missing locks in... * `LoadChainTip()` * `ScanForWalletTransactions()` (got missed in bitcoin#11281) * GUI, `ClientModel::getVerificationProgress()` <--- **this may have GUI performance impacts**, but could be relaxed later with a cache or something more efficient. Tree-SHA512: 13302946571422375f32af8e396b9d2c1180f2693ea363aeba9e98c8266ddec64fe7862bfdcbb5a93a4b12165a61eec1e51e4e7d7a8515fa50879095dc163412
In bitcoin#11281, commit ccd8ef6 the `LOCK(cs_main)` scope was reduced for the wallet. This does the same for the rest API, which is a huge performance boost for `rest_block` call when used from multiple threads. My test setup, on an Intel i7 with 6 cores (12 threads), locked to 3.2GHz: 1. start a fully synced bitcoind, with this `bitcoin.conf`: ``` server=1 rest=1 rpcport=8332 rpcthreads=12 rpcworkqueue=64 txindex=1 dbcache=2000 ``` 2. Wait until log message shows `loadblk thread exit`, so that bitcoind is idle. 3. Run ApacheBench: 10000 requests, 12 parallel threads, fetching block nr. 600000 in binary: ``` ab -n 10000 -c 12 "http://127.0.0.1:8332/rest/block/00000000000000000007316856900e76b4f7a9139cfbfba89842c8d196cd5f91.bin" ``` Requests per second: * 97.434 [ms] (mean) on master * 20.431 [ms] this branch So this can process about 5 times as many requests, and saturates all my cores instead of keeping them idle waiting in the lock.
In bitcoin#11281, commit ccd8ef6 the `LOCK(cs_main)` scope was reduced for the wallet. This does the same for the rest API, which is a huge performance boost for `rest_block` call when used from multiple threads. My test setup, on an Intel i7 with 6 cores (12 threads), locked to 3.2GHz: 1. start a fully synced bitcoind, with this `bitcoin.conf`: ``` server=1 rest=1 rpcport=8332 rpcthreads=12 rpcworkqueue=64 txindex=1 dbcache=2000 ``` 2. Wait until log message shows `loadblk thread exit`, so that bitcoind is idle. 3. Run ApacheBench: 10000 requests, 12 parallel threads, fetching block nr. 600000 in binary: ``` ab -n 10000 -c 12 "http://127.0.0.1:8332/rest/block/00000000000000000007316856900e76b4f7a9139cfbfba89842c8d196cd5f91.bin" ``` Requests per second: * 97.434 [ms] (mean) on master * 20.431 [ms] this branch So this can process about 5 times as many requests, and saturates all my cores instead of keeping them idle waiting in the lock.
In bitcoin#11281, commit ccd8ef6 the `LOCK(cs_main)` scope was reduced for the wallet. This does the same for the rest API, which is a huge performance boost for `rest_block` call when used from multiple threads. My test setup, on an Intel i7 with 6 cores (12 threads), locked to 3.2GHz: 1. start a fully synced bitcoind, with this `bitcoin.conf`: ``` server=1 rest=1 rpcport=8332 rpcthreads=12 rpcworkqueue=64 txindex=1 dbcache=2000 ``` 2. Wait until log message shows `loadblk thread exit`, so that bitcoind is idle. 3. Run ApacheBench: 10000 requests, 12 parallel threads, fetching block nr. 600000 in binary: ``` ab -n 10000 -c 12 "http://127.0.0.1:8332/rest/block/00000000000000000007316856900e76b4f7a9139cfbfba89842c8d196cd5f91.bin" ``` Requests per second: * 97.434 [ms] (mean) on master * 20.431 [ms] this branch So this can process about 5 times as many requests, and saturates all my cores instead of keeping them idle waiting in the lock.
In bitcoin#11281, commit ccd8ef6 the `LOCK(cs_main)` scope was reduced for the wallet. This does the same for the rest API, which is a huge performance boost for `rest_block` call when used from multiple threads. My test setup, on an Intel i7 with 6 cores (12 threads), locked to 3.2GHz: 1. start a fully synced bitcoind, with this `bitcoin.conf`: ``` server=1 rest=1 rpcport=8332 rpcthreads=12 rpcworkqueue=64 txindex=1 dbcache=2000 ``` 2. Wait until log message shows `loadblk thread exit`, so that bitcoind is idle. 3. Run ApacheBench: 10000 requests, 12 parallel threads, fetching block nr. 600000 in binary: ``` ab -n 10000 -c 12 "http://127.0.0.1:8332/rest/block/00000000000000000007316856900e76b4f7a9139cfbfba89842c8d196cd5f91.bin" ``` Requests per second: * 97.434 [ms] (mean) on master * 20.431 [ms] this branch So this can process about 5 times as many requests, and saturates all my cores instead of keeping them idle waiting in the lock.
In bitcoin#11281, commit ccd8ef6 the `LOCK(cs_main)` scope was reduced for the wallet. This does the same for the rest API, which is a huge performance boost for `rest_block` call when used from multiple threads. My test setup, on an Intel i7 with 6 cores (12 threads), locked to 3.2GHz: 1. start a fully synced bitcoind, with this `bitcoin.conf`: ``` server=1 rest=1 rpcport=8332 rpcthreads=12 rpcworkqueue=64 txindex=1 dbcache=2000 ``` 2. Wait until log message shows `loadblk thread exit`, so that bitcoind is idle. 3. Run ApacheBench: 10000 requests, 12 parallel threads, fetching block nr. 600000 in binary: ``` ab -n 10000 -c 12 "http://127.0.0.1:8332/rest/block/00000000000000000007316856900e76b4f7a9139cfbfba89842c8d196cd5f91.bin" ``` Requests per second: * 97.434 [ms] (mean) on master * 20.431 [ms] this branch So this can process about 5 times as many requests, and saturates all my cores instead of keeping them idle waiting in the lock.
In bitcoin#11281, commit ccd8ef6 the `LOCK(cs_main)` scope was reduced for the wallet. This does the same for the rest API, which is a huge performance boost for `rest_block` call when used from multiple threads. My test setup, on an Intel i7 with 6 cores (12 threads), locked to 3.2GHz: 1. start a fully synced bitcoind, with this `bitcoin.conf`: ``` server=1 rest=1 rpcport=8332 rpcthreads=12 rpcworkqueue=64 txindex=1 dbcache=2000 ``` 2. Wait until log message shows `loadblk thread exit`, so that bitcoind is idle. 3. Run ApacheBench: 10000 requests, 12 parallel threads, fetching block nr. 600000 in binary: ``` ab -n 10000 -c 12 "http://127.0.0.1:8332/rest/block/00000000000000000007316856900e76b4f7a9139cfbfba89842c8d196cd5f91.bin" ``` Requests per second: * 97.434 [ms] (mean) on master * 20.431 [ms] this branch So this can process about 5 times as many requests, and saturates all my cores instead of keeping them idle waiting in the lock.
…ng RescanFromTime 1cde8b4 [Trivial] Fix some comment/output: 'bitcoind' --> 'pivxd' (random-zebra) 29c267e Mention that other RPC calls report keys as "imported" while txns are still missing (Jonas Schnelli) f773203 Reduce cs_main lock in ReadBlockFromDisk, only read GetBlockPos under the lock (Jonas Schnelli) 3e01665 Make sure WalletRescanReserver has successfully reserved the rescan (random-zebra) 4d05672 Add RAII wallet rescan reserver (Jonas Schnelli) c3fc22d Avoid pemanent cs_main/cs_wallet lock during wallet rescans (random-zebra) 69a7288 Handle TIMESTAMP_WINDOW within CWallet::RescanFromTime (Russell Yanofsky) fb12767 Make CWallet::RescanFromTime comment less ambiguous (Russell Yanofsky) 2d84bee Add RescanFromTime method and use from rpcdump (Russell Yanofsky) e8dfb32 Move birthday optimization out of ScanForWalletTransactions (random-zebra) Pull request description: Based on top of: - [x] #2279 Backports bitcoin#10412, and bitcoin#11281 ACKs for top commit: furszy: ACK 1cde8b4 and merging Tree-SHA512: 1c76ca9b7925299df796f72e171d70d1df9765d3b8d5976d7d280dd318474911cd8f173f686e57422a6f77fef145e4a9aae480b293e6b77ca4f93750688e997c
…escanFromTime (dashpay#3411) * Merge bitcoin#11281: Avoid permanent cs_main/cs_wallet lock during RescanFromTime 7f81250 Mention that other RPC calls report keys as "imported" while txns are still missing (Jonas Schnelli) ccd8ef6 Reduce cs_main lock in ReadBlockFromDisk, only read GetBlockPos under the lock (Jonas Schnelli) bc356b4 Make sure WalletRescanReserver has successfully reserved the rescan (Jonas Schnelli) dbf8556 Add RAII wallet rescan reserver (Jonas Schnelli) 8d0b610 Avoid pemanent cs_main/cs_wallet lock during wallet rescans (Jonas Schnelli) Pull request description: Right now, we are holding `cs_main`/`cs_wallet` during the whole rescan process (which can take a couple of hours). This was probably only done because of laziness and it is an important show-stopper for bitcoin#11200 (GUI rescan abort). Tree-SHA512: 0fc3f82d0ee9b2f013e6bacba8d59f7334306660cd676cd64c47bb305c4cb7c7a36219d6a6f76023b74e5fe87f3ab9fc7fd2439e939f71aef653fddb0a1e23b1 Signed-off-by: Pasta <pasta@dashboost.org> # Conflicts: # src/wallet/rpcdump.cpp # src/wallet/test/wallet_tests.cpp # src/wallet/wallet.h * remove "LearnAllRelatedScripts" Signed-off-by: Pasta <pasta@dashboost.org> * fix importelectrumwallet Signed-off-by: Pasta <pasta@dashboost.org> Co-authored-by: Wladimir J. van der Laan <laanwj@gmail.com>
Right now, we are holding
cs_main
/cs_wallet
during the whole rescan process (which can take a couple of hours).This was probably only done because of laziness and it is an important show-stopper for #11200 (GUI rescan abort).