Rails.cache.increment 时常会被我们用来做一些统计追加动作,也会时常用在一些需要做频率限制的场景。
例如:
再比如最近 Ruby China 添加的每小时发帖量限制 Topic::RateLimit
我们一般会有这样的用法:
# 期望这个锁定 1 小时有效
Rails.cache.increment(_rate_limit_hour_key, 1, expires_in: 1.hour)
if Rails.cache.read(_rate_limit_hour_key) > 10
...
end
但你可能根本不会注意到这里 Rails.cache.increment
的 expires_in
是不支持的(Rails 5.2 对 :mem_cache_store
支持了 rails/rails@b22ee64),也就是说你每次传递的 expires_in
参数基本被忽略了。
irb> Rails.cache.increment("foo", 1, expires_in: 100)
1
irb> Rails.cache.data.ttl("foo")
=> -2 # 根本没设置进去
上面的场景会一直处于锁定状态,不会过期。
:redis_store
的方式,我已经提交 PR 支持了:rails/rails#33254,但在使用的时候仍然需要注意这个陷阱。这个改动估计要到 Rails 6 才会带出来。
实际上这个也不算是个 Bug,目前 Redis 的 incrby, decrby 并没有 expire
参数的支持,所以一直没实现。但大家的使用的时候会条件反射的把参数当成 write
函数的参数来用。