Rails 4: small issue with N + 1 for an undisclosed page

I have code that generates a problem with querying database N + 1.

The problem occurs if the page is not unpacked. When the page is cached, adding .includes actually results in an unnecessary database call. I am wondering how to get around this problem.

my applicaiton_helper.rb contains the following:

 module ApplicationHelper def by(article) "By #{article.username} on #{article.created_at.strftime('%B %e, %Y')}" end end 

my article.rb contains:

 class Article < ActiveRecord::Base belongs_to :user def username user.username end end 

and my article_controller.rb contains:

 class ArticlesController < ApplicationController def index @articles = user_signed_in? ? Article.all : Article.all.published.limit(13) end end 

This method is a username method that invokes a user model call. As stated above, when the page has not yet been cached, this leads to a helper method by(article) to continuously invoke the user model without any high load. However, since I cache my views, this inefficiency only occurs once. If I change my article_controller.rb to the following:

 class ArticlesController < ApplicationController def index @articles = user_signed_in? ? Article.all.includes(:user) : Article.all.published.limit(13).includes(:user) end end 

N + 1 problem disappears when loading the first page, but after reloading the page I get the unnecessary .includes .

Any idea how I can fix this small glitch?

Thanks!

+5
source share
2 answers

Somehow this solved my problem:

 class Article < ActiveRecord::Base belongs_to :user delegate :username, to: :user end 

So I just delegate the username call in the user model article. Beautiful, clean and does the trick: the bullet no longer complains.

0
source

I just came up with a crazy solution: you can check if a cached fragment exists in the controller. But the problem is that Rails automatically adds the digest file to the cache key. So my "solution": change the caching way to something like

 # in your view: <% cache 'foo', skip_digest: true do %> contents <% end %> 

And then in the controller you can check if the fragment is already cached:

 def index if fragment_exist?('asd') @articles = user_signed_in? ? Article.all : Article.published.limit(13) else @articles = user_signed_in? ? Article.all.includes(:user) : Article.published.limit(13).includes(:user) end end 

Obviously disabling digests is not a good solution: when solving your current problem, it adds a new one. But it works :)

I could not find a way to get the review digest in the controller, but, in any case, I think it would be redundant for such a small problem as a one-time N + 1. And if you only care about bullet warnings, you can disable them for a specific actions.

0
source

Source: https://habr.com/ru/post/1239987/


All Articles