It's well known that Model.find_or_create_by(X)
actually does:
and there may be race condition between steps 1 and 2. To avoid a duplication of X in the database one should use an unique index on the set of fields of X. But if you apply an unique index then one of competing transactions would fail with exception (when trying to create a copy of X).
How can I implement 'a safe version' of #find_or_create_by
which would never raise any exception and always work as expected?
The answer is in the doc
Whether that is a problem or not depends on the logic of the application, but in the particular case in which rows have a UNIQUE constraint an exception may be raised, just retry:
begin CreditAccount.find_or_create_by(user_id: user.id) rescue ActiveRecord::RecordNotUnique retry end
Solution 1
You could implement the following in your model(s), or in a Concern if you need to stay DRY
def self.find_or_create_by(*)
super
rescue ActiveRecord::RecordNotUnique
retry
end
Usage: Model.find_or_create_by(X)
Solution 2
Or if you don't want to overwrite find_or_create_by
, you can add the following to your model(s)
def self.safe_find_or_create_by(*args, &block)
find_or_create_by *args, &block
rescue ActiveRecord::RecordNotUnique
retry
end
Usage: Model.safe_find_or_create_by(X)