我可以使用对象的乐观锁定来保护其关联吗?

发布于 2024-11-06 21:39:23 字数 1283 浏览 0 评论 0原文

我们在 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文