2016-01-06 10 views
5

Ho un codice che sta generando un problema di query sul database N + 1.Rails 4: piccolo problema N + 1 per pagina non collegata

Il problema si verifica solo se la pagina è scollegata. Una volta che la pagina è stata memorizzata nella cache, l'aggiunta di uno .includes comporta effettivamente una chiamata al database non necessaria. Mi sto chiedendo come ovviare a questo problema.

mia applicaiton_helper.rb contiene quanto segue:

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

mia article.rb contiene:

class Article < ActiveRecord::Base 
    belongs_to :user 

    def username 
    user.username 
    end 
end 

e la mia articles_controller.rb contiene:

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

Il metodo in questione è il metodo username, che effettua una chiamata al modello Utente. Come già detto, quando la pagina non è già stata memorizzata nella cache, ciò comporta il metodo helper by(article) per effettuare chiamate continue al modello User senza alcun caricamento eccessivo. Tuttavia, poiché memorizzo nella cache le mie opinioni, questa inefficienza si verifica solo una volta. Se cambio il mio articles_controller.rb al seguente:

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

il problema N + 1 scompare al primo caricamento della pagina, ma poi ho ottenere un inutile .includes su di ricaricare la pagina.

Qualche idea su come posso risolvere questo piccolo problema?

Grazie!

+0

Questo non ha senso. 'includes' dovrebbe usare una query per caricare gli utenti. Probabilmente sta succedendo qualcos'altro. Stai abilitando la memorizzazione nella cache in fase di sviluppo per testare questo? – Mohamad

+0

Mi sembra che sia la chiamata "article.username" a causare la ricerca nel modello tramite una richiesta aggiuntiva. – jbehrens94

+0

@Mohamad Ho appena disabilitato il caching nello sviluppo. Ora ricevo costantemente un messaggio da Bullet che dice '' 'N + 1 query rilevata Articolo => [: utente] Aggiungi al tuo finder:: includes => [: user] N + 1 Stack di chiamate con metodo di query ~ /myapp/app/models/article.rb:64:in 'username'''' – DaniG2k

risposta

0

qualche modo questo ha risolto il mio problema:

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

Così ho semplicemente delegare la chiamata nome utente su un articolo per il modello User. Bello, pulito e fa il trucco: la pallottola non si lamenta più.

0

Sono appena arrivato a una soluzione pazzesca: è possibile verificare se il frammento memorizzato nella cache è presente nel controller. Ma il problema è che Rails aggiunge automaticamente un file digest alla chiave di cache. Quindi, la mia "soluzione": cambio modo di caching per smth come questo

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

E poi nel controller che si potrebbe verificare se frammento è già memorizzato nella cache:

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 

Ovviamente, spegnendo digerisce non è molto buona soluzione: mentre risolvi il tuo problema attuale ne aggiunge uno nuovo. Ma funziona :)

Non sono riuscito a trovare un modo per ottenere il digest della vista nel controller, ma comunque penso che sarebbe eccessivo per un problema così piccolo come N + 1 avvenuto una volta. E se ciò che ti infastidisce sono solo gli avvisi bullet puoi fare turn them off per un'azione particolare.

+0

Grazie per questo. In realtà penso che il problema potrebbe essere con Bullet e non con l'uso di '.includes (: user)'. Lo riferirò e vedrò cosa pensano. – DaniG2k

+0

Per quanto mi riguarda, 'bullet' ha ragione in questo caso, perché quando la pagina è memorizzata nella cache' includes' non è necessario. Ma dato che lo sai e il comportamento desiderato, puoi semplicemente disattivare l'avviso. – hedgesky

Problemi correlati