Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Rails Performance Optimization - YYC Ruby

Rails Performance Optimization - YYC Ruby

Avatar for mo khan

mo khan

June 13, 2013
Tweet

More Decks by mo khan

Other Decks in Programming

Transcript

  1. 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. “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. 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. Slow pages hurt 0% 25% 50% 0 2 4 6

    8 10 Source Wednesday, 19 June, 13
  5. # 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. 1 # without delayed_job 2 Notifier.signup(@user).deliver 3 # with delayed_job

    4 Notifier.delay.signup(@user) Wednesday, 19 June, 13
  7. 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. 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. 1 class CakesController < ApplicationController 2 def index 3 @cakes

    = Cake.all 4 end 5 end Wednesday, 19 June, 13
  10. 1 class CakesController < ApplicationController 2 def index 3 @cakes

    = Cake.all(include: :category) 4 end 5 end Wednesday, 19 June, 13
  11. 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. λ 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. 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. 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. 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. 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. 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