Rails 验证线程安全吗

Is this Rails validation thread-safe

这是 运行 同时在多个 Sidekiq 实例和 worker 中,似乎已经产生了几个问题,比如实例在不应该被分配 "It was alerted recently" 错误的时候和相反。

这种情况很少见,但正在发生,这是问题所在还是其他原因?

class BrokenModel < ActiveRecord::Base
  validates_with BrokenValidator

end

class BrokenValidator < ActiveModel::Validator
  def validate record
    @record = record

    check_alerted
  end

  private

  def check_alerted
    if AtomicGlobalAlerted.new(@record).valid?
      @record.errors[:base] << "It was alerted recently"
    end

    p "check_alerted: #{@record.errors[:base]}"
  end
end

class AtomicGlobalAlerted
  include Redis::Objects
  attr_accessor :id

  def initialize id
    @id = id
    @fredis = nil

    Sidekiq.redis do |redis|
        @fredis = FreshRedis.new(redis, freshness: 7.days, granularity: 4.hours)
    end
  end

  def valid?
    @fredis.smembers.includes?(@id)
  end
end

我认为 rails 中存在一些线程安全问题,但我们可以通过采取必要的预防措施来克服这些问题。

局部变量(例如您的局部变量)对于方法块的每个特定调用都是局部的。如果两个线程同时调用这个块,那么每个调用都将获得自己的局部上下文变量,除非涉及共享资源,否则它们不会重叠:实例变量如 (@global_var)、静态变量 (@@static_var), globals ($global_var) 会导致并发问题。

您正在使用实例变量,每次使用 validate_record 方法时只需实例化它,希望您的问题会像这样消失:

def validate record
    @record.errors[:base] = []
    @record = record
    check_alerted
end

更多详情可以访问这个详细的link

或尝试在此处研究 rails 配置:link

我们在工作中遇到了类似的事情,经过大量挖掘终于弄清楚发生了什么。

class 方法 validates_with 使用验证器 (BrokenValidator) 的一个实例来验证您尝试验证的 class 的所有实例 (BrokenModel).通常这很好,但是您正在分配一个变量 (@record) 并在另一个方法 (check_alerted) 中访问该变量,因此其他线程正在分配 @record 而其他线程仍在尝试 check_alerted.

有两种方法可以解决此问题:

1) 将 record 传递给 check_alerted:

class BrokenValidator < ActiveModel::Validator
  def validate(record)    
    check_alerted(record)
  end

  private

  def check_alerted(record)
    if AtomicGlobalAlerted.new(record).valid?
      record.errors[:base] << "It was alerted recently"
    end

    p "check_alerted: #{record.errors[:base]}"
  end
end

2) 使用 validates_with 的实例版本,它为您要验证的每个模型实例创建一个新的验证器实例:

class BrokenModel < ActiveRecord::Base
  validate :instance_validators

  def instance_validators
    validates_with BrokenValidator
  end

end

任一解决方案都应该有效并解决并发问题。如果您遇到任何其他问题,请告诉我。