1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

railsでmodelのscopeの実行結果をキャッシュしたかった話

Last updated at Posted at 2022-01-26

問題

policies(規約)テーブル

field type
id int(11)
version int(11)

最新の規約バージョンがほしい、
つまりpoliciesテーブルのレコードの中のversionカラムの最大値を取得したい場合を想定します。

以下のようにscopeを定義し実行することで取得できます。

policy.rb
class Policy < ApplicationRecord
  scope :latest_version -> { maximum(:version) }
end

2022/9 追記です。記事の本筋とは関係ないですが、こちらはscopeの使い方として正しくない事に気づいたので注意してください。scopeはActiveRecord::Relationが戻り値になるようにするべきです。参考

Policy.latest_version #=> 10

# もちろんscopeを定義せずに直接実行してもよいです
Policy.maximum(:version) # => 10

ここで懸念が発生しました。
もし頻繁にPolicy.latest_versionを使用したい場合、いちいちDBへ検索をかけてしまうことになります。
(ひとつのメソッド内であれば実行結果を変数に格納しておけばよいですが、いろんな場所で使うケースですね)

皆さんならこのような時どうしますか?

結論

このままで良い

RailsにはSQLキャッシュという機能があり、以前と同じクエリが発生すると、データベースへクエリを実行する代わりに、キャッシュされたデータを利用します。
参考: Railsガイド

例えばPolicy.latest_versionを5回実行した場合の、ログは以下のようになります。

(5.3ms) SELECT MAX("policies"."version") FROM "policies"
CACHE (0.1ms) SELECT MAX("policies"."version") FROM "policies"
CACHE (0.1ms) SELECT MAX("policies"."version") FROM "policies"
CACHE (0.1ms) SELECT MAX("policies"."version") FROM "policies"
CACHE (0.1ms) SELECT MAX("policies"."version") FROM "policies"

1回目だけDBに実際に検索をかけていますが、それ以降はキャッシュを利用していることがわかります。
行頭にCACHEとついている場合は、SQLキャッシュ機能が使用されDBに検索をかけずにキャッシュからデータを取得します。
実際に処理時間が減っていることが確認できるかと思います。(5.3ms => 0.1ms)

念の為、誤解のないように補足します。
キャッシュが利用されるのは同じクエリであることが条件で、scopeを使用することが条件ではありません。当然ですが以下でも問題なくキャッシュからデータを取得します。

Policy.maximum(:version)
# CACHE (0.1ms) SELECT MAX("policies"."version") FROM "policies"

ただし条件つき

SQLキャッシュが共有されるのは、あくまで1つのHTTPリクエストに対するアクションの範囲内です。

アクションの開始時に作成され、アクションの終了時に破棄されるので、キャッシュはアクションの実行中しか保持されないという点です。
Railsガイド

その他の手段

クラスインスタンス変数にキャッシュするという手段もあります。

policy.rb
class Policy < ApplicationRecord
  def self.latest_version
    @latest_version ||= maximum(:version)
  end
end

ただし本番サーバでは、クラスはサーバ起動時に作られてそれっきりです。
したがって、一度@latest_version変数にデータが格納されると、その後にDB側の規約最新バージョンが更新されても、クラスインスタンス変数は更新されません。
次にクラスインスタンス変数がリセットされるのは、サーバ再作成を伴うデプロイ処理時ということになるので注意が必要です。

お気づきの点がありましたら、ご気軽にご指摘ください。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?