以下の内容はhttps://thinkami.hatenablog.com/entry/2026/02/01/113738より取得しました。


Rails8.1系で、Redisクラスターをセッションストアとキャッシュストアに設定してみた

以前の記事で、RailsでキャッシュストアとセッションストアをRedisにしてみました。
Rails7.0系で、キャッシュストアとセッションストアをRedisにしてみた - メモ的な思考的な

その時はスタンドアロンRedisだったため、Redisクラスターの場合はどうなるのか気になったことから、試してみたときのメモを残します。

 
目次

 

環境

  • mac
  • RubyMine 2025.3.2
  • Redisクラスタ
    • docker composeで利用
    • Redis 8.4のノードを3つ用意
  • Ruby 3.4.8
  • Rails 8.1.2
  • redis 5.4.1
  • redis-clustering 5.4.1

 
なお、Redisクラスターは前回の記事の内容そのままです。事前に起動しておきます。
Rails8.1系で、Redisクラスターをセッションストアとして設定してみた - メモ的な思考的な

 

Railsアプリの環境構築

Railsアプリも前回記事とほぼ同じですので、差分だけ記載します。

今回、コントローラでキャッシュストアとセッションストアに読み書きすることで、動作確認できるようにしました。

キャッシュストアを読み書きするには、 Rails.cache.readRails.cache.writeを使います。
5.2 ActiveSupport::Cache::Store | Rails のキャッシュ機構 - Railsガイド

その際、キャッシュやセッションの有効期限を確認しやすくするため、各ストアに入れる値はタイムスタンプとします。

class HellosController < ApplicationController
  skip_before_action :verify_authenticity_token, raise: false
  def index
    session_timestamp = session[:session_timestamp]
    cache_timestamp = Rails.cache.read("cache_timestamp")

    puts session_timestamp
    puts cache_timestamp

    render json: {
      current_timestamp: Time.current.strftime("%Y/%m/%d %H:%M:%S"),
      session_timestamp:,
      cache_timestamp:
    }
  end

  def create
    now = Time.current.strftime("%Y/%m/%d %H:%M:%S")
    session[:session_timestamp] = "session -> #{now}"
    Rails.cache.write("cache_timestamp", "cache -> #{now}")

    head :ok
  end
end

 

キャッシュストアをRedisクラスターにする

キャッシュストアを設定する場合、config.cache_store を変更します。
3.2.14 config.cache_store | Rails アプリケーションの設定項目 - Railsガイド

開発環境である config/environments/development.rb では :memory_store が設定されていました。

 
今回はRedisクラスターを使うため、 :redis_cache_store へと変更します。
5.6 ActiveSupport::Cache::RedisCacheStore | Rails のキャッシュ機構 - Railsガイド

それに加え、Redisクラスターの設定も追加します。今回は動作確認しやすいよう、 expires_in は1分としました。

Rails.application.configure do
  # ...
  config.cache_store = :redis_cache_store, {
    redis: Redis::Cluster.new(nodes: %w[
      redis://redis-cluster-node-1:6379
      redis://redis-cluster-node-2:6379
      redis://redis-cluster-node-3:6379
    ]),
    namespace: "app_cache",
    expires_in: 1.minutes,
  }
  # ...

 
準備ができたので、Railsを起動した後、 curl で動作確認します。以下、見やすくするために抜粋・整形しています。

まずはPOSTでデータをキャッシュストア・セッションストアにデータを保存します。

% curl -X POST http://localhost:3000/hellos -c cookies.txt

 
続いて、GETで各ストアからデータを取り出します。この時点では両ストアともデータがあります。

% curl -b cookies.txt http://localhost:3000/hellos        
{
"current_timestamp":"2026/01/31 12:20:48",
"session_timestamp":"session -> 2026/01/31 12:20:45",
"cache_timestamp":"cache -> 2026/01/31 12:20:45"
}

 
この時、Redisクラスターの状況を確認します。 namespaceの app_cache + キャッシュへ入れたときのキー cache_timestamp に紐づくデータが保存されています。

 
1分以上経過した後、再度GETします。すると、キャッシュストアのみデータが消えていました。

% curl -b cookies.txt http://localhost:3000/hellos
{
"current_timestamp":"2026/01/31 12:22:01",
"session_timestamp":"session -> 2026/01/31 12:20:45",
"cache_timestamp":null
}

 

キャッシュストアとセッションストアをRedisクラスターにする

続いて、セッションストアをデフォルトのCookieからRedisクラスターへと変更します。
3.2.58 config.session_store | Rails アプリケーションの設定項目 - Railsガイド

Redisクラスターのセッションストアでは次の2パターンがありそうだったため、それぞれ試してみます。

  • :cache_store のみ指定して、キャッシュストアと同居する
  • :cache_store に加え namespace も設定し、キャッシュストアと分ける

 

:cache_storeのみ指定する

セッションストアとキャッシュストアを同居させるパターンです。

実運用では発生しづらいと思いますが、どのような挙動になるか気になったため試してみます。

config/initializers/session_store.rbを作成し、次の設定を追加します。

Rails.application.config.session_store :cache_store

 
準備ができたので、Railsを再起動して、 curl で動作確認します。

すると、キャッシュストアの有効期限を経過すると、キャッシュストアとセッションストアが同時にクリアされました。

% curl -X POST http://localhost:3000/hellos -c cookies.txt

# キャッシュストアの有効期限内
% curl -b cookies.txt http://localhost:3000/hellos        
{
"current_timestamp":"2026/01/31 12:23:52",
"session_timestamp":"session -> 2026/01/31 12:23:49",
"cache_timestamp":"cache -> 2026/01/31 12:23:49"
}

# キャッシュストアの有効期限を経過後
% curl -b cookies.txt http://localhost:3000/hellos
{
"current_timestamp":"2026/01/31 12:25:25",
"session_timestamp":null,
"cache_timestamp":null
}

 
ちなみに、キャッシュストアの有効期限内のRedisクラスターの状況です。同じnamespaceの app_cache で、キーだけ異なってデータが保存されていました。

キャッシュとして登録されたデータ

 
セッションとして登録されたデータ(セッションIDがキーに含まれる)

 

:cache_storeに加え、namespaceも指定する

続いて、 :cache_store + namespace を指定する設定です。

キャッシュストアとセッションストアの有効期限を別とするため、 expire_after に90分を設定します。これにより、セッションストアの expire_after 設定の影響を受けず、キャッシュストアは expires_in 設定に従うことを確認できます。

なお、この設定の詳細は前回の記事を参照してください。

# namespaceを付与するため、cache引数をあらためて定義する
Rails.application.config.session_store :cache_store,
                                       cache: ActiveSupport::Cache::RedisCacheStore.new(
                                         redis: Redis::Cluster.new(nodes: %w[
                                           redis://redis-cluster-node-1:6379
                                           redis://redis-cluster-node-2:6379
                                           redis://redis-cluster-node-3:6379
                                         ]),
                                         namespace: "session_redis5"
                                       ),
                                       expire_after: 90.minutes,
                                       key: "_redis_cluster_example_session"

 
準備ができたので、Railsを再起動して、 curl で動作確認します。

すると、キャッシュストアの有効期限を経過すると、セッションストアのみデータが保存されていることがわかりました。良さそうです。

% curl -X POST http://localhost:3000/hellos -c cookies.txt

# キャッシュストア、セッションストアとも有効期限内
% curl -b cookies.txt http://localhost:3000/hellos        
{
"current_timestamp":"2026/01/31 13:21:14",
"session_timestamp":"session -> 2026/01/31 13:21:09",
"cache_timestamp":"cache -> 2026/01/31 13:21:09"
}

# キャッシュストアが有効期限を経過
% curl -b cookies.txt http://localhost:3000/hellos
{
"current_timestamp":"2026/01/31 13:23:02",
"session_timestamp":"session -> 2026/01/31 13:21:09",
"cache_timestamp":null
}

 
ちなみに、キャッシュストア・セッションストアの有効期限内のRedisクラスターの状況です。別namespaceでデータが保存されていました。

キャッシュとして登録されたデータ

 
セッションとして登録されたデータ(namespaceが session_redis5

 

備考

RedisクラスターのDB番号と、redis + redis-clusteringでの挙動について

Redisの公式ドキュメントに

Redis Cluster does not support multiple databases like the standalone version of Redis. We only support database 0; the SELECT command is not allowed.

Implemented subset | Redis cluster specification | Docs

とあるように、Redisクラスターでは、スタンドアロンRedisのような redis://redis-cluster-node-1:6379/1 というDB番号1の指定ができない仕様です。

 
では、gem redis + redis-clustering のセッションストアの場合、どのような挙動になるか試してみます。

 

DB番号0を指定した場合、セッションへ書き込める

session_store.rb の設定で、末尾に /0 を付与してみます。

Rails.application.config.session_store :cache_store,
                                       cache: ActiveSupport::Cache::RedisCacheStore.new(
                                         redis: Redis::Cluster.new(nodes: %w[
                                           redis://redis-cluster-node-1:6379/0
                                           redis://redis-cluster-node-2:6379/0
                                           redis://redis-cluster-node-3:6379/0
                                         ]),
                                         namespace: "session_redis5"
                                       ),
# ...

 
curlでアクセスしてRedisの状態を確認したところ、セッションとして値が保存されていました。DB番号 0 のため、仕様通りそうです。

 

DB番号1を指定した場合、セッションへ書き込めないが、正常終了する

続いて、session_store.rb の設定で、末尾に /1 を付与してみます。

Rails.application.config.session_store :cache_store,
                                       cache: ActiveSupport::Cache::RedisCacheStore.new(
                                         redis: Redis::Cluster.new(nodes: %w[
                                           redis://redis-cluster-node-1:6379/1
                                           redis://redis-cluster-node-2:6379/1
                                           redis://redis-cluster-node-3:6379/1
                                         ]),
                                         namespace: "session_redis5"
                                       ),
# ...

 
同じくcurlでアクセスして確認したところ、エラーにならず正常終了しました。

Redisクラスターを確認すると、キャッシュとしての値はあるものの、セッションとしての値が保存されていません。

 
これより、gem redis + redis-clustering でRedisクラスターを使う場合、DB番号 0 以外は使えないと分かりました。

ただ、ここまで見てきた通り、DB番号 0 を指定しなくてもRedisクラスターへの接続はできることから、わざわざDB番号を付与する必要はなさそうと感じました。

 

Redis::Cluster.newについて

session_store.rb の設定では、 ActiveSupport::Cache::RedisCacheStore.newredis キーワードに Redis::Cluster.new の結果を渡していました。

Redis::Cluster.newはどのgemで定義され、どのような値が返るのか気になったことから、調べてみました。

 
まず、Redis::Clusterは redis-clustering gemで定義され、 redis gemの Redis クラスを継承しています。
https://github.com/redis/redis-rb/blob/v5.4.1/cluster/lib/redis/cluster.rb#L6

また、 new で呼ばれる initialize では super して親クラスを呼んでいます。
https://github.com/redis/redis-rb/blob/v5.4.1/cluster/lib/redis/cluster.rb#L67-L70

require "redis"

class Redis
  class Cluster < ::Redis
    #  @return [Redis::Cluster] a new client instance
    def initialize(*)
      super
    end
    # ...  

 
その親クラス Redisでは、 initialize の中で initialize_client が呼ばれます。
https://github.com/redis/redis-rb/blob/v5.4.1/lib/redis.rb#L75

class Redis
  # @return [Redis] a new client instance
  def initialize(options = {})
    # ...
    @client = initialize_client(@options)
    # ...
  end

 
Redis::Cluster では initialize_client が呼ばれます。そこでは、 redis-cluster-clientRedisClient#clusterが呼ばれます。
https://github.com/redis/redis-rb/blob/v5.4.1/cluster/lib/redis/cluster.rb#L130-L132

def initialize_client(options)
  cluster_config = RedisClient.cluster(**options, protocol: 2, client_implementation: ::Redis::Cluster::Client)
  cluster_config.new_client
end

 
RedisClient#clusterでは、 ClusterConfig を生成します。
https://github.com/redis-rb/redis-cluster-client/blob/master/lib/redis_cluster_client.rb#L8

class RedisClient
  class << self
    def cluster(**kwargs)
      ClusterConfig.new(**kwargs)
    end
  end
end

 
ClusterConfigでは RedisClient.cluster(**options, ...) で渡された options から nodes が渡され、最終的には client_config インスタンス変数へと設定されます。

また、 #clusterで渡された ::Redis::Cluster::Clientclient_implementation インスタンス変数へと設定されます。
https://github.com/redis-rb/redis-cluster-client/blob/4387eabfd4b0dc96e6c7ebada2d7cb4f52fef0e0/lib/redis_client/cluster_config.rb#L11-L65

class RedisClient
  class ClusterConfig
    def initialize( # rubocop:disable Metrics/ParameterLists
      nodes: DEFAULT_NODES,
      # ...
      client_implementation: ::RedisClient::Cluster, # for redis gem
      # ...
    )
      # ...
      node_configs = build_node_configs(nodes.dup)
      @client_config = merge_generic_config(client_config, node_configs)
      @startup_nodes = build_startup_nodes(node_configs)
      @client_implementation = client_implementation
    end
  
  # ...

 
これより、 Redis::Cluster.new(nodes: ...) で渡したRedisクラスターの情報がRedisクライアントへ引き渡され、 Redis::Clusterインスタンスが生成されると分かりました。

 

ソースコード

GitHubに上げました。
https://github.com/thinkAmi-sandbox/rails_session_and_cache_with_redis_cluster-example

今回のプルリクはこちら。 cache_storesession_storeにRedisクラスターが設定されている状態になっています。
https://github.com/thinkAmi-sandbox/rails_session_and_cache_with_redis_cluster-example/pull/1




以上の内容はhttps://thinkami.hatenablog.com/entry/2026/02/01/113738より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14