Rails 5升级至6.1后本地缓存异常:页面重载需重启服务器
Rails 6.1 本地缓存偶发异常:Location can't be referred to
生产环境基于MemCachier实现Rails缓存,将Rails版本从5升级至6.1后,本地环境出现偶发异常:重载使用缓存的页面时,偶尔会触发ActionView::Template::Error (Location can't be referred to),必须重启服务器才能恢复,且该问题并非每次刷新都会出现。
相关代码片段
局部视图 _locations_data.html.erb
<% cache(cache_view_key, skip_digest: true, :expires_in => location.cached_start_date) do %> <% end %>
Location模型代码
def cached_start_date cached_location_start_date # 其他使用缓存数据的业务代码 end def cached_location_start_date puts "ID -> #{id}" # 可正常打印 puts "Cache Object -> #{Rails.cache.inspect}" # 可正常打印:Cache Object -> #<ActiveSupport::Cache::MemoryStore entries=11, size=15971, options={:compress=>false}> puts "Cache -> #{Rails.cache.read("location_#{id}_start_date")}" # 此处触发错误:Location can't be referred to Rails.cache .fetch("location_#{id}_start_date") do Location.select(:start_date).find(id) end end
development.rb配置
config.cache_classes = false if Rails.root.join('tmp', 'caching-dev.txt').exist? config.action_controller.perform_caching = true config.action_controller.enable_fragment_cache_logging = true config.cache_store = :memory_store config.public_file_server.headers = { 'Cache-Control' => "public, max-age=#{1.days.to_i}" } else config.action_controller.perform_caching = false config.cache_store = :memory_store end
问题分析与解决办法
核心原因:开发环境类重载导致缓存实例失效
开发环境中config.cache_classes = false,Rails会在代码变更时自动重新加载类文件。当缓存中存储的是Location模型实例时,类重载后旧实例对应的类定义已失效,此时尝试访问缓存中的旧实例就会触发"can't be referred to"错误。针对性解决措施
- 缓存原始数据而非模型实例:不要直接缓存查询返回的模型对象,提取需要的字段值缓存,避免类重载带来的实例失效问题:
Rails.cache.fetch("location_#{id}_start_date") do Location.select(:start_date).find(id).start_date end - 序列化缓存对象:将模型实例序列化为JSON字符串存储,读取时再反序列化,隔绝类重载的影响:
# 存储时 Rails.cache.fetch("location_#{id}_start_date") do Location.select(:start_date).find(id).to_json end # 读取时 JSON.parse(Rails.cache.read("location_#{id}_start_date"))["start_date"] - 类重载时清理缓存:通过Rails的
to_prepare回调,在类重新加载时自动清理相关缓存,避免旧实例残留:# 在config/initializers/cache_cleanup.rb中添加 Rails.application.config.to_prepare do Rails.cache.delete_matched("location_*_start_date") end
- 缓存原始数据而非模型实例:不要直接缓存查询返回的模型对象,提取需要的字段值缓存,避免类重载带来的实例失效问题:
内容的提问来源于stack exchange,提问作者Developer
相关产品推荐
相关产品推荐

