Rails の cache ストレージで Active Record のオブジェクトを入れると事故りやすいと思う件

### やったこと

今回、Rails のキャッシュ機構で、Active Record のオブジェクトを保存すると "色々、大変なことがあるぞ" ということについて書いていきたいと思います。

ちなみにここでいうキャッシュ機構は、cache_store のことを指します。

- [Rails のキャッシュ機構 - Railsガイド](https://railsguides.jp/caching_with_rails.html#%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E3%82%B9%E3%83%88%E3%82%A2)

それでは始めます。

### 確認環境

```
$ bundle exec rails --version
Rails 5.2.4.3

$ mysql --version
mysql  Ver 14.14 Distrib 5.6.43, for osx10.13 (x86_64) using  EditLine wrapper
```

### 検証

#### 準備

テーブル作成

```mysql
CREATE TABLE `pencils` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `maker_name` varchar(50) NOT NULL,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC
```

データ登録

```mysql
INSERT INTO pencils VALUES (NULL, 'maker1', NOW(), NOW());
```

app/models/pencil.rb

```ruby
class Pencil < ApplicationRecord
end
```

config/environments/development.rb (抜粋)

```ruby
config.cache_store = :memory_store
```

#### cache に保存する

```shell
$ bundle exec rails c
Running via Spring preloader in process 65049
Loading development environment (Rails 5.2.4.3)
irb(main):001:0> p = Pencil.last
   (1.3ms)  SET NAMES utf8mb4,  @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'),  @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
  Pencil Load (0.9ms)  SELECT  `pencils`.* FROM `pencils` ORDER BY `pencils`.`id` DESC LIMIT 1
=> #<Pencil id: 1, maker_name: "maker1", created_at: "2020-11-08 22:45:49", updated_at: "2020-11-08 22:45:49">
irb(main):002:0> Rails.cache.write('pencil1', p)
=> true
irb(main):003:0> Rails.cache.read('pencil1')
=> #<Pencil id: 1, maker_name: "maker1", created_at: "2020-11-08 22:45:49", updated_at: "2020-11-08 22:45:49">
irb(main):004:0> Rails.cache.read('pencil2')
=> nil
```

#### クラス名を変更する (そろそろ来るぞ!)

app/models/pencil.rb を下記のように変更します。


app/models/pencil2.rb

```ruby

class Pencil2 < ApplicationRecord
end
```

#### 保存してある cache を読み込む

```irb
irb(main):004:0> reload!
Reloading...
=> true
irb(main):005:0> Rails.cache.read('pencil1')
Traceback (most recent call last):
        1: from (irb):5
NameError (uninitialized constant Pencil)
```

はい。Pencil クラスがなくなってしまったので、ロードで失敗してしまいました。

これは、"色々、大変なことがあるぞ" の1つです。

#### 対応方針

cacheストレージ使うときは、"安心なデータ" を入れる必要があります。

データ構造じゃない、value を入れるのが安心です。

例えば以下が "安心なデータ" と考えます。

- 数値 例: 123
- 文字列 例: あいう

配列、json、ハッシュもデータ構造が変わるときに対応を忘れないようにすれば、使って良いと思います。

Active Record のオブジェクトは、cacheストレージに保存しない方が安心です。

なぜかといえば、例えば、Railsのバージョンが変わったとき、`ApplicationRecord` の内部の構造も変わり

読み込めなくなる可能性があるからです。

はい。実際にありました。

`ApplicationRecord` は rails の本体に入っているため、この事態に気が付きにくいです。

また、システムが大きくなればなるほど、対応範囲の調査が大変になります（経験談）


### まとめ

**cacheストレージ使うときは、構造が変更される可能性が低いデータを入れましょう！！！**

"安心なデータ" は、構造が変更される可能性が低いデータになります。

- 数値 例: 123
- 文字列 例: あいう

もちろん、配列、json、ハッシュなどを使ったほうがいいケースもあり、その場合は構造を変更するときに注意してください。

構造が変更される可能性があって、検知しづらいのが `Active Record のオブジェクト` だと思います。


`Active Record のオブジェクト` をcacheストレージに入れた方が良い場合があるかもしれませんが、

いまのところ、私は、`Active Record のオブジェクト` をcacheストレージに使わないほうが良いと思っています。

### 参考

- [Rails の cache ストレージで Active Record のオブジェクトを入れると事故りやすいと思う件 – ためすう](https://blog.tamesuu.com/2020/11/14/592/)