响应体含错误信息时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
相关产品推荐
相关产品推荐

