#22 Eager Loading
Un buon punto di partenza per cominciare quando si vogliono aumentare le prestazioni delle vostre applicazioni Rails è dare un’occhiata agli accessi al database. Ridurre il numero di query fatte al database può incrementare la velocità delle vostre applicazioni in modo significativo. Un modo per farlo è quello di adottare una tecnica nota con il nome di eager loading.
I nostri modelli applicativi Task e Project.
La pagina di sopra mostra un numero consistente di task, e il progetto a cui ognuno di essi appartiene. Nel nostro TaskController, otteniamo tutti questi task e iteriamo su ciascuno di essi nella vista:
<h1>Tasks</h1> <ul> <% @tasks.each do |task| %> <li><%= link_to task.name, task %> in <%= task.project.name %></li> <% end %> </ul>The view code for the Tasks index page.
La pagina prende il nome del task e poi il nome dei suoi progetti attraverso l’associazione. Il problema nell’uso di questo approccio è che ogni volta che si ottiene il nome di un progetto del task, viene generata una query SQL. Ne si può avere conferma controllando i log di sviluppo.
Project Load (0.2ms) SELECT * FROM "projects" WHERE ("projects"."id" = 60)
CACHE (0.0ms) SELECT * FROM "projects" WHERE ("projects"."id" = 60)
CACHE (0.0ms) SELECT * FROM "projects" WHERE ("projects"."id" = 60)
CACHE (0.0ms) SELECT * FROM "projects" WHERE ("projects"."id" = 60)
CACHE (0.0ms) SELECT * FROM "projects" WHERE ("projects"."id" = 60)La nostra richiesta causa ripetute chiamate al database.
Possiamo vedere dai log che viene eseguita la stessa chiamata tante volte quanti sono i nomi dei progetti. Rails ci aiuta a ridurre il numero di chiamate, facendo cache delle richieste e recuperando le richieste ripetute dalla cache, piuttosto che dal database. Il sistema di caching è stato introdotto con Rails 2.0; se facessimo la stessa richiesta con Rails 1.x, ogni richiesta per il nome di un progetto associato al task comporterebbe una richiesta al database.
Il sistema di caching è utile, ma l’eager loading riduce le chiamate al database ulteriormente. Lo possiamo abilitare facendo una piccola modifica al TasksController:
class TasksController < ApplicationController def index @tasks = Task.find(:all, :include => :project) end end
Ciò che abbiamo fatto sopra è di aggiungere un parametro alla linea che recupera tutti i task, in modo tale che nel recupero si porti dietro anche i progetti associati. La ragione per cui nel codice abbiamo usato il simbolo al singolare (:project), anzichè :projects è che usiamo il nome dell’associazione, e nel modello Task cè scritto belongs_to => :project. Ora, se ricarichiamo la pagina e controlliamo i log, vediamo che il numero di richieste al database si è ridotto da 101 a 2, con un conseguente aumento delle performance della nostra applicazione.
Inclusione di più di una associazione
Oltre ad appartenere ad un Project, il nostro modello Task ha anche un’altra associazione con il modello Comment.
class Task < ActiveRecord::Base belongs_to :project has_many :comments end
Possiamo portarci dietro i commenti di un task insieme al suo progetto di appartenenza, includendo i nomi di entrambe le associazioni in un array. (notate che per i commenti usiamo il plurale poichè questo è ora il nome dell’associazione.)
class TasksController < ApplicationController def index @tasks = Task.find(:all, :include => [:project, :comments]) end end
Potremmo fare uso perfino di associazioni ancor più complesse. Se il nostro modello Comment fosse così …
class Comment < ActiveRecord::Base belongs_to :task belongs_to :user end
…potremmo portarci dietro anche gli utenti associati ai commenti, usando un hash:
@tasks = Task.find(:all, :include => [:project, {:comments => :user }])
L’utilizzo dell’eager loading è un potente modo per ridurre gli accessi al database delle vostre applicazioni Rails. Ci sono più informazioni a riguardo nel sito delle API Rails su http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html.


