I would like to limit the number of items in an association. I want to ensure the User doesn\'t have more than X Things. This question was asked before and the solution had th
I thought I'd chime in here. It seems like most of the answers here fail under race-conditions. I'm trying to limit the number of users who can sign up at a certain price point in our app. Checking the limit in Rails means that 10 simultaneous registrations could get through, even if it exceeds the limit I'm trying to set.
For example, say I want to restrict registrations to no more than 10. Let's say that I already have 5 users registered. Let's also say that 6 new users attempt to register at the same time. In 6 different threads, Rails reads the number of slots remaining, and it gets the answer 5
. This passes validation. Rails then allows all registrations to go through, and I have 11 registrations. :/
Here's how I solved this problem:
def reserve_slot(price_point)
num_updated = PricePoint.where(id: price_point.id)
.where('num_remaining <= max_enrollments')
.update_all('num_remaining = num_remaining + 1')
if num_updated == 0
raise ActiveRecord::Rollback
end
end
Using this approach, I never allow more registrations than max_enrollments
, even when the app is under load. This is because the validation and increment is done in a single, atomic database operation. Note, too that I always call this method from within a transaction, so it rolls back under failure.