[Rails]キャッシュストアをRedisにする
はじめに
Railsのキャッシュストアをmemory_store
からredis
にしてみました。
環境
Rails 7.0.7
ruby 3.2.1
gemをインストールする
インストールして無ければインストールします。
gem 'redis'
gem 'hiredis'
キャッシュを有効にする
config.action_controller.perform_caching = true
キャッシュストアが:memory_store
になってます:
irb(main):001:0> Rails.cache
=> #<ActiveSupport::Cache::MemoryStore entries=0, size=0, options={:compress=>false, :compress_threshold=>1024}>
キャッシュストアをredisにする
config.cache_store = :redis_cache_store, {
url: ENV['REDIS_URL'], # Redisの接続情報を環境変数から取得する
expires_in: 1.hour, # キャッシュの有効期限を設定
driver: :hiredis # hiredisドライバを使用することで高速化できる
}
他のキャッシュストアを使っていた場合キャッシュをクリアしてから実行するのも良いかもしれないです。
bin/rails tmp:clear
キャッシュストアがredis
になっていることを確認します。
irb(main):001:0> Rails.cache
=> #<ActiveSupport::Cache::RedisCacheStore options={:namespace=>nil, :compress=>true, :compress_threshold=>1024, :expires_in=>nil, :race_condition_ttl=>nil} redis={:url=>"redis://localhost:6379/0"}>
Redisサーバーを立ち上げます。
ローカルの場合redis-server
を実行して立ち上げます。
Dockerからたちあげたいのでまずdocker-compose.yml
に記述を追加します。
docker-compose.yml
にRedisの設定を追加する
version: "3.9"
services:
db:
image: postgres
restart: always
volumes:
- ./tmp/db:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD:
TZ: "Asia/Tokyo"
+ redis:
+ image: redis:latest
+ ports:
+ - "6379:6379"
+ volumes:
+ - ./data/redis:/data
web:
build: .
command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
volumes:
- .:/rails_redis
ports:
- "3000:3000"
depends_on:
- db
+ - redis
+ environment:
+ REDIS_URL: redis://redis:6379/0
...
rails_redis/
ディレクトリ内にrails new
をするとします。
Redisの公式イメージを使用してRedisコンテナを起動します。ポート6379をホストの6379にマッピングしてRedisへのアクセスを可能にします。
volume
:カレントディレクトリ(Docker Composeファイルが存在するディレクトリ)の/data
ディレクトリとRedisコンテナ内の /data
ディレクトリを同期させます。
Redisコンテナ内でのデータはホストマシン側の./data/redis
ディレクトリに保存されます。
depends_on
:webサービスが依存するRedisコンテナを指定しています。
environment
: Redisの接続情報を環境変数REDIS_URL
として設定しています。
ファイルを保存したら次のコマンドでコンテナの起動とビルドを行います。
docker-compose build
...
✔ Container rails_redis-redis-1 Created
✔ Container rails_redis-redis-1 Started
✔ Container rails_redis-redis-1 Running
docker-compose up
起動しているコンテナにredisがあることを確認します:
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
...
2850be094019 redis:latest "docker-entrypoint.s…" 14 minutes ago Up 14 minutes 0.0.0.0:6379->6379/tcp rails_redis-redis-1
キャッシュを有効にする
docker-compose run web rails dev:cache
[+] Building 0.0s (0/0)
[+] Creating 2/0
✔ Container rails_sample-db-1 Running 0.0s
✔ Container rails_sample-redis-1 Running 0.0s
[+] Building 0.0s (0/0)
Development mode is now being cached.
Redisに入ってみます:
docker exec -it rails_redis-redis-1 /bin/bash
root@2850be094019:/data# redis-cli
127.0.0.1:6379> PING
PONG
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> SET mykey "Hello, Redis!"
OK
127.0.0.1:6379> GET mykey
"Hello, Redis!"
ActiveSupport::Cache::RedisCacheStore
Redisをバックエンドとして使用するためのキャッシュストアモジュールを提供しています。
キャッシュの設定と有効期限、データの取得と保存などRedisを活用してデータのキャッシュとアクセスを実現できます。
RedisCacheStore
デフォルトの挙動を見てみます。
DEFAULT_REDIS_OPTIONS = { connect_timeout: 20, # 接続タイムアウト
read_timeout: 1, # 読み取り接続タイムアウト
write_timeout: 1, # 書き込みタイムアウト
reconnect_attempts: 0, # 再接続試行回数
}
MAX_KEY_BYTESIZE
は、キャッシュのキーの最大バイトサイズを定義しています。キーの最大バイトサイズを1KB(1024バイト)に制限しています。超える場合には、ActiveSupportのダイジェスト(ハッシュ)機能ActiveSupport::Digest
が使用されてキーが切り詰められます。
RedisCacheStore
は、Redisサーバーが利用不可になった場合でも、例外を発生させません。代わりに、キャッシュの取得はすべてミスとして扱われ、書き込みは破棄されます。
RedisCacheStore
のソースコード:
Rails.cache.fetch
を使ってみる
Rails.cache.fetch
は、指定したキーに対するキャッシュデータを取得するためのメソッドです。
class TodosController < ApplicationController
...
def index
@todos = published_todos
end
end
private
def published_todos
Rails.cache.fetch("published_todos", expires_in: 1.hour) do
Todo.includes(:user).order(created_at: :desc).to_a
end
end
end
irb(main):001:0> Rails.cache.fetch("published_todos")
=> # キャッシュされたTodo
"published_todos"
を取得してみます:
127.0.0.1:6379> GET "published_todos"
"\x01x\x9cmU\xddN\x1bG\x14\xe6\xa2Z{m\x83\xe3@\r\x04B\x12\x97\xa4M\x11\xd6\x02!\x90\x19\xa9\x1a\x03Y\xd5Q(M0m\x89/Vc\xef\xc1L\xd9\xddqf\xc6\x10cE\xeaC\xf4
...
# dataを削除する
127.0.0.1:6379> del "published_todos"
(integer) 1
127.0.0.1:6379> get "published_todos"
(nil)
終わりに
RailsにRedisを導入したいので引き続き勉強していきたいと思います。
参考した記事
Discussion