9

我正在使用 Rails.cache 缓存来自 API 的响应:

Rails.cache.fetch("key") do 
  api.get "/api/data"
  api.body
end

API 不是很可靠,有时我会收到 500 错误。我想避免缓存 500 响应。

Rails.cache.fetch("key") do 
  api.get "/api/data"
  if api.body.meta.status == 500
    # Bail out, Goose!
  end
  api.body
end

我宁愿不提出例外。在没有缓存的情况下“拯救”区块的最佳方法是什么?

4

3 回答 3

6

我自己也遇到了这个问题,看起来break可以解决我们的问题。我只是在本地针对 memory_store 和 dalli_store 进行了测试,它避免了缓存块。因此,对于您的示例,请尝试以下操作:

Rails.cache.fetch("key") do 
  api.get "/api/data"
  break if api.body.meta.status == 500
  api.body
end

作为旁注,如果使用 dalli_store 它不会缓存 nil 值,因此您可以从块中返回 nil 。

于 2013-12-14T01:48:46.937 回答
2

在这种情况下,在大多数情况下,我不会使用 fetch。

Fetch 让您使用异常处理作为您唯一的流控制机制(!)来考虑所有流控制案例。

相反,使用缓存的读取和写入方法,使用导致您想要的行为的正常流控制。Fetch 既快速又花哨,但带有额外的包袱,一旦您必须真正支持您编写的软件,您将不会满意。

于 2019-02-14T22:12:33.073 回答
0

这是我的解决方案:

 module CacheExtensions
  class << self
    # This allows caching with an expiration, but if there is an exception thrown from the block that produces
    # the cache value, it uses the old value from the cache, retrying occasionally. The trick is to
    # not put an expiration on the cache value, but on a separate generated key, because Rails.cache.read honors
    # expirations set earlier. I'm surprised Rails.cache.fetch doesn't just handle this.
    #
    # key - the cache key to read and update if needed
    # expiration - how long the key should be good for under normal circumstances
    # retry_expiration - how long between retries if the block raises an exception. if expiration is long, you may want to
    # make this shorter.
    # block - should return the updated value, or raise an exception if it can't be retrieved
    #
    # Inspired by https://github.com/ReliveRadio/reliveradio-website/blob/4874cf4158361c73a693e65643d9e7f11333d9d6/app/helpers/external_api_helper.rb
    def fetch_with_rescue(key, expiration, retry_expiration)
      freshness_key = 'ExpirationFor_' + key
      result = Rails.cache.read(key)
      freshness_result = Rails.cache.read(freshness_key)

      if freshness_result.blank? || result.blank?
        begin
          result = yield
          Rails.cache.write(key, result)
          Rails.cache.write(freshness_key, 'fresh', expires_in: expiration)
        rescue StandardError => error
          Rails.cache.write(freshness_key, 'retrying', expires_in: retry_expiration)
          Rails.logger.error("Got error #{error} attempting to update cache #{key}. Using saved value for #{retry_expiration} additional time.")
        end
      end

      return result
    end
  end
end
于 2016-08-05T19:16:17.497 回答