The Spaghetti Refactory Established 2015

Speed up your app's delegation skills using `:includes`

My last post talked about delegating methods to a parent model using the delegate module. I want to talk about performance issues you could experience doing this, and a simple way to fix them and speed them up.

When querying databases, there's something called "eager loading" that essentially is a way for the code to pull associated objects that may be needed at the same time as pulling the main object, thereby saving a separate DB call and hopefully saving some time. I won't pretend to know exactly how this works, I just know that it's supposed to be faster to do it this way.

In my previous example, I had a Course with an associated CourseName, because I wanted to be able to create multiple Courses with the same name but different times and dates. I delegated the :name method to CourseName, so that instead of calling course.course_name.name, I can simply call course.name to achieve the same results. However, when I do that, it has to load the CourseName object on the fly, and that could result in some additional sluggishness. (Apparently, this is hotly debated whether these N+1 queries are faster or slower.)

This is where eager loading is helpful.

When you are finding a course with Course.find(params[:course_id]), throw in one additional thing to make this eager load the associated course name:

Course.includes(:course_name).find(params[:course_id])

This helps remove the N+1 query and will hopefully help my performance. Now let's say I have a School object which has_many :courses. When I call school.courses, if I'm going to be displaying the course name as well, I'm into N+1 query land again. The easiest way to fix this is by adding it directly into the association in the School model:

class School < ActiveRecord::Base
has_many :courses, -> { includes(:course_name) }
end

Whambo.


Hat Tip: http://blog.arkency.com/2013/12/rails4-preloading/ for the help in understanding preloading

Tags