#59
Jul 18
Optimistic Locking
When two people attempt to update the same record near the same time, one of the updates will likely be overwritten. You can solve this problem with optimistic locking.
# migrations/011_add_products_lock_version.rb add_column :products, :lock_version, :integer, :default => 0, :null => false # products_controller.rb def update @product = Product.find(params[:id]) if @product.update_attributes(params[:product]) flash[:notice] = "Successfully updated product." redirect_to product_path(@product) else render :action => 'edit' end rescue ActiveRecord::StaleObjectError @product.reload render :action => 'conflict' end
<!-- _form.rhtml --> <%= f.hidden_field :lock_version %> <!-- conflict.rhtml --> <% title "Edit Product Conflict" %> Someone edited the product the same time you did. Please re-apply your changes to the product. <h2>Your Submission:</h2> <pre> <% params[:product].each do |name, value| %> <%=h name.humanize %>: <%=h value %> <% end %> </pre> <h2>Edit Product:</h2> <% form_for :product, :url => product_path(@product), :html => { :method => :put } do |f| %> <%= render :partial => 'form', :locals => { :f => f } %> <%= submit_tag 'Resolve' %> <% end %>




Thanks!let me see
great!!thanks again!
Good show. Locking can be a real pain to understand and implement!
Sorry this is just a bit off topic just wondering what tab terminal software you are using?
@dudzjosh : I think he's using ITerm
dudzjosh: he's using iTerm
hmm, it seems kind of hard to try to resolve the confict "behind the scenes" without keeping any additional information about the edits.
i think you can do something like a form with radio buttons to choose between the conflicting field(s), but that won't work in case of 2 concurrent changes to the same field, which brings me back to the manual approach. i'm starting to wonder if there's a better way to resolve these dependencies without additional record-keeping.
@vlad, good point. You would need to store the history of the edits with acts_as_versioned or something so you can compare it with the new edits and see who changed what. Then it would be possible to see the two changes didn't overlap (one changed the price and the other changed the description) so it can be resolved behind the scenes.
Alternatively you could pass the model's attributes in hidden fields in addition to the editable fields. That way you have the last history without using act_as_versioned. That might be messy though.
oh, so there's an acts_as_versioned? awesome.
Yep, it's a plugin by Rick Olson:
http://svn.techno-weenie.net/projects/plugins/acts_as_versioned/
Ryan, Thanks for this! I have a question for some weird behavior I'm seeing:
In my controller I'm doing a model.reload as you've implemented it, and render a "conflict" action. In the controller, if I 'puts' on any of the model variables before and after the reload, I can see the change, but the form never reflects the change. Doing a 'puts' in the form writes out the pre-update model information.
My form is a bit different in that I'm using a form_tag and :id => model_name, but as I understand it, it should reload the model from the db, correct? I'm missing something simple.
Thanks in advance for a great site!
Ryan,
Ok, ignore me, found the problem, which is in my model definition.
Sometimes it just helps to talk. ;-)