Ruby:进行乐观锁定的正确方法?

时间:2018-10-06 19:02:53

标签: ruby-on-rails ruby ruby-on-rails-3 ruby-on-rails-4 locking

上下文:产品正在由多个线程更新。因此,它引领了比赛条件。所以我使用乐观锁定,因为它经常不更新。产品的最新更新状态由updated_at属性确定。

例如,在机器上的t1上更新Prod(id = 1):1。机器2上的t2处更新了相同的产品(id = 1)。现在,机器:1上Prod(id = 1)的状态已过时。

方法要确定陈旧:我将比较计算机上updated_at的值与数据库值中updated_at的存储值。

我主要关心的是设置@original_updated_at的值。我应该使用attr_writer :original_updated_at吗?这是乐观锁定的正确方法。

attr_accessor : :original_updated_at
  def original_updated_at
    @original_updated_at || updated_at.to_f
  end


  def stale_object?
    if updated_at.to_f > original_updated_at.to_f
      @original_updated_at = nil
      return true
    else
      @original_updated_at = updated_at.to_f
      return false
    end
  end

  def recalculate
    tries = 0
    begin
      raise Product::StaleObjectError.new("StaleObjectError") if stale_object?
      attributes["updated_at"] = Time.now.utc
      Product.where(:id => self.id).update_all(attributes)
      self.attributes = attributes
    rescue Product::StaleObjectError => e
      if tries < MAX_RETRIES
        tries += 1
        sleep(1 + tries)
        reload
        retry
      else
        raise Product::StaleObjectError("StaleObjectError")
      end
    end
  end

1 个答案:

答案 0 :(得分:0)

您似乎正在使用Rails?不知道为什么要尝试推出自己的解决方案。 只需添加一个lock_version列即可在数据库级别启用乐观锁定。

https://api.rubyonrails.org/classes/ActiveRecord/Locking/Optimistic.html

另外:由于您在谈论多台计算机,因此并发可能来自多个进程和多个线程。

由于您的旧系统在添加锁列时中断,因此需要采取其他解决方案:

0)解决乐观锁定问题

1)使用悲观锁定。根据负载情况-多少个并发读/写-该操作也可能会起作用

2)修正您的锁定代码(由于您将保留有关该类的一些细节,因此无法完全确定其工作原理)

  • 无需举起,接住和重新举起(只需使用循环并在成功时使用break即可)
  • 您正在浮动电话上调用to_f
  • 我不知道self.attributes = attributes应该做什么(根据您显示的代码,我们不清楚attributes是否不是self.attributes之外的东西)
  • 使用时间戳可能无法确定并发更新(可能会生成两个完全相同的时间戳,多台计算机上的时钟可能会关闭,时钟可能会在其中重置或调整)
  • 使用sleep是一种代码味道

当编写的代码太复杂以至于不得不在互联网上询问它是否有效时,那么您可能走错了路。使用乐观锁定解决问题,而不要添加这种解决方法。