Модель не проходит проверку, но создается

Я давно сталкиваюсь с проблемой. У меня есть следующий код:

class BrokenModel < ActiveRecord::Base
  validates_with BrokenValidator

  has_many :association_name
end

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

    check_alerted
  end

  private

  def check_alerted
    return if @record.association_name.to_a.empty?

    alerted = <test for alerted>
    if alerted
      @record.errors[:base] << "It was alerted recently"
    end

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

worker.rb

[...]
BrokenModel.create(association_name: [model1, model2])
[...]

В моих журналах для последней распечатки показано, что проверка прошла только один раз, но у меня есть несколько записей, созданных для этой модели с association_name подарок.

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

Просто ради любопытства, это работает в Sidekiq.

редактировать

Поэтому я заметил в своих журналах, что это может быть проблемой параллелизма. Итак, вот что происходит:

  • проверка экземпляра 1: последнее предупреждение: сбой (последнее предупреждение)
  • проверка экземпляра 2: недавно получено предупреждение: пройдено
  • проверка экземпляра 2: другая проверка: не удалось (другая проверка)
  • ошибки создания экземпляра 2: недавно было получено предупреждение + другая проверка
  • ошибки создания экземпляра 1: Нет

Любая подсказка, если в ActiveModel::Validator есть какой-либо вид небезопасности потока или @record может быть перезаписан / разделен другими потоками?

1 ответ

Добавление ошибок в запись НЕ делает ее недействительной. Фактически, когда модель проверяется перед сохранением, все предыдущие ошибки, включая ту, которую вы добавляете в свой код, стираются.

Сделайте эту проверку в модели... Не в рабочей.

validate :check_alerted

def check_alerted
   return if association_name.to_a.empty?
    alerted = test 
    if <test for alerted> 
      errors.add(:base, "It was alerted recently") 
   end 
end
Другие вопросы по тегам