代码之家  ›  专栏  ›  技术社区  ›  sa125

Rails-实现一个简单的锁,以防止用户同时编辑相同的数据

  •  4
  • sa125  · 技术社区  · 15 年前

    我有一个应用程序,我需要防止用户在其他用户编辑数据时编辑数据。我在想最好的方法来做这件事,我想征求意见。到目前为止,我已经创建了一个设置模型,它以键/值对的形式在数据库上存储应用程序范围的配置。所以,对于这个锁,我有一个设置实例,它被称为锁定的表UID,它存储了编辑该表的用户的用户ID,如果该表是空闲的,则为空(nil)。

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

    然后,我在应用程序控制器中实现了两种方法来获取和释放锁:

    # 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 回复  |  直到 6 年前
        1
  •  1
  •   EmFi    15 年前

    你就快到了。创建所需的锁定?视情况采取行动。然后用前置过滤器进行处理。

     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
    
        2
  •  7
  •   Simone Carletti    15 年前

    您看到ActiveRecord内置锁定功能了吗?

        3
  •  0
  •   user223734    15 年前

    我喜欢这个想法,但是发现你的解决方案有一个大问题,那就是你正在获取和释放整个表的锁。

    对于一个非常小的应用程序来说,这可能很好,但是想象一下,如果你有成千上万的用户试图访问“产品”表,并且不得不等待,因为有人正在编辑一个与他们自己的产品完全无关的条目。

    也许您可以使用更精细的方法,锁定特定的行而不是表。然后锁将包括表名、行ID和用户ID。

        4
  •  0
  •   Tarek N. Elsamni    6 年前

    我想 acts_as_lockable_by gem正以更简单的术语和更少的代码执行您所要求的操作。它很容易与Rails或者甚至是一个裸露的Ruby项目集成。

    有了这个宝石,你就能得到原子 lock , unlock renew_lock 方法。另外,你会自动过期 ttl 锁定,所以如果狗屎击中风扇,你不能解锁资源,它将自动为你解锁!