parkert-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org
2008-Dec-31 21:04 UTC
validate not occurring transactionally
I''m running into a strange problem with transactions. I am seeing rare duplicate entries in the DB that should be prevented by my validations. Here is some sample code: class Review < ActiveRecord::Base has_one :member has_one :business attr_accessor :sleep_for def validate validate_uniqueness_combo_of_member_and_business sleep(sleep_for) if self.sleep_for end end and here is that code executed: #console 1> r = Review.new(:member_id => 1, :business_id => 4) > r.sleep_for = 5 > r.save!=> true #console 2 (during the 5 sec sleep)> r = Review.new(:member_id => 1, :business_id => 4) > r.save!=> true I would expect the first save to lock the table and the second to fail after 5 seconds. Instead they both succeed and I end up with bad data in the db. It also fails if I wrap both save! calls in Review.transaction blocks. I could not find docs on whether validate() is within the transaction, but I would expect it to be. Any ideas on what is wrong here? --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Validations about uniqueness of things are prone to race conditions. If you need a cast iron guarantee that can only come from the database itself (ie unique index etc...) Sent from my iPhone On 31 Dec 2008, at 21:04, parkert-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org wrote:> > I''m running into a strange problem with transactions. I am seeing > rare duplicate entries in the DB that should be prevented by my > validations. Here is some sample code: > > class Review < ActiveRecord::Base > has_one :member > has_one :business > attr_accessor :sleep_for > > def validate > validate_uniqueness_combo_of_member_and_business > sleep(sleep_for) if self.sleep_for > end > end > > and here is that code executed: > > #console 1 >> r = Review.new(:member_id => 1, :business_id => 4) >> r.sleep_for = 5 >> r.save! > => true > > #console 2 (during the 5 sec sleep) >> r = Review.new(:member_id => 1, :business_id => 4) >> r.save! > => true > > I would expect the first save to lock the table and the second to fail > after 5 seconds. Instead they both succeed and I end up with bad data > in the db. It also fails if I wrap both save! calls in > Review.transaction blocks. > > I could not find docs on whether validate() is within the transaction, > but I would expect it to be. Any ideas on what is wrong here? > >--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
parkert-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org
2008-Dec-31 23:30 UTC
Re: validate not occurring transactionally
On Dec 31, 2:58 pm, Frederick Cheung <frederick.che...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> Validations about uniqueness of things are prone to race conditions. > If you need a cast iron guarantee that can only come from the database > itself (ie unique index etc...)The app allows an "anonymous" user to have multiple reviews of a business, so a DB level constraint probably isn''t appropriate. But regardless, my question is about what I can/can''t expect from AR with respect to transactions/validations. If validations aren''t within the transaction they''re not useful. Since I don''t believe the Rails folks are stupid, I''m trying to figure out what I''m doing wrong here. Thanks, pt. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
unknown wrote:> #console 1 >> r = Review.new(:member_id => 1, :business_id => 4)This line does absolutely nothing at the database level. All you have done here is make a new Ruby object.>> r.sleep_for = 5 >> r.save!This row create a database transaction, writes the data from the Ruby object''s attributes and ends the transaction.> => true > > #console 2 (during the 5 sec sleep) >> r = Review.new(:member_id => 1, :business_id => 4)Again the database know nothing about the creation of the Ruby object.>> r.save!Since this executes before you have saved the first record, and validations for both records have already occurred your uniqueness validation WILL fail and allow a duplicate to be recorded in the database.> => true > > I would expect the first save to lock the table and the second to fail > after 5 seconds. Instead they both succeed and I end up with bad data > in the db. It also fails if I wrap both save! calls in > Review.transaction blocks.Locking the table in this scenario would be death to scalability. What most people do in this case is to use the validates_uniqueness_of for convenience, but also ensure data integrity by adding the proper unique index to the database. A fail-and-recover scheme is what is needed here. As much as table locking sounds like the way to go, it just isn''t because it''s way to expensive. I also understand your issue with anonymous users (especially since you yourself have chosen to be an anonymous coward on this forum -- kidding a little there hehe). But, that''s just something you''ll have to weigh for yourself. As Frederick has already mentioned: race conditions are a tough problem to solve and are best done at the database layer. They are a lot more difficult to solve at the model object layer, which is the layer where validations are performed). This, however, does not render validations worthless. AFAIK uniqueness validations are the only ones that suffer from this issue. -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Oh! And BTW this issue is by no means unique to Rails. I''ve had to deal with this issue in every ORM I''ve ever come across. Most ORMs don''t even attempt to provide something "built-in" to help deal with this. In most of them you have to develop your own solution anyway. -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
On 31 Dec 2008, at 23:30, parkert-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org wrote:> > On Dec 31, 2:58 pm, Frederick Cheung <frederick.che...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> > wrote: >> Validations about uniqueness of things are prone to race conditions. >> If you need a cast iron guarantee that can only come from the >> database >> itself (ie unique index etc...) > > The app allows an "anonymous" user to have multiple reviews of a > business, so a DB level constraint probably isn''t appropriate. But > regardless, my question is about what I can/can''t expect from AR with > respect to transactions/validations. If validations aren''t within the > transaction they''re not useful. Since I don''t believe the Rails folks > are stupid, I''m trying to figure out what I''m doing wrong here. >It should be easy to work out whether they run in a transaction or not by looking at the log files. Most validations are just about the one object and so couldn''t care less. Even if they are it wouldn''t help your uniqueness validations (unless you''re actually locking rows) Fred> Thanks, > > pt. > >--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---