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

こんにちは。たびたび、Rails の cache に悩まされているラクマの豊永です。

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

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

それでは始めます。

確認環境

$ 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

検証

準備

テーブル作成

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

データ登録

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

app/models/pencil.rb

class Pencil < ApplicationRecord
end

config/environments/development.rb (抜粋)

config.cache_store = :memory_store

cache に保存する

$ 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

class Pencil2 < ApplicationRecord
end

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

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ストレージに使わないほうが良いと思っています。

それでは失礼します。