Hi, In my app. I have the following set of models class Course < ActiveRecord::Base has_many :registrations has_many :users, :through => :registrations end class User < ActiveRecord::Base has_many :registrations has_many :courses, :through => :registrations end class Registration < ActiveRecord::Base belongs_to :user belongs_to :course end each course model has a ''capacity'' attribute, which dictates how many places are available on that particular course. This is how I enforce the ''capacity'' constraint when a registration is created: class Course < ActiveRecord::Base ... def enrol(user) if registration_count >= capacity errors.add_to_base("this course is now full") return false end self.registrations.create(:user_id => user.id) end def registration_count registrations.length end ... end My concern is that my code won''t guard against a race condition where 2 or more users try to register for a course that is near it capacity limit. My first thought was to wrap the registration code in a transaction and rollback if the capacity of registrations on a course has been exceeded. I''m not sure if this is a valid approach and I would love to hear from someone who could provide some adivice on how I might proceed. thanks and regards, C.
Cathal O''Riordan wrote: [...]> My concern is that my code won''t guard against a race condition where > 2 or more users try to register for a course that is near it capacity > limit. My first thought was to wrap the registration code in a > transaction and rollback if the capacity of registrations on a course > has been exceeded.[...] Use check constraints in the DB and let *it*, not your app, worry about concurrency issues! If your DB server can''t handle check constraints, get one that can. Best, -- Marnen Laibow-Koser http://www.marnen.org marnen-sbuyVjPbboAdnm+yROfE0A@public.gmane.org -- Posted via http://www.ruby-forum.com/.
Marnen Laibow-Koser wrote:> Cathal O''Riordan wrote: > [...] >> My concern is that my code won''t guard against a race condition where >> 2 or more users try to register for a course that is near it capacity >> limit. My first thought was to wrap the registration code in a >> transaction and rollback if the capacity of registrations on a course >> has been exceeded. > [...] > > Use check constraints in the DB and let *it*, not your app, worry about > concurrency issues!That''s one approach. Wrapping it up in a transaction with read isolation would work fine too. Alternatively (and better for concurrency), keep a counter cache on the registration_count and use the default isolation level. -- Roderick van Domburg -- Posted via http://www.ruby-forum.com/.
Don''t check constrainsts just ensure that data is a certain format? On May 23, 12:21 am, Marnen Laibow-Koser <rails-mailing-l...@andreas- s.net> wrote:> Cathal O''Riordan wrote: > > [...]> My concern is that my code won''t guard against a race condition where > > 2 or more users try to register for a course that is near it capacity > > limit. My first thought was to wrap the registration code in a > > transaction and rollback if the capacity of registrations on a course > > has been exceeded. > > [...] > > Use check constraints in the DB and let *it*, not your app, worry about > concurrency issues! > > If your DB server can''t handle check constraints, get one that can. > > Best, > -- > Marnen Laibow-Koserhttp://www.marnen.org > mar...-sbuyVjPbboAdnm+yROfE0A@public.gmane.org > -- > Posted viahttp://www.ruby-forum.com/.
Hi Roderick, Could you provide an example of how this would work? regards, C. On May 23, 11:58 am, Roderick van Domburg <rails-mailing-l...@andreas- s.net> wrote:> Marnen Laibow-Koser wrote: > > Cathal O''Riordan wrote: > > [...] > >> My concern is that my code won''t guard against a race condition where > >> 2 or more users try to register for a course that is near it capacity > >> limit. My first thought was to wrap the registration code in a > >> transaction and rollback if the capacity of registrations on a course > >> has been exceeded. > > [...] > > > Use check constraints in the DB and let *it*, not your app, worry about > > concurrency issues! > > That''s one approach. Wrapping it up in a transaction with read isolation > would work fine too. Alternatively (and better for concurrency), keep a > counter cache on the registration_count and use the default isolation > level. > > -- > Roderick van Domburg > -- > Posted viahttp://www.ruby-forum.com/.
Cathal O''Riordan wrote:> Don''t check constrainsts just ensure that data is a certain format?No. They check anything you like -- format, value, whatever. They actually work a lot like Rails validators. Best, -- Marnen Laibow-Koser http://www.marnen.org marnen-sbuyVjPbboAdnm+yROfE0A@public.gmane.org -- Posted via http://www.ruby-forum.com/.
Cathal O''Riordan wrote:> Could you provide an example of how this would work?Take a look at :counter_cache at http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html. Wrap everything up in a transaction, fetch the registration count, administer the new registration and update the counter cache. Any concurrent registrations will be rolled back by the database -- don''t forget to catch that exception and deal with it. -- Roderick van Domburg http://www.nedforce.com -- Posted via http://www.ruby-forum.com/.