今更見つけたredis_storeからredis_cache_storeに移行するときの注意点

Railsのcache機構では過去ビルトインのredisアダプタがなく、gem 'redis-activesupportによって使えるようになるredis_storeが使われていた。

config.cache_store = :redis_store, { url: 'redis://localhost:6379/' }

しかし、Rails 5.2からビルトインのredis_cache_storeが導入された。

config.cache_store = :redis_cache_store, { url: 'redis://localhost:6379/' }

redis_storeredis_cache_storeの間で今更気づいた違いがあったのでメモ。 この記事の実行環境は以下です。

> ruby -v
ruby 3.3.5 (2024-09-03 revision ef084cc8f4) [arm64-darwin23]
> bin/rails c
Loading development environment (Rails 7.2.1)
rails-redis-cache-sandbox(dev)> Redis::VERSION
=> "5.3.0"
> docker compose exec redis redis-server --version
Redis server v=7.4.0 sha=00000000:0 malloc=jemalloc-5.3.0 bits=64 build=86eb6afc7b1d3b21
> cat Gemfile.lock | grep 'redis-activesupport'
    redis-activesupport (5.3.0)

nxオプションとpxオプション

redis_storeではnxオプションが使えるが、redis_cache_storeでは使えない。

rails-redis-cache-sandbox(dev)> Rails.cache.class
=> ActiveSupport::Cache::RedisStore
rails-redis-cache-sandbox(dev)> Rails.cache.write("foo", true, nx: true, raw: true)
=> true
✗ docker compose exec redis redis-cli monitor
OK
1727966899.922581 [0 192.168.65.1:44932] "set" "foo" "true" "NX"

redis_cache_storeではnxオプションを渡しても無視される。

rails-redis-cache-sandbox(dev)> Rails.cache.class
=> ActiveSupport::Cache::RedisCacheStore
rails-redis-cache-sandbox(dev)> Rails.cache.write("foo", true, nx: true, raw: true)
=> true
1727967153.561036 [0 192.168.65.1:65366] "set" "foo" "true"

redis_cache_storeunless_existオプションを使うとnxオプションと同じ挙動をする。

rails-redis-cache-sandbox(dev)> Rails.cache.write("foo", true, unless_exist: true, raw: true)
=> false
1727967324.692603 [0 192.168.65.1:65366] "set" "foo" "true" "NX"

redis_storeはそのままredis-rbsetメソッドにオプションを渡しているのに対して、redis_cache_storeActiveSupport::Cache::RedisCacheStoreはオプションをそのまま渡したりはしていない模様。 https://github.com/rails/rails/blob/a11f0a63673d274c59c69c2688c63ba303b86193/activesupport/lib/active_support/cache/redis_cache_store.rb#L358-L379

redis_storeからredis_cache_storeに移行する際には、nxオプションを使っている箇所をunless_existオプションに変更する必要がある。そのまま放置していても特にエラーが発生するわけではないので気づきにくいかもしれない。

同様にredis_storeではexオプションを渡せばpxオプションを使うことができるが、 redis_cache_storeexを渡しても無視されるので、こちらも移行時に気をつける必要がある。

rails-redis-cache-sandbox(dev)> Rails.cache.class
=> ActiveSupport::Cache::RedisStore
rails-redis-cache-sandbox(dev)> Rails.cache.write("foo", true, ex: 10, raw: true)
=> "OK"
1727968022.718050 [0 192.168.65.1:28501] "set" "foo" "true" "EX" "10"
rails-redis-cache-sandbox(dev)> Rails.cache.class
=> ActiveSupport::Cache::RedisCacheStore
rails-redis-cache-sandbox(dev)> Rails.cache.write("foo", true, ex: 10, raw: true)
1727968250.025982 [0 192.168.65.1:51335] "set" "foo" "true"

redis_cache_storeではexpires_inオプションを使ってTTLを設定する。

rails-redis-cache-sandbox(dev)> Rails.cache.class
=> ActiveSupport::Cache::RedisCacheStore
rails-redis-cache-sandbox(dev)> Rails.cache.write("foo", true, expires_in: 10.seconds, raw: true)
=> true
1727967808.851687 [0 192.168.65.1:44604] "set" "foo" "true" "PX" "10000"

set + nx, px と setnxとsetex

unless_existexpires_inオプションはredis_storeでも使える。

rails-redis-cache-sandbox(dev)> Rails.cache.class
=> ActiveSupport::Cache::RedisStore
rails-redis-cache-sandbox(dev)> Rails.cache.write("foo", true, unless_exist: true, raw: true)
=> false
1727968427.193977 [0 192.168.65.1:51686] "setnx" "foo" "true"
rails-redis-cache-sandbox(dev)> Rails.cache.write("foo", true, expires_in: 10.seconds, raw: true)
=> "OK"
1727968788.970097 [0 192.168.65.1:51686] "setex" "foo" "10" "true"

redis_cache_storeではsetコマンドにnxpxオプションを渡しているのに対して、redis_storeではsetnxsetexコマンドを使っている。

redis的にはset + pxオプションを使ってほしい模様。

Note: Since the SET command options can replace SETNX, SETEX, PSETEX, GETSET, it is possible that in future versions of Redis these commands will be deprecated and finally removed. https://redis.io/docs/latest/commands/set/

もしredis_storeを使っている場合は、setnxsetexという非推奨なコマンドを使っているという意識を持っておくと良さそう。