You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

响应体含错误信息时Rails cache fetch语法的条件缓存方案咨询

实现方案

Rails 原生Rails.cache.fetch没有直接提供传入lambda控制是否写入缓存的配置项,但可以用框架原生支持的控制流实现完全符合需求的逻辑,不需要冗余的缓存删除操作,也不会丢失错误响应。

最简原生写法(无额外封装)

Rails 缓存组件内部原生支持:unchanged控制流:只要在fetch的块内抛出:unchanged,框架会直接返回块内计算得到的结果,完全跳过缓存写入步骤,没有额外性能损耗,也不属于hack写法。
对应业务场景实现代码:

def search(param1)
  Rails.cache.fetch("key", namespace: "example", expires_in: 1.hour) do
    conn = faraday_helper(url: "search/url")
    response = conn.post do |req|
      req.body = { key: param1 }
    end

    # 请求失败时跳过缓存写入,直接返回错误响应
    throw :unchanged unless response.success?
    response
  end
end

这个写法和原有长写法逻辑完全等价:

  • 缓存命中时直接返回缓存值
  • 缓存未命中时执行块内请求逻辑
  • 请求成功才写入缓存
  • 请求失败直接返回错误响应,不操作缓存

可复用封装写法

如果项目里有多处需要条件缓存的场景,可以封一层薄的工具方法,支持直接传入判断条件,和原生fetch使用体验一致:

# 放到config/initializers/cache.rb中全局加载
def conditional_fetch(key, **options, &block)
  write_checker = options.delete(:if) || ->(_res) { true }
  Rails.cache.fetch(key, **options) do
    result = yield
    throw :unchanged unless write_checker.call(result)
    result
  end
end

业务侧调用时直接传入缓存写入条件即可:

def search(param1)
  conditional_fetch(
    "key",
    namespace: "example",
    expires_in: 1.hour,
    if: ->(response) { response.success? }
  ) do
    conn = faraday_helper(url: "search/url")
    conn.post do |req|
      req.body = { key: param1 }
    end
  end
end

不推荐的方案说明

不建议采用「先写入错误缓存再删除」的实现,原因如下:

  • 多了两次无意义的缓存IO操作,存在不必要的性能损耗
  • 高并发场景下,错误缓存写入后、删除前的时间窗口内,可能被其他请求读到,引发业务异常
  • 完全违背缓存的读写逻辑设计,没有采用价值

内容的提问来源于stack exchange,提问作者James B

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 02:18:35