begin
user = User.transaction(requires_new: true) do
User.find_or_create_by(username: some_value)
end
rescue ActiveRecord::RecordNotUnique
retry
end
call_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) ) end
You need to enforce the uniqueness at the database level, then catch errors.More info on transactions here:iiuc - transaction enforces that all changes within the transaction are applied (or none).it doesn't enforce that changes in transaction B are applied before a lookup in transaction A happenslooks like you could also work with a lock, though the database enforcement seems more natural.
User.find_or_create_by(username: some_value)
On Wednesday, 10 October 2018 10:52:33 UTC+2, Rob Jonson wrote:You need to enforce the uniqueness at the database level, then catch errors.More info on transactions here:iiuc - transaction enforces that all changes within the transaction are applied (or none).it doesn't enforce that changes in transaction B are applied before a lookup in transaction A happenslooks like you could also work with a lock, though the database enforcement seems more natural.Thank you very much, Rob ! I will add a unique index to Users#username column (I had one but without unique option).What about wrapping the call into transaction ? Will it be correct to call retry ? If I got it right, retry will take another call to
User.find_or_create_by(username: some_value)but this time will fetch the existing record ? Or not ?Thank you.
def user
raise(ExceptionHandler::InvalidToken, ("#{Message.invalid_token}")) unless decoded_auth_token[:sub].present?
begin
User.transaction(requires_new: true) do
@user ||= User.find_or_create_by(username: decoded_auth_token[:sub])
Rails.logger.silence do
@user.update_column(:token, http_auth_header)
end
end
rescue ActiveRecord::RecordNotUnique
# will retry to find/create a user to avoid thread racing and
# creating users with duplicate username
retry
end
@user
end
Thank you very much, Rob ! I will add a unique index to Users#username column (I had one but without unique option).What about wrapping the call into transaction ? Will it be correct to call retry ? If I got it right, retry will take another call to
User.find_or_create_by(username: some_value)but this time will fetch the existing record ? Or not ?
def find_or_create_by(attributes, &block) find_by(attributes) || create(attributes, &block) end