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

Fix raising validation if followable already exist(race condition) #5826

Merged

Conversation

gonzar11
Copy link
Contributor

What type of PR is this? (check all applicable)

  • Refactor
  • Feature
  • Bug Fix
  • Optimization
  • Documentation Update

Description

It rescues from ActiveRecord::RecordInvalid assuming that it only be raised in case that we have a race condition trying to create the same Follow model from different threads/request.

Related Tickets & Documents

closes #5793

Mobile & Desktop Screenshots/Recordings (if there are UI changes)

Added to documentation?

  • docs.dev.to
  • readme
  • no documentation needed

[optional] What gif best describes this PR or how it makes you feel?

My first contribution to an open source project.
alt_text

@pr-triage pr-triage bot added the PR: unreviewed bot applied label for PR's with no review label Jan 31, 2020
begin
user_follow = current_user.follow(followable)
rescue ActiveRecord::RecordInvalid => e
Rails.logger.error(e)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may want to do more than just log an error here, e.g. update a DataDog metric. @mstruve can point you in the right direction.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm torn. I don't really think it is that important but at the same time, I have learned when in doubt log it so let's do DataDogStatsClient.increment("users.invalid_follow") instead of Rails.logger here to track it on the off chance we want the metric in the future for some reason.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tbh, I wasn't sure either, hence the "we may want to do more". But like you I lean in the direction of logging more.

@@ -68,7 +68,11 @@ def follow_params
end

def follow(followable, need_notification: false)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

def is an implicit begin, so this could be:

def follow(followable, need_notification: false)
  user_follow = current_user.follow(followable)
rescue ActiveRecord::RecordInvalid => e
  Rails.logger.error(e)
end

See e.g. https://www.rubyguides.com/2019/06/ruby-rescue-exceptions/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @citizen428 . I am seeing that codeclimate/diff-coverage check failed. How can I do to turn this check in green?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unfortunately the report is not very forthcoming on details, so I think you can ignore it :)

Copy link
Contributor Author

@gonzar11 gonzar11 Feb 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@citizen428 If I don't use begin then I have to use return in ensure block. Is this ok for you?

def follow(followable, need_notification: false)
  user_follow = current_user.follow(followable)
rescue ActiveRecord::RecordInvalid => e
  DataDogStatsClient.increment("users.invalid_follow")
ensure
   Notification.send_new_follower_notification(user_follow) if need_notification
   return "followed"
end

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would set it up like this and actually I think that reads pretty cleanly personally. We will see what @citizen428 thinks.

def follow(followable, need_notification: false)
  user_follow = current_user.follow(followable)
  Notification.send_new_follower_notification(user_follow) if need_notification
rescue ActiveRecord::RecordInvalid => e
  DataDogStatsClient.increment("users.invalid_follow")
ensure
   return "followed"
end

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if we should really return "followed" in case something went wrong. Maybe just have different returns based on whether we succeed or fail?

def follow(followable, need_notification: false)
  user_follow = current_user.follow(followable)
  Notification.send_new_follower_notification(user_follow) if need_notification
  "followed"
rescue ActiveRecord::RecordInvalid => e
  DataDogStatsClient.increment("users.invalid_follow")
  # some other message, e.g. "previously followed"
end

Either way, it's such a small detail I'll leave it to your judgement what you think is best, i.e. if you want to keep as-is, I have no problem with that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you folks for your help. I ended up removing ensure because Rubocop doesn't like to have a return within ensure.

user_follow = current_user.follow(followable)
rescue ActiveRecord::RecordInvalid => e
Rails.logger.error(e)
end
Notification.send_new_follower_notification(user_follow) if need_notification
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should add this to our happy path/begin block since we know if the follow is invalid we dont need to send a notification.

@rhymes rhymes self-requested a review February 4, 2020 13:28
Copy link
Contributor

@mstruve mstruve left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🚀

@pr-triage pr-triage bot added PR: reviewed-approved bot applied label for PR's where reviewer approves changes and removed PR: unreviewed bot applied label for PR's with no review labels Feb 4, 2020
@mstruve
Copy link
Contributor

mstruve commented Feb 4, 2020

I know I approved it but it would probably be a good idea to add a spec for the new failure mode

Copy link
Contributor

@rhymes rhymes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thank you @gonzar11!

@gonzar11
Copy link
Contributor Author

gonzar11 commented Feb 4, 2020

I know I approved it but it would probably be a good idea to add a spec for the new failure mode

I thought about that but I couldn't figure out how to simulate this race condition. Do you mean by making request from multiple threads o something like that?

@mstruve
Copy link
Contributor

mstruve commented Feb 4, 2020

I was thinking by creating one and then attempting to use this endpoint to create a second immediately after. But I can live without it :)

@mstruve
Copy link
Contributor

mstruve commented Feb 4, 2020

Oh one last thing, did you sign the CLA?

@gonzar11
Copy link
Contributor Author

gonzar11 commented Feb 5, 2020

Oh one last thing, did you sign the CLA?

I did it :)

@mstruve mstruve merged commit dd796aa into forem:master Feb 5, 2020
@pr-triage pr-triage bot added PR: merged bot applied label for PR's that are merged and removed PR: reviewed-approved bot applied label for PR's where reviewer approves changes labels Feb 5, 2020
@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
PR: merged bot applied label for PR's that are merged
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Return "followed" result if Followable already Exists Instead of Raising a Validation Error
6 participants