Могу ли я использовать оптимистическую блокировку объекта для защиты его ассоциаций? - PullRequest
4 голосов
/ 12 мая 2011

Мы столкнулись с проблемами с состоянием гонки в нашем приложении rails. Вот немного (упрощенного) кода, прежде чем я объясню:

class Message < ActiveRecord::Base
  belongs_to :question
end

class Question < ActiveRecord::Base
  has_many :messages

  def create_or_update_sending_message
    sending_message = messages.detect {|m| m.status == 'sending'}
    sending_message ||= messages.create :status => 'sending'
    sending_message
  end

  def validate
    errors.add_to_base('cannot have multiple sending messages') if messages.select {|m| m.status == 'sending'}.count > 1
  end
end

То, что происходит, это то, что create_or_update_sending_message вызывается из двух процессов. Оба видят коллекцию сообщений как пустую, поэтому оба создают новое сообщение. Затем третий процесс загружает вопрос, модифицирует его, пытается сохранить его, и мы получаем ошибку в месте, где не было реальной проблемы.

Я могу придумать несколько способов избежать этого, если бы мы проектировали с нуля, но, к сожалению, create_or_update_sending_message слишком глубоко унаследован для того, чтобы это было практично.

У нас оптимистическая блокировка включена для нашей модели Вопросов. Это не помогает, потому что вопрос не сохраняется - только его сообщения.

Есть ли способ использовать оптимистическую блокировку в вопросе для защиты от сохранения создания сообщения? Так что это будет выглядеть примерно так:

def create_or_update_sending_message
  self.optimistically_lock do |lock|
    sending_message = messages.detect {|m| m.status == 'sending'}
    sending_message ||= messages.create_with_optimistic_lock lock, :status => 'sending'
    sending_message
  end
end

Имеет ли это смысл с точки зрения базы данных?

...