Ruby on Rails
Tuesday, October 9, 2018
On Oct 9, 2018, at 2:03 PM, belgoros <s.cambour@gmail.com> wrote:I can't figure out the right way to use find_or_create_by method which is not atomic.In short, I have a before_action filter is used to either to find or create a User by its username.def userif decoded_auth_token && decoded_auth_token[:sub]@user ||= User.find_or_create_by!(username: decoded_auth_token[:sub])Rails.logger.silence do@user.update_column(:token, http_auth_header)end@userendrescue ActiveRecord::RecordInvalid => eraise(ExceptionHandler::InvalidToken,("#{Message.invalid_token} #{e.message}"))endThe problem is that the above method is called twice by different threads: 2 requests com from a JS front-end app, and I have the situation when 2 Users are created with the same username.I tried to apply the suggested solution and wrap the method call in a transaction and use retry:begin user = User.transaction(requires_new: true) do User.find_or_create_by(username: some_value) end rescue ActiveRecord::RecordNotUnique retry endcall_some_method to update other user attributes in User model:def update_user_info(options) identifier = normalize_identifier(options[:sitenumber]) update( first_name: options[:givenName], last_name: options[:sn], shop_identifier: identifier, shop: user_shop(identifier) ) endbut it creates nevertheless the duplicate record. What am I missing ?Used Rails version: 5.2.0Ruby: 2.5.0Thank you.
I would at least validate at the SQL level for anything that should NEVER happen. It sounds like you might have a bit of a race condition, but your DB should keep integrity in any event if set up right. (What's DB backend are you using?)
Good luck!
Phil
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment