🦓

[Rails]キャッシュストアをRedisにする

2023/09/18に公開

はじめに

Railsのキャッシュストアをmemory_storeからredisにしてみました。

環境

Rails 7.0.7
ruby 3.2.1

gemをインストールする

インストールして無ければインストールします。

Gemfile
gem 'redis'
gem 'hiredis'

https://github.com/redis/redis-rb

キャッシュを有効にする

config/environments/*.rb
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/environments/*.rb
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の設定を追加する

docker-compose.yml
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として設定しています。

https://hub.docker.com/_/redis

ファイルを保存したら次のコマンドでコンテナの起動とビルドを行います。

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を活用してデータのキャッシュとアクセスを実現できます。

https://railsguides.jp/caching_with_rails.html#activesupport-cache-rediscachestore

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のソースコード:
https://github.com/rails/rails/blob/92d204e7ac0275ec6b566559006fe64fa4319259/activesupport/lib/active_support/cache/redis_cache_store.rb

https://api.rubyonrails.org/classes/ActiveSupport/Cache/RedisCacheStore.html

Rails.cache.fetchを使ってみる

Rails.cache.fetchは、指定したキーに対するキャッシュデータを取得するためのメソッドです。

app/controllers/todos_controller.rb
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を導入したいので引き続き勉強していきたいと思います。

参考した記事

https://morizyun.github.io/ruby/rails-function-cache-fetch-write-delete.html
https://qiita.com/yamashun/items/bf9a3d29de749cf18f2e

Discussion