Rails7.0系で、キャッシュストアとセッションストアをRedisにしてみた

以前、Railsのキャッシュストアとセッションストアとして memcached を使ったことがありました。
Rails6.1で、セッションをキャッシュとは別のmemcachedへ保存する - メモ的な思考的な

そんな中、Redisを使って試してみたことから、メモを残します。

 
目次

 

環境

  • Windows11上のWSL2
  • Rails 7.0.4.3
    • 今回は development モードでのみ動作を確認
  • Redis 7.0.10
  • gem

 
今回はminimalなアプリを新規に作り、動作を確認してみます。

$ bundle exec rails new rails_with_redis --minimal --skip-bundle
$ cd rails_with_redis/
$ bundle install

 

Redisを使わない時の挙動を確認

まずはRedisを使わない時の挙動を確認してみます。

 

キャッシュストアの挙動について

設定ファイル config/environments/development.rb を確認したところ、デフォルトでは以下の設定になっていました。

tmp/caching-dev.txt ファイルが存在すれば MemoryStore を、存在しなければ NullStore を使うようです。
2 キャッシュストア | Rails のキャッシュ機構 - Railsガイド

if Rails.root.join("tmp/caching-dev.txt").exist?
  config.action_controller.perform_caching = true
  config.action_controller.enable_fragment_cache_logging = true

  config.cache_store = :memory_store
  config.public_file_server.headers = {
    "Cache-Control" => "public, max-age=#{2.days.to_i}"
  }
else
  config.action_controller.perform_caching = false

  config.cache_store = :null_store
end

 
今回キャッシュの挙動を確認したいことから、該当のファイルを作成しておきます。

$ touch tmp/caching-dev.txt

 
ここで、先ほど見た通り、 caching-dev.txt が存在した場合に使われるキャッシュストアは MemoryStore でした。

検証する上で、キャッシュをしているかどうか判断しやすいよう、できればファイルとして見えるようにしたいと考えました。

調べてみたところ、Railsガイドに以下の記載がありました。

config.cache_storeを明示的に指定しない場合は、デフォルトのキャッシュストア実装("#{root}/tmp/cache/")が提供されます。

[2.4 ActiveSupport::Cache::FileStore | Rails のキャッシュ機構 - Railsガイド](https://railsguides.jp/caching_with_rails.html#activesupport-cache-filestore)

 
そこで、 config.cache_store の設定を削除し、デフォルトのキャッシュストア実装を使うようにします。

# コメントアウトする
# config.cache_store = :memory_store

 
キャッシュストアの準備ができたので、次は実際にキャッシュするアプリを準備します。

今回は home_controller.rb を作成し、ビューでフラグメントキャッシュを使うようにします。
1.3 フラグメントキャッシュ | Rails のキャッシュ機構 - Railsガイド

views/home/index.html.erb

<% cache 'my_cache' do %>
  <h1>Home#index</h1>
<% end %>

 
以上で準備が終わりました。

次に動作確認を行います。

Railsを起動し、http://localhost:3000/home へアクセスします。

すると、 tmp/cache ディレクトリの中に、 453/C71/views%2Fhome%2Findex%3Ada7ba5741191c5f328ad4921a2e99ac1%2Fmy_cache というファイルができました。

 

セッションストアの挙動について

続いてセッションストアの挙動を確認します。

Railsガイドによると、デフォルトのストアは CookieStore でした。

CookieStoreは、Railsで推奨されているデフォルトのセッションストアであり、例外的にすべてのセッションデータをcookie自身に保存します(必要に応じてセッションIDも利用可能です)。

5 セッション | Action Controller の概要 - Railsガイド

 
では、Cookieにセッションが保存されるかを確認してみます。

まずは準備として、コントローラでセッションへデータを保存するよう実装します。

class HomeController < ApplicationController
  def index
    session[:foo] = 'bar'
  end
end

 
Railsを再起動し、アクセスしてみたところ、Cookieにセッションデータ一式が保存されました。

 

セッションストアを ActionDispatch::Session::CacheStore にする

先ほど見た通り、デフォルトではセッションはCookieに保存されました。

そんな中、Railsガイドの

ActionDispatch::Session::CacheStore: データをRailsのキャッシュに保存する

5 セッション | Action Controller の概要 - Railsガイド

という記載が気になりました。

そこで、セッションストアに ActionDispatch::Session::CacheStore を使ってみることにします。

 
セッションストアの切り替えは、config/initializers/session_store.rb で行えば良さそうです。

config.session_store には何を指定すればよいか調べたところ、Railsガイドに記載がありました。

セッションの保存に使うクラスを指定します。指定できる値は:cookie_store(デフォルト)、:mem_cache_store、カスタムストア、または:disabledです。:disabledを指定すると、Railsでセッションが扱われなくなります。

3.2.36 config.session_store | Rails アプリケーションを設定する - Railsガイド

 
今回は CacheStore を使いたいため、おそらくカスタムストアになるだろうと考えました。

カスタムストアの場合には何をシンボルとして渡せばよいかを調べたところ、ソースコードに以下のコメントがありました。

If a custom store is specified as a symbol, it will be resolved to the +ActionDispatch::Session+ namespace:

https://github.com/rails/rails/blob/v7.0.4.3/railties/lib/rails/application/configuration.rb#L370-L383

 
キャッシュストアの名前は ActionDispatch::Session::CacheStore なので、 cache_store というシンボルを渡してみることにしました。

Rails.application.config.session_store :cache_store

 
Railsを再起動してアクセスしたところ、キャッシュと同じストア(ファイルシステム上)に保存されました。

 
以上で、Redisを使わない時の挙動を確認できました。

   

Redisをdocker-composeで導入する

Redisを使ったキャッシュストア・セッションストアを見る前に、Redisをセットアップしておきます。

今回はdocker composeを使ってRedisを導入します。

以下の docker-componse.yml ファイルを作成します。

version: '3'
services:
  redis:
    image: redis:7.0.10
    ports:
      - '6379:6379'

 
準備ができたので、バックグラウンドでRedisを起動しておきます。

$ docker compose up -d

$ docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS         PORTS                                       NAMES
5e84005d5d8d   redis:7.0.10   "docker-entrypoint.s…"   7 seconds ago   Up 7 seconds   0.0.0.0:6379->6379/tcp, :::6379->6379/tcp   rails_with_redis-redis-1

 

Redisを使った時の挙動を確認

Redisの準備ができたので、次はキャッシュストア・セッションストアの切り替えを複数パターンで試してみます。

今回は以下のパターンを見ていくことにします。

  • キャッシュストアだけRedisに切り替える(セッションはCookieStoreのまま)
  • セッションストアだけRedisに切り替える(キャッシュはファイルのまま)
  • キャッシュストア・セッションストアともRedisに切り替える
    • 同じデータベースに置く
    • 別のデータベースに置く

 
なお、動作確認する時のURLは、いずれも http://localhost:3000/home とします。

 

キャッシュストアだけRedisに切り替える(セッションはCookieStoreのまま)

設定

Railsガイドに従い作業します。
2.6 ActiveSupport::Cache::RedisCacheStore | Rails のキャッシュ機構 - Railsガイド

 
まず、Gemfileに redis gemを追加し、 bundle install します。

 
次に、 config/environments/* のファイルを修正します。今回は開発環境にて動作確認するため、 development.rb のみの設定します。

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

 
また、セッションストアはCookieのままにするため、先ほどCacheStoreを使うように設定した config/initializers/session_store.rb は削除します。

他に、キャッシュとセッションがどこに保存されるか分かりやすくするよう、 tmp/cache ディレクトリの中にあったキャッシュ関連のファイルも削除しておきます。

以上で準備ができたので、Railsを再起動しておきます。

 

動作確認

ブラウザでアクセスしたところ、キャッシュはRedisに保存されました。RubyMineで確認すると、以下のように表示されました。

 
一方、セッションはCookieに保存されていました。

 
なお、 tmp/cache の下にはキャッシュ・セッション用のディレクトリなどは作成されませんでした。

 

セッションストアだけRedisに切り替える(キャッシュはファイルのまま)

次に、セッションストアだけをRedisに切り替えます。

Railsガイドを見ましたが、設定方法は見当たりませんでした。

 
そこでWebで調べてみたところ、以下の記事で複数のgemが挙げられていました。
Rails で Redis サーバが落ちた時、慌てないようにする | Basicinc Enjoy Hacking!

 
このうち、Webでは redis-rails をよく見かけます。

そこで redis-rails のREADMEを見たところ

If you need session storage, consider directly using redis-actionpack instead.

https://github.com/redis-store/redis-rails#session-storage

とあったため、今回は redis-actionpack を使うことにします。

 

設定

Gemfileに redis-actionpack を追加します。一方、上記で使っていた redis はいったん削除しておきます。

gem "redis-actionpack"

 
bundle install 後の Gemfile.lock は以下のようになっていました。結局 redis gemを使うようです。

GEM
  remote: https://rubygems.org/
  specs:
    redis-actionpack (5.3.0)
      actionpack (>= 5, < 8)
      redis-rack (>= 2.1.0, < 3)
      redis-store (>= 1.1.0, < 2)
    redis-rack (2.1.4)
      rack (>= 2.0.8, < 3)
      redis-store (>= 1.2, < 2)
    redis-store (1.9.2)
      redis (>= 4, < 6)

 
続いて、redis-actionpackのREADMEに従い、 config/initializers/session_store.rb を追加します。

Rails.application.config.session_store :redis_store,
  servers: %w(redis://localhost:6379/0/session),
  key: '_my_application_session'

 
一方、キャッシュストアの設定 (config/environments/development.rb)は、ファイルで保存するように修正しておきます。

# コメントアウト
# config.cache_store = :redis_cache_store, { url: 'redis://localhost:6379/0' }

 
そして、Redisに保存されていた情報をすべてクリアした後、Railsを再起動します。

 

現時点ではgem redis 5系ではredis-actionpackが動かないため対応

しかし、 redis 5.0.6redis-actionpack 5.3.0 を使用している環境で http://localhost:3000/home にアクセスすると、以下のエラーになります。

ArgumentError (unknown keywords: :scheme, :namespace):

redis-client (0.14.1) lib/redis_client/config.rb:21:in 'initialize'
redis-client (0.14.1) lib/redis_client/config.rb:184:in 'initialize'
redis-client (0.14.1) lib/redis_client.rb:143:in 'new'
redis-client (0.14.1) lib/redis_client.rb:143:in 'config'
redis (5.0.6) lib/redis/client.rb:23:in 'config'

 
似たような事例がないか調べてみたところ、 redis-store にて、エラーへ対応するプルリクがありました。
Redis 5 compatibility fix by PikachuEXE · Pull Request #359 · redis-store/redis-store

 
ただ、現時点ではまだマージされていないため、ここは redis gemは最新の4系 4.8.1 で試してみることにします。

Gemfileに追加します。

gem "redis", "~> 4.8.1", "< 5"  # 追加
gem "redis-actionpack"

 
bundle installします。Gemfile.lock は以下のようになりました。

redis (4.8.1)
redis-actionpack (5.3.0)
  actionpack (>= 5, < 8)
  redis-rack (>= 2.1.0, < 3)
  redis-store (>= 1.1.0, < 2)
redis-rack (2.1.4)
  rack (>= 2.0.8, < 3)
  redis-store (>= 1.2, < 2)
redis-store (1.9.2)
  redis (>= 4, < 6)

 
Railsを再起動し、あらためて http://localhost:3000/home にアクセスすると、エラーにはなりませんでした。良さそうです。

 

動作確認

ブラウザでアクセスしたところ、キャッシュはファイルシステムに保存されていました。

 
また、セッションはRedisに保存されていました。RubyMineで確認すると、以下のように表示されました。

 
ブラウザのCookieにはセッションIDだけ保存されています。

 

キャッシュストア・セッションストアともRedisに切り替える (同じデータベースに置く)

続いて、キャッシュ・セッションの両ストアともRedisへ切り替えます。

 

設定

ここまでの流れでセッションストアはRedisに切り替わっているため、ここではキャッシュストアをRedisに切り替えるだけになります。

今回は、両方ともRedisのデータベース 0 へと保存するよう設定します。先ほどセッションストアはデータベース 0 にしたため、キャッシュストアもデータベース 0 に設定します。

config/environments/development.rb

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

 
続いて、動作確認を容易にするため、以下の作業を行います。

  • tmp/cache 以下をクリア
  • Redisをクリア

 
最後に、Railsを再起動します。

 

動作確認

ブラウザでアクセスすると、ブラウザにはセッションIDのみ保存されています。

 
Redisには、キャッシュとセッションの2エントリが存在します。

 
なお、Railsのキャッシュディレクトリには何も保存されていませんでした。

 

キャッシュストア・セッションストアともRedisに切り替える (別のデータベースに置く)

設定

今回はセッションストアを 1 のデータベースに切り替えます。

config/initializers/session_store.rb

Rails.application.config.session_store :redis_store,
  servers: %w(redis://localhost:6379/1/session),  # ここを /0/session から切り替えた
  key: '_my_application_session'

 
その後、Railsの再起動とRedisのクリアを行っておきます。

 

動作確認

アクセス後にRedisを確認すると、

  • 0 にキャッシュ
  • 1 にセッション

がそれぞれ保存されていました。

 

ソースコード

Githubに上げました。
https://github.com/thinkAmi-sandbox/rails_with_redis-sample

プルリクはこちら。今回追っていった段階ごとにコミットが分かれています。
https://github.com/thinkAmi-sandbox/rails_with_redis-sample/pull/1