Rails - implementing a simple lock to prevent editing the same data at the same time

I have an application in which I need to prevent users from editing data while it is being edited by another user. I am trying to think about how to do this and would like to ask for ideas. So far, I have created a settings model that stores the general configuration of the application on db in key / value pairs. So, for locking, I have an instance of settings that called LOCKED_TABLE_UID, and it saved the user_id of the user editing the table, or null (nil) if the table is free.

>> lock = Setting.find_by_key('LOCKED_TABLE_UID') 

Then I implemented 2 methods in my application controller to get and release the lock:

 # current_user returns the user currently logged in def acquire_lock lock = Setting.find_by_key("LOCKED_TABLE_UID") if lock.value # if lock taken, see if it the current_user or someone else if lock.value.to_i == current_user.id.to_i return true else return false end else # lock is free, assign it to this user lock.value = current_user.id return true if lock.save end end def release_lock lock = Setting.find_by_key("LOCKED_TABLE_UID") if lock.value # the lock belongs to current_user, so he can release it if lock.value.to_i == current_user.id.to_i lock.value = nil return true if lock.save else # not your lock, go away return false end else # lock is free, quit bugging return true end end 

I want to create some kind of block code containing a locking mechanism, something like this:

 def some_crud_action requires_locking do |lock| if lock # do some CRUD stuff here else # decline CRUD and give some error end end end 

I would appreciate help in this, but I am also open to other suggestions on how to do this, or about some things that I may have forgotten. This lock does not have to be atomic, but rather simple and most important - that it works :) thanks.

+4
source share
3 answers

You are almost there. Create your require_locking? as you see fit. Then process it using the before_filter file.

  before_filter :requires_locking?, :only => [:update, :destroy] after_filter :release_lock, :only => [:update, :destroy] def requires_locking do |lock| unless acquire_lock lock = Setting.find_by_key("LOCKED_TABLE_UID") user_with_lock = User.find(lock.value) flash[:message] = "Action denied: Table locked by: #{user_with_lock.name}" redirect_to :back end end 
+1
source

Have you seen the built-in ActiveRecord lock function?

+7
source

I like the idea, but I see a big problem with your solution, and this is what you get and release the locks of entire tables.

For a very small application, which can be great, but imagine if you have thousands of users trying to access, say, the PRODUCT table and wait, because someone is editing an entry that is completely unrelated to their own products.

Perhaps you may have a finer approach to grain and locking certain rows instead of tables. The lock will then include the table name, row id, and user id.

0
source

All Articles