How to Speed Up Load Times In A Rails App – What I Wish I Knew Four Months Ago

Not too long ago I was introduced to Ruby on Rails through my time as a Flatiron student. To those familiar with the Ruby framework, Rails makes creating complex MVC web applications very simple. It wasn’t long before I started developing fairly compli…


This content originally appeared on DEV Community and was authored by nicklevenson

Not too long ago I was introduced to Ruby on Rails through my time as a Flatiron student. To those familiar with the Ruby framework, Rails makes creating complex MVC web applications very simple. It wasn't long before I started developing fairly complicated apps with Rails. However, after gaining a fair amount of users on my Heroku hosted app MeMix, I ran into some big problems. My application kept crashing. It hadn't been crashing before, and after some diagnostics with the New Relic analytics tool, the problem became clear - slow database loading times. Heroku will automatically crash you app if your load time takes more than 30 seconds. I clearly had a big problem with my database queries, something that is referred to as N+1 queries.

The Problem

A big cog in the Rails machine is something called Active Record. Let's say we had two models that are associated with each other: (I actually made a Github repo with this sample so you can easily try it yourself)

class User < ActiveRecord::Base
  has_many :posts
end

class Post < ActiveRecord::Base
  belongs_to :user
end

With Active Record, a typical and easy way to access a list of a user's posts, and what I learned at bootcamp, would be to write User.posts. But what if we want to iterate over a list of Users and then iterate over each user's posts? We could write something like:

User.all.each do |user|
  user.posts.each do |post|
    puts post.content
  end
end

This will work just fine. However for each user we are querying the database for their posts. Which means our query complexity has become N+1. N being the number of users, since for each user we make a request to the database for its associated posts, and plus one query for getting all users.

This is totally fine if you have a small database. However, once your database grows, and if you have complicated associations, it will start to slow down.

The Solution

ActiveRecord has a method called includes. Basically it allows you to load a model's associations in a single query. So in our example before, you would write:

User.includes(:posts).all.each do |user|
  user.posts.each do |post|
    post.content
  end
end

Here's the difference between the queries when looking at the database output in the console:

Bad Query:

User.bad_query
  User Load (0.2ms)  SELECT "users".* FROM "users"
  Post Load (0.1ms)  SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = ?  [["user_id", 1]]
hello
  Post Load (0.1ms)  SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = ?  [["user_id", 2]]
hello

Good Query:

User.good_query
  User Load (0.2ms)  SELECT "users".* FROM "users"
  Post Load (0.2ms)  SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (?, ?)  [[nil, 1], [nil, 2]]
hello
hello

Can you spot the extra db call in the bad query? This is just with two users, but its easy to imagine a complicated db structure causing major speed issues. The cool thing about includes is that you can chain on as many associations as you want - User.includes(:posts, :followers, :likes).all.

Sometimes, however, using this handy active record method could slow down your app if the database query is very complicated (e.g. nested associations, many-to-many, etc). It can be hard to know when it is more efficient to use it. However, there is a gem called Bullet that is designed to help with this exact issue. It will tell you when your query chains are inefficient and what to do about it.

After implementing this technique on my slow Rails app, the average load time went down from 10-15 seconds to 2-3 seconds. So if you're experiencing slow load times, please consider checking your query methods, and look for N+1 queries. I wish I knew this when I built my first Rails app.


This content originally appeared on DEV Community and was authored by nicklevenson


Print Share Comment Cite Upload Translate Updates
APA

nicklevenson | Sciencx (2021-05-13T19:55:33+00:00) How to Speed Up Load Times In A Rails App – What I Wish I Knew Four Months Ago. Retrieved from https://www.scien.cx/2021/05/13/how-to-speed-up-load-times-in-a-rails-app-what-i-wish-i-knew-four-months-ago/

MLA
" » How to Speed Up Load Times In A Rails App – What I Wish I Knew Four Months Ago." nicklevenson | Sciencx - Thursday May 13, 2021, https://www.scien.cx/2021/05/13/how-to-speed-up-load-times-in-a-rails-app-what-i-wish-i-knew-four-months-ago/
HARVARD
nicklevenson | Sciencx Thursday May 13, 2021 » How to Speed Up Load Times In A Rails App – What I Wish I Knew Four Months Ago., viewed ,<https://www.scien.cx/2021/05/13/how-to-speed-up-load-times-in-a-rails-app-what-i-wish-i-knew-four-months-ago/>
VANCOUVER
nicklevenson | Sciencx - » How to Speed Up Load Times In A Rails App – What I Wish I Knew Four Months Ago. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2021/05/13/how-to-speed-up-load-times-in-a-rails-app-what-i-wish-i-knew-four-months-ago/
CHICAGO
" » How to Speed Up Load Times In A Rails App – What I Wish I Knew Four Months Ago." nicklevenson | Sciencx - Accessed . https://www.scien.cx/2021/05/13/how-to-speed-up-load-times-in-a-rails-app-what-i-wish-i-knew-four-months-ago/
IEEE
" » How to Speed Up Load Times In A Rails App – What I Wish I Knew Four Months Ago." nicklevenson | Sciencx [Online]. Available: https://www.scien.cx/2021/05/13/how-to-speed-up-load-times-in-a-rails-app-what-i-wish-i-knew-four-months-ago/. [Accessed: ]
rf:citation
» How to Speed Up Load Times In A Rails App – What I Wish I Knew Four Months Ago | nicklevenson | Sciencx | https://www.scien.cx/2021/05/13/how-to-speed-up-load-times-in-a-rails-app-what-i-wish-i-knew-four-months-ago/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.