我可以使用对象的乐观锁定来保护其关联吗?
我们在 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
从数据库的角度来看这是否有意义?
We're running into problems with a race condition in our rails app. Here's a bit of (simplified) code before I explain:
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
What's happening is that create_or_update_sending_message
is being called from two processes. Both see the messages collection as empty, so both create a new message. Then a third process loads the question, modifies it, tries to save it, and we get an error thrown in a place that isn't where the actual problem occurred.
I can think of a few ways to avoid this if we were designing from scratch, but unfortunately create_or_update_sending_message
is too deep in legacy code for this to be practical.
We have optimistic locking enabled for our Question model. It doesn't help because the question isn't being saved - only its messages are.
Is there a way to use the optimistic locking on the question to guard against the saving of the creation of the message? So it would look something like
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
Does that even make sense from a database point of view?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论