以前、Railsのキャッシュストアとセッションストアとして memcached
を使ったことがありました。
Rails6.1で、セッションをキャッシュとは別のmemcachedへ保存する - メモ的な思考的な
そんな中、Redisを使って試してみたことから、メモを残します。
目次
環境
- Windows11上のWSL2
- Ubuntu 22.04.2 LTS
- Rails 7.0.4.3
- 今回は
development
モードでのみ動作を確認
- 今回は
- Redis 7.0.10
- gem
- redis 4.8.1
- redis-actionpack 5.3.0
今回は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も利用可能です)。
では、Cookieにセッションが保存されるかを確認してみます。
まずは準備として、コントローラでセッションへデータを保存するよう実装します。
class HomeController < ApplicationController def index session[:foo] = 'bar' end end
Railsを再起動し、アクセスしてみたところ、Cookieにセッションデータ一式が保存されました。
セッションストアを ActionDispatch::Session::CacheStore にする
先ほど見た通り、デフォルトではセッションはCookieに保存されました。
そんな中、Railsガイドの
ActionDispatch::Session::CacheStore: データを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:
キャッシュストアの名前は 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!
- redis-store/redis-rails: Redis stores for Ruby on Rails
- redis-store/redis-actionpack: Redis stores for ActionPack
- roidrage/redis-session-store: A simple session store for Rails based on Redis.
このうち、Webでは redis-rails
をよく見かけます。
そこで redis-rails
のREADMEを見たところ
If you need session storage, consider directly using redis-actionpack instead.
とあったため、今回は 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.6
と redis-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