我们在 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
从数据库的角度来看,这是否有意义?