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?
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?||= 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With