Мы столкнулись с проблемами с состоянием гонки в нашем приложении 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
Имеет ли это смысл с точки зрения базы данных?