-
Notifications
You must be signed in to change notification settings - Fork 276
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
Avoid recalculating the wallet balance - use model cache #598
Avoid recalculating the wallet balance - use model cache #598
Conversation
a8d9b7d
to
7345a04
Compare
QCOMPARE(balanceText, balanceComparison); | ||
} | ||
// Update walletModel cached balance which will trigger an update for the 'labelBalance' QLabel. | ||
walletModel.pollBalanceChanged(); |
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 7345a04 "test: GUI WalletTests, trigger walletModel balance changed manually."
It seems potentially bad that the cached balance might be out of sync with the real balance and that pollBalanceChanged
needs to be called here in the tests?
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 out of sync merely because we are not starting the polling timer at all in the test. Same as we aren't initializing several parts of the GUI objects. All the objects (wallet, models, views, etc) that are used on this test are manually created instead of using the WalletController
class flow.
Which I think that is actually what we are looking for. It's an unit test that is focused on verifying that GUI widgets/views are behaving as expected: updating the presented information, etc. when they receive different signals and/or function calls from outside (in other words, focus is on the signal slots/receiver side). It's not about whether the wallet balance polling timer is functioning as expected or not (which we definitely can create a new test case for it in a follow-up work).
a95c81f
to
061c6ee
Compare
thanks achow101 for the review! typo fixed 👍🏼. |
The following part of the PR description
looks outdated now. |
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 seems wrong to me when the commit 696683d "GUI: use cached balance in overviewpage and sendcoinsdialog." breaks GUI WalletTests
, then another commit fixes the broken test.
If a change is a refactoring then all tests must pass. Otherwise, if a commit introduces a new feature/functionality, it should change both main code and its tests.
061c6ee
to
f6084d0
Compare
thanks for the review hebasto!
I decoupled it to explain the rationale properly but sure. Squashed 👍🏼 . Feedback tackled. |
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ConflictsNo conflicts as of last run. |
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.
could break off 20d1b90 into it's own PR in order to make this focused on refactoring to use the model cache
Hey @jarolrod, I honestly don't think that separating that easy reviewable fix would make any difference. Would rather friendly ping/dm some possible reviewers directly. This set of changes will be quite beneficial for large wallets. |
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.
Concept ACK
- I agree with using cached balances whenever possible to save time on wallet balances more than necessary.
- I would like to briefly summarise my understanding of each commit, along with a few points I would like to discuss.
- If some of my observations are erroneous or incomplete, please do correct me.
Notes:
- Commit-1
- Doubt: It is claimed that
setBalance
is called twice. But it is so only whendisplayUnitChanged
signal is emitted. So, as far as I understood, this will lead to not updating the balance in casedisplayUnit
is not changed. - Renaming the
updateDisplayUnit
function is technically a refactoring change, so ideally, it should be separate from the code changes.
- Doubt: It is claimed that
- Commit-2:
- Introduces the
getCachedBalance
function. - I think I understand @hebasto’s reasoning here to rename the function to
CachedBalance()
.m_cached_balances
should be something that is updated only by the internal functions of the class. Since they can directly access the variable, they don’t need a setter. - And having a function named
CachedBalance()
clearly points to what value the function will return; hence theget
suffix seems redundant.
- Introduces the
- Commit-3:
- Makes the code changes to use the cached balance.
- And make relevant test changes to allow them to pass.
- The relevant change includes doing manual balance polling updates.
- commit ACK.
- Commit-4:
- Adds a function that checks if the coins are manually selected; if so, they are passed through the previously used
getAvailableBalance()
function, otherwise, the cachedBalance is used. - This makes sense as the cached balance is created with the
GetBalance(*m_wallet)
function. - commit ACK.
- Adds a function that checks if the coins are manually selected; if so, they are passed through the previously used
- Commit-5:
- Removes a redundant global variable.
- On line 308, of
src/qt/overviewpage.cpp
, them_balances
variable should be renamed to balances as them_
prefix is reserved for global variables.
Thanks @shaavan for the review :).
At startup, when the widgets are initialized, To be specific, in master:
I tried to explain the rationale in a previous comment: So any dev using the model interface (or any interface actually) can find it right away without even need to think twice about it. At the end, It’s more a general code styling conversation than something for this PR. But.. I’m in favor of not making code harder to read/follow with local/own conventions where is possible. |
Thanks for the explanation @furszy! I understand now how setBalance is being called twice. Also, after some thinking, I agree with your rationale for adding the Just one last nit suggestion.
|
f6084d0
to
42059fb
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 42059fb
Changes since my last review:
- Renamed
m_balances
->balances
Apologize for the late reply. I missed your notification earlier when the PR was updated. Thanks for your patience!
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 42059fb
@hebasto maybe merge? |
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 42059fb, I have reviewed the code and it looks OK.
Inside setModel, we call 'wallet().getBalances()', to set the view balance, right before calling 'updateDisplayUnit' which calls 'wallet().getBalances()' internally and re-sets the view balance again.
No need to guard it as it is/will only be accessed from the main thread for now
Plus, calculate the cached balance right when the wallet model, so the wallet widgets don't need to redo the same balance calculation multiple times when they are waiting for the model balance polling timer. ---------------------------------------------------------------------- test wise: `WalletTests` now need to trigger the walletModel balance changed manually. So the model updates its internal state and can be used by the widgets. This is because the test does not start the balance polling timer, in the same way as does not initialize several parts of the GUI workflow. All the objects (wallet, models, views, etc) that are used on this test are manually created instead of using the `WalletController` class flow. Rationale is that this unit test is focused on verifying the GUI widgets/views behavior only: update the presented information, etc. when they receive different signals and/or function calls from outside (in other words, focus is on the signal slots/receiver side). It's not about whether the wallet balance polling timer is functioning as expected or not (which we definitely create a new test case for it in a follow-up work).
…lect UTXO manually No need to walk through the entire wallet's tx map. Used for 'walletModel::prepareTransaction' and 'useAvailable' flow in sendcoinsdialog.
42059fb
to
4584d30
Compare
nits tackled. |
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 4584d30 I tested this with a script i wrote to generate a bunch of blocks on regtest and generate 10K transactions for a wallet. I tested the wallet with this PR and Master. I didn't visually perceive a difference between master and this PR; probably due to the power of my desktop, but obviously the code shows that this is an improvement. |
10k txes isn't that much (otherwise the wallet would be pretty unusable). 100k should be enough to notice it even on powerful machines. Will see a longer loading time for the wallet at startup (in the worker thread). Still.. have to say that this is just the tip of the iceberg, and we do have other stuff that can be improved to decrease the wallet startup time. |
…… …use model cache
… skips selected coins 68eed5d test,gui: add coverage for PSBT creation on legacy watch-only wallets (furszy) 306aab5 test,gui: decouple widgets and model into a MiniGui struct (furszy) 2f76ac0 test,gui: decouple chain and wallet initialization from test case (furszy) cd98b71 gui: 'getAvailableBalance', include watch only balance (furszy) 74eac3a test: add coverage for 'useAvailableBalance' functionality (furszy) dc1cc1c gui: bugfix, getAvailableBalance skips selected coins (furszy) Pull request description: Fixes #688 and bitcoin/bitcoin#26687. First Issue Description (#688): The previous behavior for `getAvailableBalance`, when the coin control had selected coins, was to return the sum of them. Instead, we are currently returning the wallet's available total balance minus the selected coins total amount. Reason: Missed to update the `GetAvailableBalance` function to include the coin control selected coins on #25685. Context: Since #25685 we skip the selected coins inside `AvailableCoins`, the reason is that there is no need to waste resources walking through the entire wallet's txes map just to get coins that could have gotten by just doing a simple `mapWallet.find`). Places Where This Generates Issues (only when the user manually select coins via coin control): 1) The GUI balance check prior the transaction creation process. 2) The GUI "useAvailableBalance" functionality. Note 1: As the GUI uses a balance cache since #598, this issue does not affect the regular spending process. Only arises when the user manually select coins. Note 2: Added test coverage for the `useAvailableBalance` functionality. ---------------------------------- Second Issue Description (bitcoin/bitcoin#26687): As we are using a cached balance on `WalletModel::getAvailableBalance`, the function needs to include the watch-only available balance for wallets with private keys disabled. ACKs for top commit: Sjors: tACK 68eed5d achow101: ACK 68eed5d theStack: ACK 68eed5d Tree-SHA512: 674f3e050024dabda2ff4a04b9ed3750cf54a040527204c920e1e38bd3d7f5fd4d096e4fd08a0fea84ee6abb5070f022b5c0d450c58fd30202ef05ebfd7af6d3
As per the title says, we are recalculating the entire wallet balance on different situations calling to
wallet().getBalances()
, when should instead make use of the wallet model cached balance.This has the benefits of (1) not spending resources calculating a balance that we already have cached, and (2) avoid blocking the main thread for a long time, in case of big wallets, walking through the entire wallet's tx map more than what it's really needed.
Changes:
SendCoinsDialog
was callingwallet().getBalances()
twice duringsetModel
.getAvailableBalance
call.As an extra note, this work born in #25005 but grew out of scope of it.