简体   繁体   中英

Rails 3.2 ActiveRecord concurrency

I have one application that is a task manager.

Each user can select a new task to be assigned to himself.

Is there a problem of concurrency if 2 users accept the same task at the same moment?

My code looks like this:

if @user.task == nil
  @task.user = @user
  @task.save
end

if 2 diferent users, on 2 diferent machines open this url at the same time. Will i have a problem?

You can use optimistic locking to prevent other "stale" records from being saved to the database. To enable it, your model needs to have a lock_version column with a default value of 0 .

When the record is fetched from the database, the current lock_version comes along with it. When the record is modified and saved to the database, the database row is updated conditionally, by constraining the UPDATE on the lock_version that was present when the record was fetched. If it hasn't changed, the UPDATE will increment the lock_version . If it has changed, the update will do nothing, and an exception ( ActiveRecord::StaleObjectError ) will be raised. This is the default behavior for ActiveRecord unless turned off as follows:

ActiveRecord::Base.lock_optimistically = false

You can (optionally) use a column-name other than lock_version . To use a custom name, add a line like the following to your model-class:

set_locking_column :some_column_name

An alternative to optimistic locking is pessimistic locking , which relies on table- or row-level locks at the database level. This mechanism will block out all access to a locked row, and thus may negatively affect your performance.

Never tried it but you may use http://api.rubyonrails.org/classes/ActiveRecord/Locking/Pessimistic.html

You should be able to acquire a lock on your specific task, something like that:

@task = Task.find(some_id)

@task.with_lock do

  #Then let's check if there's still no one assigned to this task
  if @task.user.nil? && @user.task.nil?
    @task.user = @user
    @task.save
  end
end

Again, I never used this so I'd test it with a big sleep inside the lock to make sure it actually locks everything the way you want it

Also I'm not sure about the reload here. Since the row is locked, it may fail. But you have to make sure your object is fresh from the db after acquiring the lock, there may be another way to do it.

EDit : NO need to reload, I checked the source code and with_lock does it for you. https://github.com/rails/rails/blob/4c5b73fef8a41bd2bd8435fa4b00f7c40b721650/activerecord/lib/active_record/locking/pessimistic.rb#L61

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM