I have a really simple Rails 3 application where users can reserve one of a finite number of homogeneous items for a particular day. I'm trying to avoid a race condition where two people reserve the last item available on a particular day. The model (simplified) is as follows:
class Reservation < ActiveRecord::Base
belongs_to :user
attr_accessible :date
MAX_TH开发者_运维百科INGS_AVAILABLE = 20
validate :check_things_available
def check_things_available
unless things_available? errors[:base] << "No things available"
end
def things_available?
Reservation.find_all_by_date(date).count < MAX_THINGS_AVAILABLE
end
end
The reservation is being created in the controller via current_user.reservations.build(params[:reservation])
It feels like there is a better way to do this, but I can't quite put my finger on what it is. Any help on how to prevent the race condition would be greatly appreciated.
Not sure this answers your question, but it might point you towards a solution:
http://webcache.googleusercontent.com/search?q=cache:http://barelyenough.org/blog/2007/11/activerecord-race-conditions/
(the original site seems to be down so that's a link to the google cache)
The conclusion on that page is that optimistic locking and row level locking are not solutions for race conditions on create, just on update.
The author suggests reimplementing find_or_create with a db constraint.
Another suggestion is that switching the transaction isolation level to 'serializable' ought to work but there's no information on how to do that in Rails.
Just use any locking mechanism like redis locker
RedisLocker.new("thing_to_sell_#{@thing.id}").run do
current_user.create_reservation(@thing) or raise "Item already sold to another user"
end
精彩评论