-
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
(2/6) Remove onBatch interfaces in P2PDataStore #3621
Conversation
b008b26
to
d1ab3c0
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.
utAck
Commit 833611f is also part of this PR #3628 (review) |
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.
Besides #3621 (review) everything looks good.
Instead of using a subclass that overwrites a value, utilize Guice to inject the real value of 10000 in the app and let the tests overwrite it with their own.
Remove unused imports and clean up some access modifiers now that the final test structure is complete
Previously, this interface was called each time an item was changed. This required listeners to understand performance implications of multiple adds or removes in a short time span. Instead, give each listener the ability to process a list of added or removed entrys which can help them avoid performance issues. This patch is just a refactor. Each listener is called once for each ProtectedStorageEntry. Future patches will change this.
Minor performance overhead for constructing MapEntry and Collections of one element, but keeps the code cleaner and all removes can still use the same logic to remove from map, delete from data store, signal listeners, etc. The MapEntry type is used instead of Pair since it will require less operations when this is eventually used in the removeExpiredEntries path.
…batch All current users still call this one-at-a-time. But, it gives the ability for the expire code path to remove in a batch.
This will cause HashMapChangedListeners to receive just one onRemoved() call for the expire work instead of multiple onRemoved() calls for each item. This required a bit of updating for the remove validation in tests so that it correctly compares onRemoved with multiple items.
…ch removes bisq-network#3143 identified an issue that tempProposals listeners were being signaled once for each item that was removed during the P2PDataStore operation that expired old TempProposal objects. Some of the listeners are very expensive (ProposalListPresentation::updateLists()) which results in large UI performance issues. Now that the infrastructure is in place to receive updates from the P2PDataStore in a batch, the ProposalService can apply all of the removes received from the P2PDataStore at once. This results in only 1 onChanged() callback for each listener. The end result is that updateLists() is only called once and the performance problems are reduced. This removes the need for bisq-network#3148 and those interfaces will be removed in the next patch.
Now that the only user of this interface has been removed, go ahead and delete it. This is a partial revert of f5d75c4 that includes the code that was added into ProposalService that subscribed to the P2PDataStore.
d1ab3c0
to
a8139f3
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.
utACK
Patches start 78211a6
Before:
After:
A bug was introduced in #3148 where ProposalListPresentation would incorrectly call
updateLists()
when the proposal state didn't change. This pull request should help #3143 (the target of #3148) in a more maintainable way that removes the bug and UI performance issues by callingupdateLists()
less often. There is still an outstanding bug thatupdateLists()
can take seconds to run on each invocation.Initial Bug #3143
If the
P2PDataStore
removes multiple items during the expiration work, each item is signaled separately. TheProposalService
is a listener for these removes and would eventually callupdateLists()
for each remove.Exacerbating Issue
Old TempProposals were not correctly deleted from disk. They would be recreated at startup and the expire code would always remove them. This was fixed with #3150.
Follow Up Bug #3148
The fix for the initial bug was to create a new listener interface on
P2PDataStore
that would signal when the expire code starts and ends. TheProposalListPresentation
would subscribe to this interface and remove its listener from the ProposalService so that all of the extraonRemoved()
calls would not triggerupdateLists()
.The code did remove the proposal listener when the expire code started, but it had a bug where it would ALWAYS call
updateLists()
when the expire code finished if ANYTempProposal
object was received (not necessarily a new one).In the pre-fix code, the
ProposalListPresentation
code only calledupdateList()
when theProposalService
proposal list changed. TheProposalService
had validation to ensure that it only signaled listeners if theProposal
was new. By going around theProposalService
and directly listening to theP2PDataStore
, the new code was running every time an existingProposal
object was seen.Duplicate
Proposal
objects (same payload different sequence number) can be added during startup, onrestart()
, or republished on startup by ownersMyProposalListService::rePublishMyProposalsOnceWellConnected()
. The effect is that the next call to the expiration code would causeupdateLists()
to run when it didn't need to.Fix
The root of the original performance issue is straightforward. Don't update listeners of the
P2PDataStore
or theProposalService
on each remove when batching them is possible.This pull request implements batching by:
onAdded()
andonRemoved()
listener functions to take aCollection
ofProtectedStorageEntry
objects instead of just one.remove()
operations done inP2PDataStore::removeExpiredEntries()
to a singleonRemoved()
call that includes all of the removedProtectedStorageEntry
objects.ProposalService
to only update its listeners once for eachonRemoved()
call.onBatch
listener changesThe end result is that any number of removes from
P2PDataStore::removeExpiredEntries()
will result in 1 call toProposalListPresentation::updateLists()
which was the original intention of the fix.It also cleans up one of the existing code smells for the P2PDataStorage layer that requires listeners to understand if and when
removeExpiredEntries()
is called and if they should do something special.