如何避免 Rails 缓存导致子对象看起来不存在

Posted

技术标签:

【中文标题】如何避免 Rails 缓存导致子对象看起来不存在【英文标题】:How to avoid Rails caching from causing child objects to appear to not exist 【发布时间】:2021-04-11 07:46:16 【问题描述】:

我正在处理一个订单应用程序,它接受来自 CC 处理供应商的订单,并将它们传递给一个履行应用程序。

class Order < ApplicationRecord
  has_many :items, class_name: 'Product'
  def payload
    @builder = Nokogiri::XML::Builder.new do |xml|
      ...
      self.items.each do |o|
        ...
      end
    end
    @builder.to_xml
  end
  ...
end

当订单进来时,它会创建所有需要的记录,然后尝试将有效负载传递给履行应用程序

def generate_order
  ActiveRecord::Base.transaction do
    # create order
    ...
    External.send_order(self) # Web Call to Customer Support system <= this is were caching is happening
    Product.create!(...) #create the actual product items
    p "Payload #self.payload"
  rescue StandardError => e
    ...
  end
end

因为我们将订单本身传递给客户支持系统,所以在我们实际处理商品之前,Rails 会缓存不含任何商品的订单。因此,当我们尝试查看有效负载时,这些项目不存在。

我可以将标注移至客户支持系统,直到我们处理完项目并解决它,但是..

我们如何确保我们使用的是订单的当前/非缓存版本?

【问题讨论】:

我认为这与缓存无关。这条线 p "Payload #self.payload" 实际上在事务块内吗?问题可能是事务尚未提交,这意味着您尝试检索的记录实际上并不存在。您可以发布有关调用此方法的日志吗? @engineersmnky 感谢您的回复。它在事务块中,但是将 External.send_order(self) 移到 Product.create!() 之后允许“Payload #self.payload”查看已创建的项目。 我不知道External::send_order 做了什么,因为这段代码没有发布,但你可以reloadpayload 中的关联,例如self.items.reload @engineersmnky 谢谢! reload 方法是我正在寻找的。如果您可以将其作为答案,我会将其标记为已接受。 【参考方案1】:

从评论中复制:我不知道External::send_order 做了什么,因为这段代码没有发布。

也就是说,如果您有缓存的关系并且希望更新它,您可以使用ActiveRecord::Relation#reload 来执行此操作。例如

self.items.reload.each do |o|
#...
end

此方法所做的是重置关系,然后再次加载记录。这意味着它将触发查询以重新收集关系的所有记录,因此请注意在何处以及如何使用它(不建议在循环或迭代器中使用它)

【讨论】:

以上是关于如何避免 Rails 缓存导致子对象看起来不存在的主要内容,如果未能解决你的问题,请参考以下文章

如果对象在 Rails 6 中以嵌套形式不存在,如何创建对象?

《Android源码设计模式》--享元模式

Rails:旧数据与新数据不匹配时如何更新片段缓存

Redis分布式锁

如何清除 Rails 动作缓存? (Rails.cache.clear 不起作用)

缓存优化