Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to cache instance variables between requests?

Miniprofiler indicates that my Rails app is making a large number of database queries on every page. The issue seems to be a leaderboard module that is included in the layout.

The application looks like this:

# application_controller.rb

before_filter :initialize_leaderboard

def initialize_leaderboard
  @leaderboard_users ||= User.monthly_leaderboard
end
# user.rb

def self.monthly_leaderboard
  User.includes(:events).sort_by(&:events_last_month).reverse
end

def events_last_month
  @events_last_month ||= events.where( created_at: Date.today.beginning_of_month-1.month..Date.today.end_of_month-1.month ).size
end
# application.html.erb

...
<% unless @leaderboard_users.blank? %>
  <% cache ["sidebar_leaderboard", @leaderboard_users] do %>
    ...html
  <% end %>
<% end %>

In the logs and miniprofiler output, I am seeing an events_last_month query for every user. This is less than desirable, and is making me question my understanding of how this all works. Have I go this right?

  1. The view is cached, so provided that a particular leaderboard_user has been viewed before (recently), his details should be displayed from the cache.
  2. So the many events_last_month queries must be originating from the initialize_leaderboard method in application_controller.rb. But, I understood that using ||= would cache the response of the User.monthly_leaderboard method for subsequent requests. Why is it being called on each page view?
like image 263
Andy Harvey Avatar asked Oct 25 '25 12:10

Andy Harvey


1 Answers

||= will memoize non-falsey results from User.monthly_leaderboard. That data is retained for the life of the ApplicationController instance. A new instance is created for every request.

From the docs:

When your application receives a request, the routing will determine which controller and action to run, then Rails creates an instance of that controller and runs the method with the same name as the action.

If you wish to cache (and occasionally expire and re-query) the value of that instance variable between requests you need to explicitly utilize an external cache or find another place to store the value that persists across requests such as a session if the result is relatively small in size and tied specific to a user's session.

If it is a value that should be queried once and persist until the web server is restarted then consider querying for that data in an initializer and storing the result somewhere that will persist for the life of the process.

like image 136
Matt Glover Avatar answered Oct 28 '25 01:10

Matt Glover