-
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
PaymentAccount object removal from ObservableSet issue #3572
PaymentAccount object removal from ObservableSet issue #3572
Conversation
I just tried to reproduce the issue by only using your Test but without your fix. Turns out the test does not fail then either. Have you been able to reproduce the issue with the test? (because I wanted to try if a EDIT: it seems mockito does not memorize the account name, nor does it memorize anything else. Hence, the hash-method returns always the same (null being null being null). So it seems that this kind of test is pretty useless. @christophsturm do you have an opinion on that? |
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.
Test needs some work so it will fail before your patch. I've given an example of one inline.
Thanks @freimair for the test issue. Have resolved it 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.
great! Now testing is good for something. thanks!
Could you try if my @EqualsAndHashCode.Exclude
suggestion works? and if yes, update the PR accordingly?
This issues also happens at Uphold or MoneyGram. All payment methods using a list of tradeCurrencies. |
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.
NACK
We should find the real reason for the issue and not reduce the check to id and date only. See comment above...
After further investigation it seems that ObservableSet behaves weird. If you copy the ObservableSet entries into a HashSet you can remove it.
|
After this change i could remove uphold/revolut without any issue. What i believe is because of hashcode change, we are not able to remove. hashcode change can be because of some state change. so usually we restrict that to unique identifying fields (id, date in this case) to maintain hashcode contract. I couldn't create moneygram account even in prod. will open a bug. #3598 |
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.
NACK
After this change i could remove uphold/revolut without any issue.
What i believe is because of hashcode change, we are not able to remove. hashcode change can be because of some state change. so usually we restrict that to unique identifying fields (id, date in this case) to maintain hashcode contract.
I couldn't create moneygram account even in prod. will open a bug. #3598
But as @chimp1984 mentioned above I'm not sure we should fix it like that with only using id and date. The account name cannot be changed in the interface so this can't be the problem of this bug IMO. Probably this part of the code doesn't need changes, but more the removePaymentAccount
method in the User class as @chimp1984 pointed out. We have to find out why ObservableSet is behaving not as expected in that case.
public void removePaymentAccount(PaymentAccount paymentAccount) { HashSet<PaymentAccount> temp = new HashSet<>(paymentAccountsAsObservable); boolean changed = temp.remove(paymentAccount); paymentAccountsAsObservable.clear(); paymentAccountsAsObservable.addAll(temp); if (changed) persist(); }
I have done deep debugging of this issue.
what the above code suggests:
Real Issue: we need to implement hashcode contract correctly. that's what i believe have done. |
I tracked it down and it brings up one of the dangers of modifying elements while they are inside Hash containers. The hash bucket for an item is computed at insert time by calling item.hashCode(). But, if the item is later changed such that hashCode() returns a different value, the remove will look for the item in the bucket specified by the hashCode() return value at removal time. This explains why just moving them all to a map and deleting also worked. Also, why after restart it works. The "fix" is to ensure the hashCode is stable before inserting it into the HashSet. The attached patch works, but I would recommend testing it more or auditing the other users that do something similar. For example, the code is almost duplicated in AltCoinAccountsDataModel.
|
Could you see that the objects got modified? I have not seen that but if so that would explain it. |
Yes, the screenshots show the tradeCurrencies object was different inside the debugger at insert time and remove time. This is confirmed with the attached patch that "fixes" it by moving the insert until after the tradeCurrencies are set. Previously, it was inserted, then modified. A quick run through the UI shows that none of the other fields are modifiable after creation time, but I don't have enough background to know other operations that modify that state and if it will have the same issue. For example, can the list of supported currencies change after creation time which would cause the hash to change again? It seems like using the fields that are guaranteed to be unique and stable is the way to go, but I'll leave that to the devs with more knowledge of that area. Knowing why it behaved strangely should be good enough to move forward now. |
No. That the tradeCurrencies are not set initially is a bug. Once the account is set it is immutable. |
We saved the account and afterwards called setSelectedTradeCurrency. That caused a bug with remove as the hash at insert was different as at remove.
Fixed with #3612 |
This is what i tried in this PR. |
no worries, although the bug does not appear anymore due to @chimp1984 s PR, we are currently discussing if your approach breaks anything (we like your idea). But please use lombok for the Equals and Hash stuff. |
I think the issue was more with understanding why it failed initially and not directly tied to the fix. The initial PR suggested that the state changed, but until there was a good understanding of which specific state changed, was it changed intentionally, and what is the scope of a PaymentAccount mutating, it is hard to come to a consensus on the right set of fields to guarantee uniqueness. @beingindot I really appreciate the work to identify and work on this bug. You've started a good conversation on how to approach this type of issue in the future. |
7f1014f
to
93002dc
Compare
Thanks @freimair @julianknutsen. Have pushed the review changes accordingly. |
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.
NACK
I don't see the need for excluding the other fields. It add some unneeded risk and testing effort. The objects should be immutable so any fields has to be fix. If we go further on that area I would recommend to think how we can refactor to make all those accounts immutable. But I think there are many other higher prio stuff to do....
Is it ok to add |
I think we should not change the @EqualsAndHashCode as that was not the source of the problem and the reason for the problem is solved. Manipulating the @EqualsAndHashCode should be only done in cases where we really need it IMO. So I suggest to close that PR. |
Issue:
#3461
Addresses issue by adding equals/hashcode override.
Also added one unit test.
fixes #3461