I have been working on a web application made with Ruby on Rails. I've "completed" the site, but it runs very slow and pages sometimes take on the order of ten seconds to load. I've broken this post up into three sections:
- Overview
- Diagnosis
- Possible Ideas
Overview
As a very rough overview, this website displays personal projects, each of which have their own page. Each project (which I call a “post” in the code) is stored in a table. I’m not sure if it’s a bad idea to store a whole post in a database as some posts have a decent amount of text and images in their body. Each entry in the posts
table has the following attributes:
# == Schema Information
#
# Table name: posts
#
# id :integer not null, primary key
# title :string
# body :text
# description :text
# slug :string
# created_at :datetime not null
# updated_at :datetime not null
# image_file_name :string
# image_content_type :string
# image_file_size :integer
# image_updated_at :datetime
# thumbnail_file_name :string
# thumbnail_content_type :string
# thumbnail_file_size :integer
# thumbnail_updated_at :datetime
# published :boolean default("f")
# published_at :datetime
#
Diagnosis
I’m using Heroku to host the application. I’ve upgraded to Hobby level dynos, so I could have access to some of the metrics they provide, like throughput, memory usage, etc. I don’t think Heroku is what is making the website slow, but rather my code. In an effort to debug, I've added a third party addon, Scout, that tracks the bottlenecks in the application.
Scout offers a trace feature that quantizes which code paths are taking the most time in the application. You can see below (in the bottom half of the picture) that there are a significant amount of traces that take upwards of ten seconds. Not very good…
When I click on the second line (6/17 2:04 PM), it gives a breakdown of the response time:
Expanding the SQL statements shows that most of the time intensive queries are from acting on the posts database, and sometimes sorting/ordering the posts (see below).
Possible Ideas
Is there anything obvious I am doing wrong here? I am pretty stuck, and not sure how to speed things up. Given what Scout is saying, I have two ideas:
- Controller/SQL queries the controller invokes are slow
- Embedded Ruby code in HTML is slow.
Controller/SQL queries the controller invokes are slow:
The code below shows the code where I am assigning @posts
in the PostsController
. The home
method is run when the user visits the home page, and the index
method is run when the user goes to the posts page. Could these queries be slow because there is a fair amount of data in the database (5 posts worth of text and images)?
class PostsController < ApplicationController
#before_action :authenticate_user!
before_action :set_post, only: [:show, :edit, :update, :destroy, :publish, :unpublish]
def home
@posts = Post.all.published.order('published_at DESC')
end
# GET /posts
# GET /posts.json
def index
if user_signed_in? and current_user.admin
@posts = Post.all.order("id")
else
@posts = Post.all.published
end
end
Embedded Ruby Code in HTML is slow:
I am using Ruby in some of my HTML code to sort the posts by date and to determine the most recent post. For example, in the sidebar of the website (to the left of the home page), there is section that displays “Recent”, and the logic behind that is:
<h4 style="border-bottom: 1px solid #bbb">Recent</h4>
<% @post=Post.all.published.order("published_at").last %>
<% if @post == nil or @post.published_at == nil %>
<div class="temp_sidebar">Coming Soon!</div>
<% else %>
<%= render partial: "layouts/media", locals: {post: @post} %>
<% end %>
Similarly, in the “Archives” section of the sidebar, I’m sorting the posts by date, and am doing this logic:
<% if Post.published.length != 0 %>
<h4>Archives</h4>
<div id="sidebar" class="list-group">
<% @published_posts = Post.published %>
<% archives = Hash.new(0) %>
<% @published_posts.each do |post| %>
<% if archives[post.date] == 0 %>
<% archives[post.date] = 1%>
<% else %>
<% archives[post.date] += 1 %>
<% end %>
<% end %>
<% archives.each do |key, value| %>
<button class="accordion"><%= key %> <span class="badge"> <%= value %></span></button>
<div class="panel">
<% @published_posts.each do |post| %>
<% if post.date == key %>
<p><%= link_to post.title, post_path(@post) %></p>
<% end %>
<% end %>
</div>
<% end %>
</div>
<% end %>
My idea is that maybe iterating over posts is taking a very long time, but I’m not entirely sure. I feel like this is valid code that is ok to be used, but maybe something about it is very slow. Would you guys have any ideas here? It may also be good to note here that the app's memory usage is pretty high: around 500MB. Maybe these queries are slow because of all the data that is getting fetched, but that said, I'm am not quite sure what "a lot" of data is for a web app like this. And of course, my hypothesis as to why this site is slow could be totally wrong, so I'm very open to your thoughts. Lastly, if the SQL queries/code I am using is slow, are there ways I could speed it up/improve its performance? Thank you for any help in advance!