Rails4 : Model caching (low level caching)

2019-06-04 03:09发布

I am trying to cache results of Active Record Query.

Here is my use case:

User.rb
  def self.awesome_users_cached
    Rails.cache.fetch("awesome_users") { where(awesome: true) }
  end

UsersController
  def index
    all_awesome_users  = User.awesome_users_cached
    less_awesome_users = User.where(less_awesome_user:true).pluck(:id)
    @users             = all_awesome_users.where.not(id: less_awesome_users)
  end

Here are 2 issues:

  1. self.awesome_users_cached doesnt cache the results and ends up hitting the
  2. all_awesome_users ends up as an array instead of active Record and hence I cannot filter out less_awesome_users from it.

There was a solution that doesnt seem to work in Rails4 anymore: Rails cache with where clause

1条回答
等我变得足够好
2楼-- · 2019-06-04 03:41

You should never cache ActiveRecord instances, or complex objects in general. Rather, you should cache simple primitive values, such as the primary keys of the records.

In your case, there is one more issue. where(awesome: true) is returning a scope, not a query result. Because the scope is lazy-executed, you effectively never cache anything but the query is always executed.

If you want to cache the lookup, execute the heavy query and return the IDs of the affected records. Then perform a second query fetching the records by id. You will be running 2 queries, but the first one (the most expensive one) will be cached after the first time.

def self.awesome_users_cached
  ids = Rails.cache.fetch("awesome_users") { where(awesome: true).pluck(:id) }
  find(ids)
end

You can cache the lookup of the first query, but as I mentioned before it is not a good idea.

def self.awesome_users_cached
  Rails.cache.fetch("awesome_users") { where(awesome: true).to_a }
end

You can also use a different query approach so that you can easily chain additional conditions to the cached query.

def self.awesome_users_cached
  ids = Rails.cache.fetch("awesome_users") { where(awesome: true).pluck(:id) }
  where(id: ids)
end

awesome_users_cached.where.not(id: same_company_users)
查看更多
登录 后发表回答