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 5 years have passed since last update.

RelationのModelオブジェクトを Array に変換せず、 Relation そのままMemcachedにキャッシュさせたい場合

Last updated at Posted at 2018-12-06

はじめに

RelationのModelオブジェクトを Array に変換せず、 Relation そのままMemcachedにキャッシュさせる方法について記載していきます。

結論

結論から言いますと、 load を呼び出すことで強制的にSQLを発行させ、ロードすることができます。

ことのはじまり

Modelオブジェクトをキャッシュしたい

Memcachedへオブジェクトをキャッシュさせるために以下のコードを実行してみましょう。

Rails.cache.fetch('key'){
  Character.where(attack_type: 1)
}

あら?キャッシュしたと思ったんですが。。。

Rails.cache.read('key')
D, [2018-12-06T09:41:50.742460 #14296] DEBUG -- : [Shard: slave1]  Character Load (7.2ms)  SELECT `characters`.* FROM `characters` WHERE (`characters`.`deleted_at` IS NULL) AND `characters`.`attack_type` = 1
[#<Character:0x007fb2802c8280 ...

Memcachedからオブジェクトを取り出して使おうとするたびにSQLが発行されます。これじゃあまりキャッシュする意味がありません。

これは遅延評価により、実際使うまでSQLも発行されず、オブジェクトも生成されないためです。
そしてMemcachedにはSQL発行&レコードがロードがされてない Relation オブジェクトのみがキャッシュされていることになります。

どうする?

他の記事で書いてある通りに、 to_a を呼び出すことで、 Array に変換して保持する方法もあります。それもよい方法だと思います。
変換する段階で(実際操作する段階で)SQLが発行され、オブジェクトが生成されるためです。

Rails.cache.fetch('key'){
  Character.where(attack_type: 1).to_a
}

でも、Relationの機能も使いたい。

せっかくRails使っているし、 Relation の機能もそのまま使いたいという要望もあるかと思います。

(Arrayを無理やりにモンキパッチして、 Relation 風にしてもよいかもしれませんが、そこまでやる?的な感じがしますね。)

Railsのソースをみると以下のロジックが書いてあります。

module ActiveRecord
  class Relation
    ...

    # Causes the records to be loaded from the database if they have not
    # been loaded already. You can use this if for some reason you need
    # to explicitly load some records before actually using them. The
    # return value is the relation itself, not the records.
    #
    #   Post.where(published: true).load # => #<ActiveRecord::Relation>
    def load(&block)
      exec_queries(&block) unless loaded?

      self
    end
  end
end

コメントを見ますと、 " 明示的にレコードをロードする必要があれば、これ(load)をつかってね " と記載されています。
そして、実際のロジックを見ますと、実際にクエリを発行していることがわかります。

結論

結論は、load をつけることです。

Rails.cache.fetch('key'){
  Character.where(attack_type: 1).load
}
obj = Rails.cache.read('key')
[#<Character:0x007fb2802c8280 ...

obj.class
Character::ActiveRecord_Relation

やった!SQLを発行しない。 Array ではなく、 Relation ままでキャッシュさせた!

まとめ

  • load をつけることで、ModelのRelationオブジェクトを強制的にロードできる。そしてそれをMemcachedにいれればピュアなオブジェクトキャッシュができる。
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?