Rails Performance Optimization

Rails Performance Optimization

Learn how to test the performance of your rails applications and ways to improve. We will demonstrate simple changes to increase throughput and reduce response time for a rails app. Topics will include asset optimization, using a CDN, caching and more.

2e8b04b449ed3e426ea928997d8640e2?s=128

Tyler Mercier

June 19, 2013
Tweet

Transcript

  1. 8.

    1 class Cake < ActiveRecord::Base 2 belongs_to :category 3 attr_accessible

    :name, :photo 4 end 1 class Category < ActiveRecord::Base 2 has_many :cakes 3 attr_accessible :name 4 end Wednesday, 19 June, 13
  2. 10.

    “We should forget about small efficiencies, say about 97% of

    the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%.” Wednesday, 19 June, 13
  3. 11.

    1 class CategoryList 2 # Add the uncategorized "magic" category

    3 def add_uncategorized 4 # Support for uncategorized topics 5 uncategorized_topics = Topic 6 .listable_topics 7 .where(category_id: nil) 8 .topic_list_order 9 .limit(SiteSetting.category_featured_topics) 10 if uncategorized_topics.present? 11 12 totals = Topic.exec_sql("SELECT SUM(CASE WHEN created_at >= (CURRENT_TIMESTAMP - INTERVAL '1 WEEK') THEN 1 ELSE 0 END) as topics_week, 13 SUM(CASE WHEN created_at >= (CURRENT_TIMESTAMP - INTERVAL '1 MONTH') THEN 1 ELSE 0 END) as topics_month, 14 SUM(CASE WHEN created_at >= (CURRENT_TIMESTAMP - INTERVAL '1 YEAR') THEN 1 ELSE 0 END) as topics_year, 15 COUNT(*) AS topic_count 16 FROM topics 17 WHERE topics.visible 18 AND topics.deleted_at IS NULL 19 AND topics.category_id IS NULL 20 AND topics.archetype <> '#{Archetype.private_message}'").first 21 22 23 uncategorized = Category.new({name: SiteSetting.uncategorized_name, 24 slug: Slug.for(SiteSetting.uncategorized_name), 25 color: SiteSetting.uncategorized_color, 26 text_color: SiteSetting.uncategorized_text_color, 27 featured_topics: uncategorized_topics}.merge(totals)) 28 29 # Find the appropriate place to insert it: 30 insert_at = nil 31 @categories.each_with_index do |c, idx| 32 if (uncategorized.topics_week || 0) > (c.topics_week || 0) 33 insert_at = idx 34 break 35 end 36 end 37 38 @categories.insert(insert_at || @categories.size, uncategorized) 39 end 40 41 if @all_topics.present? && uncategorized.present? 42 uncategorized.displayable_topics = uncategorized_topics 43 @all_topics << uncategorized_topics 44 @all_topics.flatten! 45 end 46 end 47 end Wednesday, 19 June, 13
  4. 21.

    Slow pages hurt 0% 25% 50% 0 2 4 6

    8 10 Source Wednesday, 19 June, 13
  5. 27.

    # Gemfile 1 group :assets do 2 gem 'asset_sync' 3

    end # production.rb 1 ... 2 config.action_controller.asset_host = ENV['ASSET_HOST'] 3 ... FOG_PROVIDER=AWS AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY FOG_DIRECTORY=<bucketname> ASSET_HOST=//<cloudfront subdomain>.cloudfront.net Wednesday, 19 June, 13
  6. 43.

    1 # without delayed_job 2 Notifier.signup(@user).deliver 3 # with delayed_job

    4 Notifier.delay.signup(@user) Wednesday, 19 June, 13
  7. 45.

    1 SELECT "cakes".* FROM "cakes" LIMIT 10 2 SELECT "categories".*

    FROM "categories" WHERE "categories"."id" = 1 LIMIT 1 3 SELECT "categories".* FROM "categories" WHERE "categories"."id" = 2 LIMIT 1 4 SELECT "categories".* FROM "categories" WHERE "categories"."id" = 3 LIMIT 1 5 .... Wednesday, 19 June, 13
  8. 46.

    1 SELECT "cakes".* FROM "cakes" LIMIT 10 2 SELECT "categories".*

    FROM "categories" WHERE "categories"."id" = 1 LIMIT 1 3 SELECT "categories".* FROM "categories" WHERE "categories"."id" = 2 LIMIT 1 4 SELECT "categories".* FROM "categories" WHERE "categories"."id" = 3 LIMIT 1 5 .... N + 1 Wednesday, 19 June, 13
  9. 48.

    1 class CakesController < ApplicationController 2 def index 3 @cakes

    = Cake.all 4 end 5 end Wednesday, 19 June, 13
  10. 49.

    1 class CakesController < ApplicationController 2 def index 3 @cakes

    = Cake.all(include: :category) 4 end 5 end Wednesday, 19 June, 13
  11. 53.

    1 class AddIndexesToCakes < ActiveRecord::Migration 2 def change 3 add_index

    :cakes, :updated_at 4 add_index :cakes, :category_id 5 end 6 end Wednesday, 19 June, 13
  12. 57.

    λ rake db:find_indexes * TIP: if you have a problem

    with the index name('index name too long') you can solve with the :name option. Something like :name => 'my_index'. * run `rails g migration AddMissingIndexes` and add the following content: class AddMissingIndexes < ActiveRecord::Migration def change add_index :cakes, :user_id end end Wednesday, 19 June, 13
  13. 62.

    1 class PostsController 2 caches_page :index 3 4 def index

    5 @posts = Post.all 6 end 7 8 def clear 9 expire_page :action => :index 10 end 11 end Wednesday, 19 June, 13
  14. 64.

    1 class PostsController < ApplicationController 2 caches_action :index 3 4

    def index 5 @posts = Post.all 6 end 7 end Wednesday, 19 June, 13
  15. 67.

    1 <%= cache "post-#{@post.id}-#{@post.updated_at}" do %> 2 <p> 3 <b>Title:</b>

    4 <%= @post.title %> 5 </p> 6 7 <p> 8 <b>Content:</b> 9 <%= @post.content %> 10 </p> 11 <% end %> Wednesday, 19 June, 13
  16. 70.

    1 class Post 2 def self.top_100 3 timestamp = Post.maximum(:updated_at)

    4 Rails.cache.fetch ['top-100', timestamp.to_i].join('/') do 5 order('vote_count DESC').limit(100).all 6 end 7 end 8 end Wednesday, 19 June, 13
  17. 73.

    yslow webpagetesting.org chrome developer tools apache bench seige locust.io curl

    rack-mini-profiler gem bullet gem loldba gem log-2-viz log-runtime-metrics new relic Wednesday, 19 June, 13