Skip to content
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

Bodhi allows users to give +1 twice, counting it as +2 after a Build is updated or removed #1018

Closed
bowlofeggs opened this issue Oct 10, 2016 · 4 comments · Fixed by #1066
Closed
Assignees
Labels
Critical We can't go on living in this sqalor, drop everything and fix it!

Comments

@bowlofeggs
Copy link
Contributor

As of the moment of this filing, this znc-1.6.3-6.el7 update has +2 karma, both of which seem to have come from elyscape. Bodhi should allow users to add further comments with Karma (so that users can change their mind and swap a +1 for a -1 or vice-versa), but it should only count their most recent karma vote in the total.

I am labeling this as critical since it has potential for users to game the system.

@bowlofeggs bowlofeggs added the Critical We can't go on living in this sqalor, drop everything and fix it! label Oct 10, 2016
@bowlofeggs bowlofeggs self-assigned this Oct 25, 2016
@bowlofeggs
Copy link
Contributor Author

I spent some time yesterday and today trying to figure out how this happened. At first, I believed this happened due to a transaction race condition. The karma attribute of an Update is an integer column in the database that stores a denormalized form of the karma data. Though the two comments were 2.5 minutes apart, it was conceivable that a very heavily loaded server (or database server) would give enough time to start two comment transactions, with both of them giving a +1 comment. In this situation, neither of the transactions would see the comment from the other when they check to see if the user has already given a +1. However, it occurred to me that even though this is possible (and with a sleep statement I was able to get two concurrent transactions to go through the +1 process), they will each increase the karma by only 1 from where they started, and they would have each started at the same value, so the total increase of the two transactions is still only 1.

I have not been able to develop any other theories as to how this might have happened, but I do have a solution that I think is viable: I think it would be an improvement to normalize the karma calculation. I do not know why the karma is denormalized, but my guess is that it was done for performance reasons (to save having to load and process the comments to calculate the karma on an Update). However, most views on an Update (such as the web UI and the CLI) are going to want to render the comments anyway, which means that we aren't saving any database loading time by having the karma be a column on the Update table.

Due to this, I propose that we drop the karma column on the Update table and replace Update.karma with a property that iterates the Comments and counts only each user's most recent karma comment, returning that sum. I believe we will not see a noticeable performance impact by adjusting the karma to be normalized, and it will certainly solve the issue with the particular update mentioned in this issue. I believe it will also simplify a few areas of our code, as the karma rules are a bit complicated, and there is some repeated code in this area as well.

@jeremycline
Copy link
Member

I also think normalizing makes good sense although, like you, I am disturbed by the lack of theories. If nothing else, normalizing the karma will cut down on the places this bug (or others) can hide :shipit:

@bowlofeggs
Copy link
Contributor Author

I just had a good chat with @puiterwijk where we attempted to explain what happened to this update, and @puiterwijk found the bug. This code attempts to filter out comments that happened before the update's builds were edited:

            # Take all comments since the previous karma reset
            reset_index = 0
            for i, comment in enumerate(self.comments):
                if (comment.user.name == u'bodhi' and ('New build' in
                    comment.text or 'Removed build' in comment.text)):
                    reset_index = i

            mycomments = [c.karma for c in self.comments if c.user.name == author][reset_index:]

@puiterwijk noted that the reset_index is used on the comments after they were filtered by the user's id, rather than being used before the filter. That last line should probably look like this instead:

mycomments = [c.karma for c in self.comments[reset_index:] if c.user.name == author]

@puiterwijk and I talked about whether to make this simple change, or whether to go ahead with the refactor to eliminate the karma column and always calculate it on demand and we agreed that the refactor would still be the preferred way to address this issue. The code as it is is quite complicated and the proposed refactor would greatly simplify the code which should reduce the maintenance burden and increase the product quality as a result.

@bowlofeggs
Copy link
Contributor Author

bowlofeggs commented Oct 26, 2016

It just occurred to me that my transaction theory does reveal another similar problem: if two users manage to start concurrent transactions and they both give a +1, the overall karma will only be increased by 1 and not by 2 because they will both set the karma to the current value + 1 instead of atomically increasing the value upon the COMMIT.

The proposed refactor would also solve this race condition, which makes it even more the right direction to go.

@bowlofeggs bowlofeggs changed the title Bodhi seems to allow users to give +1 twice, counting it as +2 Bodhi allows users to give +1 twice, counting it as +2 after a Build is updated or removed Oct 26, 2016
bowlofeggs added a commit to bowlofeggs/bodhi that referenced this issue Oct 26, 2016
Prior to this commit Update karma had been denormalized, but that
led to a few issues with keeping the karma column correctly in sync
with the Comments in an Update. In one case, a user was able to
double +1 an Update, causing the karma column to contain an
incorrect value. The code that managed the karma column was
complicated and it was determined that the denormalized column did
not provide a significant enough performance gain to justify the
complex code it takes to maintain correct values in the database.
Nearly all views on Updates need to load the associated Comments
from the database, so calculating the Update.karma property
dynamically will not introduce any new queries in most cases.

This commit drops the karma column and replaces it with two
properties. The first calculates the sums of positive and negative
karma separately, and the second uses the first to generate and
return the overall karma. The first property was created to replace
the has_negative_karma() method, which had an issue where it failed
to correctly filter out karma before karma reset events. Rather
than fix that method, it was determined that it would be simpler to
drop it and rework the caller to use the new detailed_karma()
property to determine if there is negative karma.

This commit also addresses a third issue with karma counting where
users who changed their mind more than once would no longer be able
to change the karma of an Update.

fixes fedora-infra#1018
fixes fedora-infra#1064
fixes fedora-infra#1065
bowlofeggs added a commit to bowlofeggs/bodhi that referenced this issue Oct 27, 2016
Prior to this commit Update karma had been denormalized, but that
led to a few issues with keeping the karma column correctly in sync
with the Comments in an Update. In one case, a user was able to
double +1 an Update, causing the karma column to contain an
incorrect value. The code that managed the karma column was
complicated and it was determined that the denormalized column did
not provide a significant enough performance gain to justify the
complex code it takes to maintain correct values in the database.
Nearly all views on Updates need to load the associated Comments
from the database, so calculating the Update.karma property
dynamically will not introduce any new queries in most cases.

This commit drops the karma column and replaces it with two
properties. The first calculates the sums of positive and negative
karma separately, and the second uses the first to generate and
return the overall karma. The first property was created to replace
the has_negative_karma() method, which had an issue where it failed
to correctly filter out karma before karma reset events. Rather
than fix that method, it was determined that it would be simpler to
drop it and rework the caller to use the new _composite_karma()
property to determine if there is negative karma.

This commit also addresses a third issue with karma counting where
users who changed their mind more than once would no longer be able
to change the karma of an Update.

fixes fedora-infra#1018
fixes fedora-infra#1064
fixes fedora-infra#1065
bowlofeggs added a commit to bowlofeggs/bodhi that referenced this issue Oct 27, 2016
Prior to this commit Update karma had been denormalized, but that
led to a few issues with keeping the karma column correctly in sync
with the Comments in an Update. In one case, a user was able to
double +1 an Update, causing the karma column to contain an
incorrect value. The code that managed the karma column was
complicated and it was determined that the denormalized column did
not provide a significant enough performance gain to justify the
complex code it takes to maintain correct values in the database.
Nearly all views on Updates need to load the associated Comments
from the database, so calculating the Update.karma property
dynamically will not introduce any new queries in most cases.

This commit drops the karma column and replaces it with two
properties. The first calculates the sums of positive and negative
karma separately, and the second uses the first to generate and
return the overall karma. The first property was created to replace
the has_negative_karma() method, which had an issue where it failed
to correctly filter out karma before karma reset events. Rather
than fix that method, it was determined that it would be simpler to
drop it and rework the caller to use the new _composite_karma()
property to determine if there is negative karma.

This commit also addresses a third issue with karma counting where
users who changed their mind more than once would no longer be able
to change the karma of an Update.

fixes fedora-infra#1018
fixes fedora-infra#1064
fixes fedora-infra#1065
bowlofeggs added a commit to bowlofeggs/bodhi that referenced this issue Oct 27, 2016
Prior to this commit Update karma had been denormalized, but that
led to a few issues with keeping the karma column correctly in sync
with the Comments in an Update. In one case, a user was able to
double +1 an Update, causing the karma column to contain an
incorrect value. The code that managed the karma column was
complicated and it was determined that the denormalized column did
not provide a significant enough performance gain to justify the
complex code it takes to maintain correct values in the database.
Nearly all views on Updates need to load the associated Comments
from the database, so calculating the Update.karma property
dynamically will not introduce any new queries in most cases.

This commit drops the karma column and replaces it with two
properties. The first calculates the sums of positive and negative
karma separately, and the second uses the first to generate and
return the overall karma. The first property was created to replace
the has_negative_karma() method, which had an issue where it failed
to correctly filter out karma before karma reset events. Rather
than fix that method, it was determined that it would be simpler to
drop it and rework the caller to use the new _composite_karma()
property to determine if there is negative karma.

This commit also addresses a third issue with karma counting where
users who changed their mind more than once would no longer be able
to change the karma of an Update.

fixes fedora-infra#1018
fixes fedora-infra#1064
fixes fedora-infra#1065
amolkahat pushed a commit to amolkahat/bodhi that referenced this issue Feb 2, 2017
Prior to this commit Update karma had been denormalized, but that
led to a few issues with keeping the karma column correctly in sync
with the Comments in an Update. In one case, a user was able to
double +1 an Update, causing the karma column to contain an
incorrect value. The code that managed the karma column was
complicated and it was determined that the denormalized column did
not provide a significant enough performance gain to justify the
complex code it takes to maintain correct values in the database.
Nearly all views on Updates need to load the associated Comments
from the database, so calculating the Update.karma property
dynamically will not introduce any new queries in most cases.

This commit drops the karma column and replaces it with two
properties. The first calculates the sums of positive and negative
karma separately, and the second uses the first to generate and
return the overall karma. The first property was created to replace
the has_negative_karma() method, which had an issue where it failed
to correctly filter out karma before karma reset events. Rather
than fix that method, it was determined that it would be simpler to
drop it and rework the caller to use the new _composite_karma()
property to determine if there is negative karma.

This commit also addresses a third issue with karma counting where
users who changed their mind more than once would no longer be able
to change the karma of an Update.

fixes fedora-infra#1018
fixes fedora-infra#1064
fixes fedora-infra#1065
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Critical We can't go on living in this sqalor, drop everything and fix it!
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants