问题
I'm trying to enforce uniqueness of values in one of my table fields. Changing the table isn't an option. I need to use ActiveRecord to conditionally insert a row into the table but I'm concerned about synchronization.
Does first_or_create
in Rails ActiveRecord prevent race conditions?
This is the source code for first_or_create
from GitHub:
def first_or_create(attributes = nil, options = {}, &block)
first || create(attributes, options, &block)
end
Is it possible that a duplicate entry will result in the database due to synchronization issues with multiple processes?
回答1:
Yes, it's possible.
You can significantly reduce the chance of conflict with either optimistic or pessimistic locking. Of course optimistic locking requires adding a field to the table, and pessimistic locking doesn't scale as well--plus, it depends on your data store's capabilities.
I'm not sure whether you need the extra protection, but it's available.
回答2:
The Rails 4 documentation for find_or_create_by provides a tip that may be useful for this situation:
Please note this method is not atomic, it runs first a SELECT, and if there are no results an INSERT is attempted. If there are other threads or processes there is a race condition between both calls and it could be the case that you end up with two similar records.
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
Similar error catching may be useful for Rails 3. (Not sure if the same ActiveRecord::RecordNotUnique
error is thrown in Rails 3, so your implementation may need to be different.)
来源:https://stackoverflow.com/questions/10641647/race-conditions-in-rails-first-or-create