这同时在多个 Sidekiq 实例和 worker 中运行,这似乎产生了一些问题,例如实例被分配了“最近被警告”的错误,而在不应该和相反的情况下。
这种情况很少见,但正在发生,这是问题所在还是其他原因?
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
最佳答案
我们在工作中遇到了类似的事情,经过大量挖掘终于弄清楚发生了什么。
类方法 validates_with
使用验证器的一个实例 (BrokenValidator
) 来验证您尝试验证的类的所有实例 (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
这两种解决方案都可以解决并发问题。如果您遇到任何其他问题,请告诉我。
关于ruby-on-rails - 这是 Rails 验证线程安全的吗,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36923269/