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

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. Rails Performance Optimization Wednesday, 19 June, 13

  2. Tyler Mercier @tylermercier Mo Khan @mocheen Wednesday, 19 June, 13

  3. Who is using heroku? Wednesday, 19 June, 13

  4. Who is using Rails 3.1+? Wednesday, 19 June, 13

  5. Who is using thin? Wednesday, 19 June, 13

  6. https://github.com/mokhan/confection Wednesday, 19 June, 13

  7. Wednesday, 19 June, 13

  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
  9. “Premature optimization is the root of all evil.” Wednesday, 19

    June, 13
  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
  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
  12. What does Performance mean Wednesday, 19 June, 13

  13. Load Throughput Capacity Wednesday, 19 June, 13

  14. Load - How much work Throughput Capacity Wednesday, 19 June,

    13
  15. Load Throughput - Tasks per sec Capacity Wednesday, 19 June,

    13
  16. Load Throughput Capacity - When does it fail Wednesday, 19

    June, 13
  17. Wednesday, 19 June, 13

  18. Load Capacity Throughput Wednesday, 19 June, 13

  19. Load Capacity Throughput Wednesday, 19 June, 13

  20. Why Performance Wednesday, 19 June, 13

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

    8 10 Source Wednesday, 19 June, 13
  22. Optimization Time! Wednesday, 19 June, 13

  23. Optimize Images Wednesday, 19 June, 13

  24. Page Size 39% 61% Source Wednesday, 19 June, 13

  25. Content Delivery Network Wednesday, 19 June, 13

  26. Wednesday, 19 June, 13

  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
  28. Wednesday, 19 June, 13

  29. Wednesday, 19 June, 13

  30. Common Mistakes Wednesday, 19 June, 13

  31. Optimize Assets Wednesday, 19 June, 13

  32. Optimize Server Wednesday, 19 June, 13

  33. Wednesday, 19 June, 13

  34. Thin Unicorn Puma Wednesday, 19 June, 13

  35. Thin - Single Process Unicorn Puma Wednesday, 19 June, 13

  36. Thin Unicorn - Multi Process Puma Wednesday, 19 June, 13

  37. Thin Unicorn Puma - Multi Threaded Wednesday, 19 June, 13

  38. http://ylan.segal-family.com/blog/2012/08/20/better-performance-on-heroku-thins-vs-unicorn-vs-puma/ Wednesday, 19 June, 13

  39. Wednesday, 19 June, 13

  40. Wednesday, 19 June, 13

  41. Optimize Code Wednesday, 19 June, 13

  42. Offline Tasks Wednesday, 19 June, 13

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

    4 Notifier.delay.signup(@user) Wednesday, 19 June, 13
  44. Reduce Queries Wednesday, 19 June, 13

  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
  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
  47. 1 <% @cakes.each do |cake| %> 2 <p><%= cake.category.name %></p>

    3 <% end %> Wednesday, 19 June, 13
  48. 1 class CakesController < ApplicationController 2 def index 3 @cakes

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

    = Cake.all(include: :category) 4 end 5 end Wednesday, 19 June, 13
  50. bullet gem Wednesday, 19 June, 13

  51. Wednesday, 19 June, 13

  52. Indexes Wednesday, 19 June, 13

  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
  54. Wednesday, 19 June, 13

  55. Wednesday, 19 June, 13

  56. loldba gem Wednesday, 19 June, 13

  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
  58. Caching Wednesday, 19 June, 13

  59. If you must Wednesday, 19 June, 13

  60. Caching Strategies Wednesday, 19 June, 13

  61. Page Caching Wednesday, 19 June, 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
  63. Action Caching Wednesday, 19 June, 13

  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
  65. Sweepers Suck Wednesday, 19 June, 13

  66. Fragment Caching Wednesday, 19 June, 13

  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
  68. Russian Doll Caching Wednesday, 19 June, 13

  69. Model Caching Wednesday, 19 June, 13

  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
  71. Cache Digests Wednesday, 19 June, 13

  72. Tools Wednesday, 19 June, 13

  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
  74. Questions Wednesday, 19 June, 13