dockerで立ち上げたRailsのキャッシュストアをRedisに設定する

docker-composeを用いてRailsサーバをAPIとして立ち上げ, 
Reactで書いたフロントからAPIを叩く構成でアプリケーションを開発しています. 

API側の負荷を抑えるため, クエリ結果をキャッシュするためにRedisを導入した時のメモになります. 
Docker, docker-composeを用いた環境構築やRedisの概要には触れていませんのでご了承ください.

# 開発環境
```
$ sw_vers
ProductName:	Mac OS X
ProductVersion:	10.15.1
BuildVersion:	19B88
$ docker -v
Docker version 19.03.1, build 74b1e89
$ docker-compose -v
docker-compose version 1.24.1, build 4667896b

```
# Redis導入前
```Dockerfile:Dockerfile
FROM ruby:2.6.5-slim

# db, js関係の環境をインストール
RUN apt-get update -qq && apt-get install -y mariadb-client libmariadb-dev-compat build-essential apt-transport-https curl imagemagick && \
    curl -sL https://deb.nodesource.com/setup_8.x | bash - && \
    apt-get install -y nodejs && \
    curl -sS http://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
    echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
    apt-get update && apt-get install -y yarn

# Rails (bundle install)
RUN mkdir /myapp
WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install
COPY . /myapp

```

<br />

```yml:docker-compose.yml
version: "3"
services:
  web:
    build: .
    command: /bin/sh -c "rm -f /myapp/tmp/pids/server.pid && bundle exec rails s -p '3001' -b '0.0.0.0'"
    environment:
      - DATABASE=myapp_development
      - DATABASE_USER=root
      - DATABASE_PASSWORD=password
      - DATABASE_HOST=db
    volumes:
      - .:/myapp
    ports:
      - 3001:3001
    depends_on:
      - db

  db:
    image: mysql:5.7
    command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci
    environment:
      - MYSQL_DATABASE=myapp_development
      - MYSQL_ROOT_USER=root
      - MYSQL_ROOT_PASSWORD=password
    volumes:
      - mysql_vol:/var/lib/mysql
    ports:
      - 3306:3306
  
  volumes:
    mysql_vol:
```

<br />


## 
```config/environments/development.rb
Rails.application.configure do
  # 略
  # Enable/disable caching. By default caching is disabled.
  # Run rails dev:cache to toggle caching.
  if Rails.root.join('tmp', 'caching-dev.txt').exist?
    config.action_controller.perform_caching = 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
  # 略
end
```

# Redisの導入
## 1. キャッシュの有効化
先ほど載せたDockerfileを除く2つのファイルを編集する
**...前に, 先にやっておくことがあります!!**  
`config/environments/development.rb` のコメントにあるように, 
Railsのdevelopment環境ではデフォルトでキャッシュが無効になっているのです.
これに気付かず, かなりハマってました...😅

> Enable/disable caching. By default caching is disabled.
> Run rails dev:cache to toggle caching.

ということで, 書いてあるコマンドをそのまま実行します.

```
$ docker-compose run web rails dev:cache
Starting myapp_db_1    ... done
Development mode is now being cached.
```
<br />

## 2. Redisコンテナの作成
`docker-compose`で起動させるコンテナにRedisを追加します.

```diff:docker-compose.yml
version: "3"
services:
  web:
  # 略
    depends_on:
      - db
+     - redis
  
  db:
  # 略
+ redis:
+   image: redis
+   ports:
+     - 6379:6379
+   volumes:
+     - "./app/redis:/data"

volumes:
  mysql_vol:

```

<br />

## 3. キャッシュストアの設定
Gemfileに必要なgemを追加後, ビルドして`bundle install`を走らせます.

```diff:Gemfile
# 略
+ gem 'redis'
+ gem 'redis-rails'
```

```
$ docker-compose build
```
Railsでのキャッシュ先を, 先ほど追加したRedisコンテナに指定します.
後ほどの動作確認のため, 保存期限は短めに設定しておきます.

```diff:config/environments/development.rb
Rails.application.configure do
  # 略
  if Rails.root.join('tmp', 'caching-dev.txt').exist?
    config.action_controller.perform_caching = true
-   config.cache_store = :memory_store
+   config.cache_store = :redis_store, 'redis://redis:6379/0', { expires_in: 1.minute }
    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
  # 略
end
```
設定は以上で完了です 🎉

<br />

## 4. 動作確認
### Redisコンテナ
まずはコンテナを再起動させて, Redisコンテナが正常に動作していることを確認しましょう.
コンテナ内にアタッチして`redis-cli`を起動後, 以下のようにRedisのコマンドが実行できればokです.

```
$ docker-compose up -d
Creating myapp_db_1    ... done
Creating myapp_redis_1 ... done
Creating myapp_web_1   ... done
$ docker exec -it myapp_redis_1 /bin/bash
root@05aaca0b40db:/data# redis-cli
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> set name 'hoge'
OK
127.0.0.1:6379> get name
"hoge"
127.0.0.1:6379> FLUSHALL
OK
127.0.0.1:6379> keys *
(empty list or set)
``` 
[Redisコマンド一覧はこちら](http://redis.shibu.jp/commandreference/#id2)

### Rails.cacheを使ってみる
いよいよRailsでキャッシュを使ってみましょう.
ここでは以下のコマンドを使用します.

- `Rails.cache.write(key, value)` : キャッシュデータを保存
- `Rails.cache.exist?(key)` : key を持つキャッシュデータがあるか確認

```config/routes.rb
Rails.application.routes.draw do
  get 'hoge/:id', to: 'redis_samples#hoge'
end
```

```app/controllers/redis_samples_controller.rb
class RedisSamplesController < ApplicationController
  def hoge
    Rails.cache.write('name', 'hogehoge') if params[:id].to_i > 0
    render json: Rails.cache.exist?('name')
  end
end
```

<br />


コンソールからAPIを叩きます.
最初はパラメータに `id = 1` を与えてキャッシュデータを保存します.
もちろん`true`が返ってきます.

```
$ curl localhost:3001/hoge/1
true
```

<br />


実際にRedisコンテナからキャッシュストアを見てみると, 
先ほど保存したキャッシュデータが保存されていることが確認できます!
また, `ttl`コマンドでキャッシュデータの有効期限の残りを秒単位で取得できます.

```
$ docker exec -it myapp_redis_1 /bin/bash
root@05aaca0b40db:/data# redis-cli
127.0.0.1:6379> keys *
1) "name"
127.0.0.1:6379> get name
"\x04\bo: ActiveSupport::Cache::Entry\t:\x0b@valueI\..."
127.0.0.1:6379> ttl name
(integer) 49
```
指定した有効期限を経過した後に実行すると, 
きちんとキャッシュデータが削除されていることも確認できます.

```
127.0.0.1:6379> keys *
(empty list or set)
```

<br />

最後に削除されたことをRails側からも確認しましょう.
パラメータを `id = 0` としてAPIを叩くと`false`が返ってきます.

```
$ curl localhost:3001/katagamis/hoge/0
false
```

# 終わりに
さくっとできるかと思いきや結構手間取ってしまい, 忘れないうちにメモとして残しておきました.
まるっとチュートリアル的な記事ではないので, 開発進めてく途中の参考程度になれば幸いです.
ご意見・質問などあれば遠慮なく!!

# 参考記事
- https://morizyun.github.io/ruby/rails-function-cache-fetch-write-delete.html
- https://qiita.com/yamashun/items/bf9a3d29de749cf18f2e
- https://qiita.com/pokohide/items/7397b92a188da841b435
- https://www.sejuku.net/blog/58218