【问题标题】:Rails: how best to re-populate cache when invalidatingRails:失效时如何最好地重新填充缓存
【发布时间】:2012-03-05 16:15:47
【问题描述】:

对于生成成本高昂的页面或操作,标准的 Rails 缓存调用方法会在其基础数据过时时使页面/操作无效。然后,当缓存未命中并生成页面时,下一个请求该页面的用户必须在那里坐 N 秒。如果 N 足够大,这将使该用户感到难过。当然,这只是一个用户,但如果失效的频率足够高,我们可能会招来很多伤心的用户。

一种解决方案是,每当我使某些页面/操作无效时,也会触发页面/操作的新版本的呈现并将它们放入缓存中。构建我需要的版本需要几个小时的编码。

但肯定有人已经解决了这个问题?我环顾四周,找不到任何合适的宝石或答案。

谢谢!

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-3 caching invalidation


    【解决方案1】:

    我假设昂贵的计算不是 HTML 页面的呈现,而是填充页面所需的查询和/或计算。

    正如您所说,您应该在它失效时触发此计算/查询,以便在下次访问该页面时立即可用。

    您可以以多种格式存储数据:YAML、Marshal 转储、JSON、CSV 等。此外,如果您采用这种方式,我会将其存储在文件系统或数据库等永久位置,而不是 Rails缓存,因为 Rails 缓存并不总是包含您的数据。

    【讨论】:

    • 这是一个很有前途的线索。您是否看到任何用于组织查询缓存的好模式/宝石?通过控制器代码随机调用 Rails.cache.fetch() 似乎是通向意大利面条代码和混乱的捷径。
    • 不,我不知道是否有任何宝石或模式,但自己为您的用例找出一个好的模式应该不会太难。只要记住重构,你就会得到一些合理的东西。
    【解决方案2】:

    终于找到一个问答:
    问:"Warm Up Cache" on deployment
    答:https://stackoverflow.com/a/942774/593053

    将我的问题留在这里,以便其他人可以跟踪并学习 从我的痛苦中。

    也相关:Rails 3.2: Pre-render (bake) a new page cache immediately after expiry?

    我的完整解决方案是采用约定,如果cache=regen 在请求的 URL 中,这意味着生成的页面应该被填充到缓存中。

    对于我想要缓存的控制器,include CacheRegenCacheRegen 导致控制器在 cache=regen 时不从缓存中读取,并且在缓存中存储时不将 cache=regen 放入 key。

    代码如下:

    module CacheRegen
      def read_fragment(key, options = nil)
        if /cache=regen/.match(key)
          logger.info("forcing cache miss due to param cache=regen, key=#{key}")
          return nil
        end
        super(key, options)
      end
    
      def write_fragment(key, content, options = nil)
        unless key.sub!(/cache=regen/, '').nil?
          key.sub!(/\?\&/, '?')
          key.sub!(/\&\&/, '&')
          key.sub!(/\?$/, '')
          key.sub!(/\&$/, '')
          logger.info("wrote page to cache with key #{key}")
        end
        super(key, content, options)
      end
    end
    

    最后,我在new_pages.rake中加入如下代码:

    require 'action_dispatch'
    
    def get_url(sess, url)
          uri = "http://YOURSITE.com/" + url + "cache=regen"
          puts "retrieving " + uri
          foo = sess.get(uri)
          puts "got it. #{foo}, #{sess.response.body.length} bytes"
    end
    
    desc "If necessary, generate new versions of the most expensive pages"
    task :new_pages => :environment do
        puts "Updating pages..."
        sess = ActionDispatch::Integration::Session.new(Rails.application)
        ["controller1", "controller2", "controller3"].each { |noun|
          get_url(sess, noun + "?")
        }
    
        puts "done."
    end
    

    在我的环境中,我有一个 deploy 任务,它依赖于 new_pages 任务。

    有没有什么宝石可以让这一切变得更加自动化?

    【讨论】:

      【解决方案3】:

      rails 中缓存失效的典型解决方案是清扫器。这是自动缓存失效的技术。 你可以阅读更多关于扫地机的信息in this guide

      【讨论】:

      • sweepers 简化了失效过程,但似乎无助于重新填充缓存,这就是这个问题的意义所在。
      猜你喜欢
      • 2013-01-27
      • 2011-04-12
      • 2014-04-17
      • 1970-01-01
      • 2010-09-07
      • 1970-01-01
      • 2012-06-01
      • 2019-09-29
      • 1970-01-01
      相关资源
      最近更新 更多