tags:

views:

9

answers:

1
class State < ActiveRecord::Base
  validates_uniqueness_of :identifier
end

Now assume there are two running processes that can possibly insert two of these records simultaneously. Passenger is a good example. Here's a contrived one:

State.find_by_identifier("UNIQ").delete rescue nil
while Time.now < Time.now.change(:hour => 17, :min => 00, :sec => 15)
  sleep 0.001
end
ActiveRecord::Base.transaction do
  s = State.new;s.identifier = "UNIQ"
  s.save!
end
s.valid?

The idea is that we change the time values to be slightly in the future. Then copy/paste the whole thing into two different consoles.

The ultimate goal is to get them to "release" at the same time. What happens? They both succeed in creating a new State object, and they both return false at the end.

Soooo, how do I stop this from happening?

For what it's worth I am using MySQL and the table is InnoDB.

A: 

You use #find_or_create helper methods, in addition to proper validation and a unique index at the database level:

# In a migration
add_index :states, :identifier, :unique => true

class State < ActiveRecord::Base
  validates_uniqueness_of :identifier
end

State.find_or_create_by_identifier("UNIQ")

That will work with any adapter.

François Beausoleil