This project is archived and is in readonly mode.
AssociationCollection + Optimistic Locking + Save callback break things
Reported by mail2lf (at gmail) | March 21st, 2009 @ 11:40 AM | in 2.x
Consider the following models:
class User has_many :posts end
class Comment belongs_to :user
after_save :update_user
def update_user
// do some weird stuff
user.save
end end
In case you have Optimistic locking enabled on User model, the following code fragment will fail:
@user.posts = [@post1, @post2] ... @user.save
This occured on ActiveRecord 2.1.0.
The cause is simple: when @post1 and @post2 get saved, they invoke their :update_user callbacks, but every model uses its own instance of same User model. The optimistic locking counter gets increased in the models and in the DB, but when you try to save your user model, it has an obsolete version.
An alternative fix is to add a callback: :before_add => lambda { |post, user| r.send :user=, o }, but it's kinda dirty hack I think.
Comments and changes to this ticket
-
mail2lf (at gmail) March 21st, 2009 @ 11:43 AM
Crap, guys, I'm new here, sorry for breaking the code text a bit:
class User has_many :posts end
class Comment belongs_to :user
after_save :update_user
def update_user
// do some weird stuff user.save
end end
-
mail2lf (at gmail) March 24th, 2009 @ 05:00 PM
- Assigned user set to DHH
-
schorsch April 16th, 2009 @ 08:43 PM
I have similar probs with callbacks from related records changing their parents. In my case its an invoice which is to be destroyed. Its attaxched payments have a callback which changes the status of the parent invoice in an after_destroy callback.
when the transaction reaches the invoice its locking version has been increased and the destroy fails.
@@@ruby class Payment
belongs_to :invoice
after_destroy :change_status
def change status #change the invoice unless self.invoice.to_be_destroyed
end
end
I had to enhance the stale error output in /activerecord/lib/active_record/locking/optimistic.rb to track my probs down Yes it might be a bad design and i'll try to avoid such things in the future but for now i need a way to figure out if the object referenced in the callback in inside a (destroy) transaction. I tried setting an instance var @to_be_destroyed but as i just read both objects(invoice / payment.invoice) are no equal. Maybe a thread variable could help or an explicit destroy switch in the db-table? Or has anybody another suggestion?
-
schorsch April 16th, 2009 @ 08:45 PM
I have similar probs with callbacks from related records changing their parents. In my case its an invoice which is to be destroyed. Its attaxched payments have a callback which changes the status of the parent invoice in an after_destroy callback.
when the transaction reaches the invoice its locking version has been increased and the destroy fails.
class Payment
belongs_to :invoice
after_destroy :change_status
def change status #change the invoice unless self.invoice.to_be_destroyed
end
end
I had to enhance the stale error output in /activerecord/lib/active_record/locking/optimistic.rb to track my probs down
Yes it might be a bad design and i'll try to avoid such things in the future but for now i need a way to figure out if the object referenced in the callback in inside a (destroy) transaction.
I tried setting an instance var @to_be_destroyed but as i just read both objects(invoice / payment.invoice) are no equal. Maybe a thread variable could help or an explicit destroy switch in the db-table?
Or has anybody another suggestion?
.... to dumb for using formatting
-
Neeraj Singh June 28th, 2010 @ 08:32 AM
- Milestone changed from 3.x to 2.x
- Importance changed from to
I am not able to reproduce this in rails 3.
ActiveRecord::Schema.define(:version => 20100628071959) do create_table "brakes", :force => true do |t| t.string "name" t.integer "car_id" end create_table "cars", :force => true do |t| t.string "name" t.integer "lock_version", :default => 0 end end class Brake < ActiveRecord::Base belongs_to :car after_save :update_car def update_car car.save end end class Car < ActiveRecord::Base has_many :brakes end Car.create Car.first.brakes.create Car.first.brakes.create Car.create Car.last.brakes = [Brake.first, Brake.last]
-
Santiago Pastorino June 29th, 2010 @ 05:09 AM
mail2lf, schorsch: can you try the git versions of rails 3 and 2-3 to see if you can reproduce the problem?.
Thanks. -
Neeraj Singh July 29th, 2010 @ 09:19 PM
- State changed from new to invalid
Closing this ticket. If it could be reproduced then it will be opened.
-
csnk May 18th, 2011 @ 08:27 AM
We are the professional underwear manufacturer, underwear supplier, underwear factory, custom underwear.
Create your profile
Help contribute to this project by taking a few moments to create your personal profile. Create your profile »
<h2 style="font-size: 14px">Tickets have moved to Github</h2>
The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>