#59
Jul 18, 2007

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.
Download (16.4 MB, 7:26)
alternative download for iPod & Apple TV (10.4 MB, 7:26)
# 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 %>

RSS Feed for Episode Comments 12 comments

1. chineseGuy Jul 18, 2007 at 00:21

Thanks!let me see


2. chineseGuy Jul 18, 2007 at 00:29

great!!thanks again!


3. Lisa Seelye Jul 18, 2007 at 00:58

Good show. Locking can be a real pain to understand and implement!


4. dudzjosh Jul 18, 2007 at 03:20

Sorry this is just a bit off topic just wondering what tab terminal software you are using?


5. Bounga Jul 18, 2007 at 04:39

@dudzjosh : I think he's using ITerm


6. vlad Jul 18, 2007 at 05:44

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.


7. Ryan Bates Jul 18, 2007 at 07:33

@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.


8. vlad Jul 18, 2007 at 08:42

oh, so there's an acts_as_versioned? awesome.


9. Ryan Bates Jul 18, 2007 at 08:55

Yep, it's a plugin by Rick Olson:

http://svn.techno-weenie.net/projects/plugins/acts_as_versioned/


10. Eric Baker Jul 24, 2007 at 15:23

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!


11. Eric Baker Jul 24, 2007 at 15:56

Ryan,

Ok, ignore me, found the problem, which is in my model definition.

Sometimes it just helps to talk. ;-)


12. kino May 23, 2008 at 01:55

Still, there can be no doubt that the objects in space and time are a representation of, so far as I know, natural causes.

Add your comment:

(SKIP THIS ONE)

(required)

(not shown)


(use pastie or gist for code)

sponsored by:
if you want to help:
required:
Get Quicktime Player