RedisCacheStore が redis-client に再実装され、DeprecatedRedisCacheStore が追加
RedisCacheStore の実装が redis-client に置き換えられ、旧実装は DeprecatedRedisCacheStore として分離されました。これにより依存が軽量化されつつ、既存の :redis オプションは段階的に廃止されます。
背景
Rails のキャッシュ機構で使用されてきた ActiveSupport::Cache::RedisCacheStore は、長年 redis gem と Redis::Distributed に依存していました。Redis::Distributed が実装上のブロッカーとなり、モダンな RESP3 への移行や依存サイズの削減が難しかったのです。そこで、軽量で RESP2/3 をサポートする redis-client への全面的な置き換えが計画されました。結果として、キャッシュの挙動を変えずに依存関係の刷新が実現されました。
技術的な変更
Gemfile に redis-client >= 0.28.0 が追加され、Gemfile.lock でもバージョンが更新されています。依存が redis (>= 4.0.1) から redis-client (>= 0.28.0) に変わり、インストールサイズが大幅に削減されました。
@@ -70,6 +70,7 @@ end
# Active Support
gem "dalli"
+gem "redis-client", ">= 0.28.0"
gem "listen", "~> 3.3", require: false
ActiveSupport::Cache モジュールに DeprecatedRedisCacheStore が autoload され、旧実装への参照が明示的に分離されました。これにより :redis オプションが渡された際に自動的に旧ストアへフォールバックする仕組みが実装されています。
module Cache
autoload :MemCacheStore, "active_support/cache/mem_cache_store"
autoload :NullStore, "active_support/cache/null_store"
autoload :RedisCacheStore, "active_support/cache/redis_cache_store"
+ autoload :DeprecatedRedisCacheStore, "active_support/cache/deprecated_redis_cache_store"
# These options mean something to all cache implementations. Individual cache
# implementations may support additional options.
end
redis_cache_store.rb の実装が大幅に書き換えられ、redis-client が直接使用されるようになりました。gem "redis" の宣言は削除され、代わりに gem "redis-client" を要求し、require "redis-client" を読み込みます。また RedisClient.ring が導入され、複数ノード構成の扱いが簡潔に実装されています。
redis_client_min_version = "0.28.0"
begin
gem "redis-client", ">= #{redis_client_min_version}"
rescue LoadError
raise LoadError, <<~MSG
The Redis cache store requires the redis-client gem version #{redis_client_min_version} or later.
Please add it to your Gemfile:
gem "redis-client", ">= #{redis_client_min_version}"
MSG
end
require "redis-client"
RedisCacheStore の new メソッドは :redis オプションが渡された場合に DeprecatedRedisCacheStore へ委譲し、同時にデプリケーション警告を出します。これにより既存アプリは動作を維持しつつ、将来的に :url または :client オプションへの移行が促されます。
def self.new(**options)
if options[:redis]
ActiveSupport.deprecator.warn(<<~MSG.squish)
Passing a Redis or ConnectionPool instance via the `:redis` configuration to ActiveSupport::Cache::RedisCacheStore
is deprecated and will be removed in Rails 8.3.
MSG
return DeprecatedRedisCacheStore.new(**options)
end
# ...新しい実装続く...
end
機能面では GETDEL コマンドへの対応が追加され、delete: true オプションで読み取りと同時削除が可能になりました。また read_multi / write_multi が redis-client の mget / mset にマッピングされ、バッチ処理のオーバーヘッドが低減されます。
# read_serialized_entry (抜粋)
if options[:delete]
value = client.call("GETDEL", namespaced_key)
else
value = client.call("GET", namespaced_key)
end
テストスイートも更新され、RedisClient を用いた接続チェックや新ストアの挙動検証が追加されています。旧ストア向けのテストは DeprecatedRedisCacheStoreTests として分離され、両方の互換性が CI で保証されます。
redis_up = begin
RedisClient.new(url: REDIS_URL, protocol: 2).call("ping")
true
rescue RedisClient::ConnectionError
false
end
設計判断
Rails は互換性を保ちつつ依存を軽量化するため、既存の :redis オプションを段階的に廃止し、代替として DeprecatedRedisCacheStore を導入しました。redis-client の ring 機能を活用することで分散構成をシンプルに扱えるようになり、追加の gem (redis) を削除できました。さらに、設定インターフェースは :url または :client に統一され、将来の機能追加やプロトコル拡張が容易になる設計です。デプリケーション警告により利用者は安全に移行でき、既存コードは即座に破壊されません。
まとめ
本 PR により RedisCacheStore が redis-client に完全移行し、依存が軽量化されました。旧実装は DeprecatedRedisCacheStore として残り、:redis オプションは非推奨として段階的に削除されます。開発者は :url や :client を用いて新しいストアを利用でき、機能面でも GETDEL などの最新コマンドがサポートされます。