rails - реализация простой блокировки, чтобы предотвратить одновременное редактирование одних и тех же данных пользователем - PullRequest
4 голосов
/ 26 ноября 2009

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

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

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

Ответы [ 4 ]

7 голосов
/ 26 ноября 2009

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

1 голос
/ 26 ноября 2009

Ты почти у цели. Создать свой 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
0 голосов
/ 06 ноября 2018

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

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

0 голосов
/ 03 декабря 2009

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

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

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

...