rails - реализация простой блокировки для предотвращения одновременного редактирования пользователем одних и тех же данных

У меня есть приложение, в котором мне нужно запретить пользователям редактировать данные, пока они редактируются другим пользователем. Я пытаюсь придумать лучший способ сделать это и хотел спросить идеи. До сих пор я создал модель настроек, которая хранит конфигурацию приложения в базе данных в парах ключ/значение. Итак, для блокировки у меня есть экземпляр настроек, который называется LOCKED_TABLE_UID, и в нем хранится user_id пользователя, редактирующего таблицу, или null (ноль), если таблица свободна.

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

Затем я реализовал 2 метода в своем контроллере приложений для получения и снятия блокировки:

# 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's 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

Я хочу создать какой-то блочный код, содержащий механизм блокировки, что-то вроде этого:

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

Я был бы признателен за помощь в этом, но я также открыт для других предложений о том, как выполнить все это, или некоторых вещей, которые я, возможно, упустил из виду. Эта блокировка не обязательно должна быть атомарной, но довольно простой и, самое главное, чтобы она работала :) спасибо.


person sa125    schedule 26.11.2009    source источник


Ответы (4)


Ты почти там. Создайте свой require_locking? действовать, как вы считаете нужным. Затем обработайте его с помощью before_filter.

 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
person EmFi    schedule 26.11.2009

Вы видели встроенную функцию блокировки ActiveRecord?

person Simone Carletti    schedule 26.11.2009
comment
Это хорошо, но я искал что-то более простое, которое также предоставляло бы информацию о пользователе, который в данный момент держит блокировку. - person sa125; 26.11.2009

Мне нравится эта идея, но я вижу большую проблему в вашем решении, заключающуюся в том, что вы получаете и снимаете блокировки целых таблиц.

Для очень маленького приложения это может быть хорошо, но представьте, что у вас есть тысячи пользователей, пытающихся получить доступ, скажем, к таблице «ПРОДУКТЫ», и им приходится ждать, потому что кто-то редактирует запись, совершенно не связанную с их собственными продуктами.

Возможно, вы могли бы использовать более тонкий подход и заблокировать определенные строки вместо таблиц. Тогда блокировка будет включать имя таблицы, идентификатор строки и идентификатор пользователя.

person Community    schedule 03.12.2009

Я думаю, что гем acts_as_lockable_by точно делает то, о чем вы просили, в более простых терминах и меньшем количестве кода. Его легко интегрировать с рельсами или даже с голым рубиновым проектом.

С этим драгоценным камнем вы получаете атомарные методы lock, unlock и renew_lock. Кроме того, вы получаете ttl блокировки с автоматически истекающим сроком действия, поэтому, если дерьмо попадает в вентилятор, и вы не можете разблокировать ресурс, он будет автоматически разблокирован для вас!

person Tarek N. Elsamni    schedule 06.11.2018