-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Speed up deposit and transactions view loads #5120
Speed up deposit and transactions view loads #5120
Conversation
Make the WalletService.walletEventListener field private and add it via a protected method defined in the base class, addListenersToWallet(), so that the setup code in the two subclasses (Bsq|Btc)WalletService can be deduplicated and more easily kept in sync with the listener removal code in WalletService.shutDown(). Also remove some unnecessary deprecation warning suppressions.
Use a guava Multiset to cache the total number of tx outputs (out of the live txs in the user's wallet) with a given address. Since this requires a scan of the entire tx set, compute all the counts in one go and store in an ImmutableMultiset<Address>. Invalidate the entire cache any time a tx set change occurs, by attaching a WalletChangeEventListener to the wallet (using a direct executor for immediate effect). This is to fix a quadratic time bug in DepositView, which uses the count to determine if a given address in the BTC wallet is used/unused.
Use a guava SetMultimap (a many-to-many mapping without duplicates) to cache the set of live txs in the user's wallet with a given address as an input or output. As with the cache of output counts from the previous commit, compute all the tx sets in one go (by a tx stream followed by a map inverse) and store in an ImmutableSetMultimap<Address, Transaction>, invalidating the entire cache immediately upon each wallet change event. This is to fix another (larger) quadratic time bug in DepositView, when getting the confidence (i.e. confirmation count) of each wallet address. Also simplify getTransactionConfidence & onTransactionConfidenceChanged methods slightly, which generated (possibly unintentionally) repeating & singleton lists of TransactionConfidence objects to pass to WalletService.getMostRecentConfidence(..) respectively.
Attempt to remove a bottleneck during the transactions view load, as revealed by JProfiler, by optimising the code to determine if any given transaction and trade are related. Since both these sets will tend to grow linearly with time, causing quadratic slowdown of TransactionsView, try to alleviate (without completely fixing) the problem. To do this, add a cached set of disputed trade IDs to DisputeListService so that TransactionAwareTradable.is(Dispute|RefundPayout)Tx can be made O(1) in the common case that the given trade is not involved in any dispute. Also avoid calling Sha256Hash::toString by passing tx IDs as Sha256Hash objects directly to is(Deposit|Payout)Tx, and short circuit an expensive call BtcWalletService.getTransaction in isDelayedPayoutTx, in the common case, by pre-checking the transaction locktime. This also fixes a bug in isRefundPayoutTx whereby it incorrectly returns false if there are >1 disputes in the list returned by RefundManager but the given trade is not involved in the last one.
Fix a serious memory leak in DepositListItem due to missing removal of the BalanceListener (one per item) from BtcWalletService. This prevented GC of the entire list of items, which was observed to leak ~500 MB in JProfiler after repeated switching (several dozen times) between tabs.
Add a nested class of lazy fields to (Deposit|Transactions)ListItem, together with an associated memoised Supplier, lazyFieldsSupplier, which initialises them all the first time any one of them is requested. Move the txConfidenceIndicator & tooltip fields to this class, so that they are only loaded when the given address/tx row is displayed in the deposit/transactions views, respectively. This prevents a minor bottleneck, as profiling indicates that creating a tooltip for each tx confidence indicator in the list is quite expensive and takes up around half the rendering time. (There may be 1000's of txs or addresses in an old 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.
I think the getTransactionConfidence method is not correct. Otherwise all looks good. Was not aware of the guava Multimap framework...Could be used in many places...
Undo the earlier simplification changes to getTransactionConfidence, which preserved its original but broken behaviour. Fix the original stream pipeline so that each matching tx input maps to the confidence of the connected parent tx (if any), not the child tx. In this way, it correctly considers parent tx confidences when determining the most recent confidence of all the matching inputs & outputs. Before it was simply feeding a repeating list of identical objects into getMostRecentConfidence, via the erroneous line: .map(o -> tx.getConfidence()) (Also add a missing @nullable annotation & make getMostRecentConfidence private instead of protected.) Finally, simplify BisqWalletListener.onTransactionConfidenceChanged, by no longer feeding a singleton list into getMostRecentConfidence whose element is already a return value of that method, as that is a no-op.
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.
utACK
This PR speeds up the loading of the deposit ("Receive funds") and transactions views (under the "Funds" tab), by adding some caches to
WalletService
andDisputeListService
, lazily loading tx confidence tooltips for the two respective lists of items (DepositListItem
andTransactionsListItem
) and applying some low level optimisations toTransactionsAwareTrade.isRelatedToTransaction
.This also fixes a fairly minor bug in the transactions view due to a broken
TranscationsAwareTrade.isRefundPayoutTx
method, as well as a somewhat serious memory leak in the deposit view due to missingBalanceListener
removals from theBtcWalletService
instance.The performance issues in these two views had started to become severe with the number of transactions and addresses now in my Bisq wallet. The deposit view was the worst, taking around 25 seconds to load, with bottlenecks in
WalletService.getTransactionConfidence
&WalletService.getNumTxOutputsForAddress
as seen with JProfiler:The transactions view (with 1210 rows) was taking around 5 seconds to load, with bottlenecks in
TransactionsAwareTrade.isRelatedToTransaction
&Tooltip.<init>
:They both seem significantly faster (in my Bisq client) with the changes in this PR, with the biggest improvement to the deposit view via the two caches added to
WalletService
. The quadratic time issue (# transactions * # past trades) causing the slowness of the transactions view is alleviated but not completely fixed by these changes. It may be possible to do more extensive lazy loading of the relevantTransactionsListItem
fields to get further speedups.